/*static char *SCCSID = "@(#)except.c   6.18 92/01/23";*/
#define SCCSID   "@(#)except.c  6.18 92/01/23"


/*
 *      Exceptions - Kernel Thread Exception Routines
 *
 *      This is an OS/2 2.x specific file
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *      Copyright (c) IBM Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Re-Created: April 1990 - Kyle Peltonen
 *
 *      This file provides the kernel routines to build 32 bit
 *      exception stacks, enter back into the kernel from the ring 3
 *      dispatcher to deliver 16 bit exceptions (when necessary),
 *      take the appropriate default actions when an exception is not
 *      handled, and send guard page exceptions.
 */


#define INCL_TYPES                      // for basedef.h typedefs

#include <basemac.h>
#include <basedef.h>
#define FNO_NUM FNO_EXCEPT_C
#include <fnotask.h>
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <infoseg.h>
#include <mi.h>
#include <dossymc.h>
#include <proc.h>
#include <error.h>
#include <sigdef.h>
#include <except.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <sched.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <tkptda.h>
#include <kmode.h>
#include <selxport.h>
#include <bsexcpt.h>
#include <bsetib.h>
#include <ldrxport.h>
#include <tklibi.h>
#include <tklocal.h>
#include <ordinals.h>

#ifdef INCDEBUG
#include <dbgapi.h>
#include <dbgex.h>
#include <dbgin.h>
#endif /* DEBUG */

#include <exmg.h>
#include <sigxport.h>

extern ULONG PLD32ApiDump (ULONG Pid, ULONG ulTrapNumber);   /* D26150     */

/*
 * This is a special code whose bits define which traps can be delivered
 * to 16-bit handlers. To check whether TrapNumber can be delivered, use
 * the logical result from (1L << TrapNumber) & EXCEPT16.
 */
#define EXCEPT16            ((1L << EXCDIVZ ) | \
                             (1L << EXCOV   ) | \
                             (1L << EXCBOUND) | \
                             (1L << EXCOP   ) | \
                             (1L << EXCFPERR))

#pragma code_seg("SWAP32_10", "CODE")

/***LP  xcpt2Trap - Translate an exception number to a trap number.
 *
 *      xcpt2Trap translates exception numbers to the equivalent
 *      16-bit trap (if any).
 *
 *      ENTRY   ulExceptionNum - Exception number
 *              pulTrapNum     - pointer to store 16-bit trap number.
 *
 *      EXIT    Success - TRUE  --> 16-bit trap number stored in pulTrapNum
 *              Failure - FALSE --> No 16-bit equivalent
 */

ULONG xcpt2Trap(ULONG ulExceptionNum, PULONG pulTrapNum)
{
     switch (ulExceptionNum) {
        case XCPT_INTEGER_DIVIDE_BY_ZERO:
           *pulTrapNum = EXCDIVZ;
           break;
        case XCPT_INTEGER_OVERFLOW:
           *pulTrapNum = EXCOV;
           break;
        case XCPT_ARRAY_BOUNDS_EXCEEDED:
           *pulTrapNum = EXCBOUND;
           break;
        case XCPT_FLOAT_DENORMAL_OPERAND:
        case XCPT_FLOAT_DIVIDE_BY_ZERO:
        case XCPT_FLOAT_INEXACT_RESULT:
        case XCPT_FLOAT_INVALID_OPERATION:
        case XCPT_FLOAT_OVERFLOW:
        case XCPT_FLOAT_STACK_CHECK:
        case XCPT_FLOAT_UNDERFLOW:
           *pulTrapNum = EXCFPERR;
           break;
        case XCPT_ILLEGAL_INSTRUCTION:
           *pulTrapNum = EXCOP;
           break;
        default:
           return(FALSE);
     }
}

/***EP  XCPTBuildR3DispatcherStack - Setup for exception
 *
 *      XCPTBuildR3DispatcherStack is called from kernel components that
 *      need to dispatch exceptions.
 *
 *      Multiple exceptions may need to be delivered before return
 *      to ring 3. For example, a thread causes a divide by zero
 *      exception, XcptBuildR3DispatcherStack then writes the exception
 *      context to the R3 stack which causes a guard page exception.
 *      Thus, two exceptions need to be delivered.  The order of delivery
 *      is in reverse cronological order.
 *
 *      Upon completion of delivery, the R3 dispatcher returns to the
 *      kernel. If the exception was successfully handled, then the kernel
 *      restores the exception context. If not, the appropriate default
 *      action is taken.
 *
 *      XCPTBuildR3DispatcherStack may or may not alter the ring 3 stack.
 *      It first calls XCPTCheckExceptionHandler to make sure that there
 *      are 32-bit handlers for this thread.  If there aren't any and the
 *      exception is neither XCPT_GUARD_PAGE_VIOLATION nor XCPT_FLOAT_ and
 *      the process is not being debugged, it passes control to directly to
 *      xcptExceptionCallBack (the default action for guard page faults is
 *      handled at ring 3, while the floating point emulator and debugger
 *      require the presence of the ring 3 information).  Otherwise, it
 *      alters the thread's stacks as shown below, and, upon exiting the
 *      kernel, control will pass to the R3 Exception Dispatcher.
 *
 *      Upon entry, the user's stack looks like:
 *
 *                      +---------------+
 *                      |               |
 *                      .   Program's   .
 *                      .     Stack     .
 *
 *                      |               |<------+
 *                      +---------------+       |
 *                                              |
 *      And the kernel stack looks like:        |
 *                                              |
 *                      +---------------+       |
 *                      |    User SS    |--+    |
 *                      +---------------+  +----+
 *                      |    User SP    |--+
 *                      +---------------+
 *                      |               |
 *                      .               .
 *                      .   User args   .
 *                          (if any)
 *                      |               |
 *                      +---------------+
 *                      |    User CS    |
 *                      +---------------+
 *                      |    User IP    |
 *                      +---------------+
 *                      |   User flags  |
 *                      +---------------+
 *                      |               |
 *                      .               .
 *                      .   User Regs   .
 *
 *                      |               |
 *                      +---------------+
 *
 *      XcptBuildR3DispatcherStack makes them look like:
 *
 *                      +---------------+
 *                      |               |
 *                      .   Program's   .
 *                      .     Stack     .
 *
 *                      |               |
 *                      +---------------+
 *                      |               |
 *                      |   128 bytes   |
 *                      |               |
 *                      +---------------+
 *                      |               |
 *                      .   Several     .
 *                      .   Exception   .
 *                      .   Frames if   .
 *                      .   R0 Nesting  .
 *
 *                      |               |
 *              +------ +---------------+
 *              |   +-> | Context Rec.  |
 *              |   |   +---------------+
 *              |   |   | Report Rec.   |
 *  Ring 3      |   |   +---------------+ <-----+
 *  Exception <-+   +-- | pContext      |       |
 *  Frame       |       +---------------+       |
 *              |       | pERepRec      | ------+
 *              |       +---------------+
 *              |       | Trap Number   |
 *              |       +---------------+
 *              |       |       0       |<------+
 *              +------ +---------------+       |
 *                                              |
 *      and                                     |
 *                                              |
 *                      +---------------+       |
 *                      |    User SS    |--+    |
 *                      +---------------+  +----+
 *                      |    User SP    |--+
 *                      +---------------+
 *                      |               |
 *                      .               .
 *                      .   User args   .
 *                          (if any)
 *                      |               |
 *                      +---------------+
 *                      | Dispatcher CS |
 *                      +---------------+
 *                      | Dispatcher IP |
 *                      +---------------+
 *                      |   User flags  |
 *                      +---------------+
 *                      |               |
 *                      .               .
 *                      .   User Regs   .
 *
 *                      |               |
 *                      +---------------+
 *
 *      ENTRY   ulTrapNumber     - Trap number
 *              ulExceptionNum   - Exception number
 *              cExceptionInfo   - Count of exception info parameters
 *              pulExceptionInfo - Array of exception info parameters
 *              fNonContinuable  - TRUE --> Non continuable exception
 *              pSeconRepRec     - Additional nested report record (optional)
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_PROTECTION_VIOLATION
 */

ULONG XCPTBuildR3DispatcherStack(ULONG ulTrapNumber,
                                 ULONG ulExceptionNum,
                                 ULONG cExceptionInfo,
                                 PULONG pulExceptionInfo,
                                 ULONG fNonContinuable,
                                 PULONG pSecondRepRec)
{
    ULONG   rc;

#ifdef INCDEBUG
    ULONG   dc;                     /* DbgException rc */
#endif /* DEBUG */

    KMREG   XcptContext;            /* exception context */

    R3XCPTSTACK XcptStack;          /* R3 stack image */
    PCHAR   pSSBackP;               /* SS:ESP Backpointer */
    USHORT  usBackPSS;              /* Backpointer Temporary storage */
    USHORT  usBackPSP;
    ULONG   TLMA0;
    ULONG   SendXcptToDbger = TRUE;

    pTCBCur->TCBMiscFlags |= TMF_MULT_XCPT;

    /*
     * Save trap number corresponding to exception (user code is not aware
     * of the existence of this).
     */
    XcptStack.TrapNumber = ulTrapNumber;

    /*
     * Initialize ContextFlags to NULL
     */
    XcptStack.Context.ContextFlags = 0;

    /*
     * Get user registers when exception occurred.
     */
    if (((rc = KMQueryAllClientRegisters (pTCBCur,
            SSToDS(&XcptContext))) != NO_ERROR) ||
        ((rc = KMQueryClientReturnESP (pTCBCur,
            SSToDS(&XcptContext.kmreg_esp))) != NO_ERROR)) {
        return (rc);
    }

    /*
     * And now the Exception Report Record...
     */

    XcptStack.ERepRec.fHandlerFlags = fNonContinuable ? EH_NONCONTINUABLE : 0;
    XcptStack.ERepRec.ExceptionNum = ulExceptionNum;

#ifdef SECURITY
    if (pPTDACur->SigFTerm == (USHORT)-1)                                //@SEC
       XcptStack.ERepRec.fHandlerFlags |= EH_SIGFTERM;                   //@SEC
#endif                                                                   //@SEC

    if ((pSecondRepRec != NULL) && (ulExceptionNum == XCPT_PROCESS_TERMINATE)) {
       XcptStack.ERepRec.fHandlerFlags |= EH_NESTED_CALL;
    }

    /*
     * The Exception Address is always linearized, even if the exception
     * occurred in flat code or in V86 mode.
     */
    if (XcptContext.kmreg_cs == R3CS) {
       XcptStack.ERepRec.ExceptionAddress = (PVOID)XcptContext.kmreg_eip;
    } else if (XcptContext.kmreg_eflag & F_VM) {
       XcptStack.ERepRec.ExceptionAddress = (PVOID)((XcptContext.kmreg_cs << 4)
           + XcptContext.kmreg_eip);
    } else {
       (ULONG)XcptStack.ERepRec.ExceptionAddress = SELVirtToLin(XcptContext.kmreg_eip,
           (SEL)XcptContext.kmreg_cs);
    }
    XcptStack.ERepRec.cParameters = cExceptionInfo;
    for (; cExceptionInfo != 0; cExceptionInfo--) {
       XcptStack.ERepRec.ExceptionInfo[cExceptionInfo-1] =
          pulExceptionInfo[cExceptionInfo-1];
    }

    /*
     * The debugger got pre-first chance at XCPT_SINGLE_STEP or
     * XCPT_BREAKPOINT exception in DbgTrap().
     */

    /*
     * If this is a floating point error, then get the state of
     * the x87. Since this procedure is called from the kernel we
     * know there really is an x87. The emulator uses DosRaiseException
     * and can't call this routine.
     */

    if ((ulExceptionNum >= XCPT_FLOAT_DENORMAL_OPERAND) &&
        (ulExceptionNum <= XCPT_FLOAT_UNDERFLOW)) {

       xcptAssert((XCPT_FLOAT_DENORMAL_OPERAND == 0xC0000094) &&
                  (XCPT_FLOAT_DIVIDE_BY_ZERO == 0xC0000095) &&
                  (XCPT_FLOAT_INEXACT_RESULT == 0xC0000096) &&
                  (XCPT_FLOAT_INVALID_OPERATION == 0xC0000097) &&
                  (XCPT_FLOAT_OVERFLOW == 0xC0000098) &&
                  (XCPT_FLOAT_STACK_CHECK == 0xC0000099) &&
                  (XCPT_FLOAT_UNDERFLOW == 0xC000009A));

       xcptQueryFPState((PFPSTATE)SSToDS(&XcptStack.Context.ctx_env[0]));
       XcptStack.Context.ContextFlags |= CONTEXT_FLOATING_POINT;
    }

    /*
     * Build an image of the ring 3 exception stack.
     *
     * This includes the exception context, the exception structure,
     * and the ring 3 exception dispatcher arguments.
     */

    /*
     * Build the context record
     */
    XcptStack.Context.ContextFlags |= CONTEXT_CONTROL | CONTEXT_INTEGER |
        CONTEXT_SEGMENTS;

    /*
     * CONTEXT_SEGMENT
     */
    XcptStack.Context.ctx_SegGs = XcptContext.kmreg_gs;
    XcptStack.Context.ctx_SegFs = XcptContext.kmreg_fs;
    XcptStack.Context.ctx_SegEs = XcptContext.kmreg_es;
    XcptStack.Context.ctx_SegDs = XcptContext.kmreg_ds;

    /*
     * CONTEXT_INTEGER
     */
    XcptStack.Context.ctx_RegEdi = XcptContext.kmreg_edi;   /* exception context */
    XcptStack.Context.ctx_RegEsi = XcptContext.kmreg_esi;

    //
    // BUGBUG: EAX Should be set to ERROR_INTERRUPT if the exception
    //         is XCPT_SIGNAL and the thread was in kernel mode.
    //

    XcptStack.Context.ctx_RegEax = XcptContext.kmreg_eax;
    XcptStack.Context.ctx_RegEbx = XcptContext.kmreg_ebx;
    XcptStack.Context.ctx_RegEcx = XcptContext.kmreg_ecx;
    XcptStack.Context.ctx_RegEdx = XcptContext.kmreg_edx;


    /*
     * CONTEXT_CONTROL
     */
    XcptStack.Context.ctx_RegEbp = XcptContext.kmreg_ebp;
    XcptStack.Context.ctx_RegEip = XcptContext.kmreg_eip;
    XcptStack.Context.ctx_SegCs = XcptContext.kmreg_cs;
    XcptStack.Context.ctx_EFlags = XcptContext.kmreg_eflag;
    XcptStack.Context.ctx_RegEsp = XcptContext.kmreg_esp;  /* Return ESP */
    XcptStack.Context.ctx_SegSs = XcptContext.kmreg_ss;


    /*
     * V86 mode processes don't get exceptions. Just blast the harderror
     * message...
     */
    if (XcptContext.kmreg_eflag & F_VM) {
       xcptFormatAndSendHardErr(ulTrapNumber,
                                SSToDS(&(XcptStack.ERepRec)),
                                SSToDS(&(XcptStack.Context)));
       return(NO_ERROR);
    }

    /*
     * If we were on a stack other than a Ring 3 stack, we need to switch
     * to the Ring 3 stack to run EH's. We do this by finding the backpointer
     * to the Ring n+m stack until we're at Ring 3.
     */

    if ((XcptContext.kmreg_ss & SEL_RPLMASK) == SEL_RPL2) {
       if((pSSBackP = (PCHAR)SELVirtToLin(pTCBCur->TCBCpl2_ESP - 4,
           pTCBCur->TCBCpl2_SS)) == (PCHAR)(-1)) {
          xcptFatalDispatchError(EXCSW,
                                 SSToDS(&XcptStack.ERepRec),
                                 SSToDS(&XcptStack.Context));
          return(ERROR_PROTECTION_VIOLATION);
       }
       if (((rc = TKFuWordNF(SSToDS(&usBackPSS),pSSBackP+2)) != NO_ERROR) ||
           ((rc = TKFuWordNF(SSToDS(&usBackPSP),pSSBackP)) != NO_ERROR)) {
          xcptFatalDispatchError(EXCSW,
                                 SSToDS(&XcptStack.ERepRec),
                                 SSToDS(&XcptStack.Context));
          return(rc);
       }

       if ((rc = KMSetClientRegister(pTCBCur, KM_REG_SS, (ULONG)usBackPSS)) ||
           (rc = KMSetClientRegister(pTCBCur, KM_REG_ESP, (ULONG)usBackPSP))
           != NO_ERROR) {
          xcptFatalDispatchError(EXCSW,
                                 SSToDS(&XcptStack.ERepRec),
                                 SSToDS(&XcptStack.Context));
          return(rc);
       }

    }

#ifdef INCDEBUG
    if (pPTDACur->ptda_DbgEnabled) {

       /* If we take an ACCESS_VIOLATION, or GUARD_PAGE exception,
        * when a process is under the control of DosDebug, we must
        * check to see if the least significant bit of the first
        * TLMA variable (used for PM/Kernel communication) is set.
        * If it is, this means that PM will handle the exception,
        * therefore, we should not notify the debugger.
        *
        * This is used in the pointer verification functions of the
        * PM subsystem. If they take an exception they abort the
        * PM call, and return back an error.
        */

       if ((ulExceptionNum == XCPT_ACCESS_VIOLATION) ||
           (ulExceptionNum == XCPT_GUARD_PAGE_VIOLATION)) {

           TKFuDWord(SSToDS(&TLMA0),(SEL_TASKINFOLADDR + LIS_LEN));
           if (TLMA0 & 1)
              SendXcptToDbger = FALSE;
       }
    }
#endif

    /* Get the flat stack return address to get pExceptionContext */

    if ((rc = KMQueryReturnESPLaddr(pTCBCur,
        (PPVOID) SSToDS(&XcptStack.pContext))) != NO_ERROR) {

#ifdef INCDEBUG
       if (pPTDACur->ptda_DbgEnabled) {
          if (SendXcptToDbger) {
             if ((dc=DbgException(SSToDS(&(XcptStack.ERepRec)), NULL,
                              SSToDS(&(XcptStack.Context)), NULL,
                              DBG_X_STACK_INVALID)) != XCPT_CONTINUE_SEARCH) {

                xcptAssert((dc == XCPT_CONTINUE_EXECUTION) ||
                           (dc == XCPT_CONTINUE_STOP));
                xcptExceptionCallBack(EXCSW, NULL, NULL, SSToDS(&XcptStack.pERepRec),
                                      SSToDS(&XcptStack.pContext), dc);
                return(NO_ERROR);
             }
          }
       }
#endif /* DEBUG */

       if ((pSecondRepRec == NULL) ||
           !(((PEXCEPTIONREPORTRECORD)pSecondRepRec)->fHandlerFlags & EH_STACK_INVALID)) {
          xcptStackOverRun(EXCSW,
                           SSToDS(&XcptStack.ERepRec),
                           SSToDS(&XcptStack.Context));
       }
       return rc;
    }

    /*
     * The context record is the first structure pushed on the stack.
     * It is followed by the exception report record.
     */
    (ULONG)XcptStack.pContext -= sizeof(CONTEXTRECORD) + LOCALVARSPACE;
    (ULONG)XcptStack.pERepRec = (ULONG)XcptStack.pContext -
                                 sizeof(EXCEPTIONREPORTRECORD);

    /*
     * Adjust the exception report record's nested report record pointer.
     * If a second report record was passed in to this routine, place it
     * in the proper field.  Otherwise place the pointer saved in the TCB
     * in the proper field.  In either case, update the pointer saved in
     * the TCB to reference the current exception report record.
     */
    if (pSecondRepRec == NULL) {
       XcptStack.ERepRec.NestedExceptionReportRecord = (PEXCEPTIONREPORTRECORD) pTCBCur->TCBXcptRepRec;
    } else {
       XcptStack.ERepRec.NestedExceptionReportRecord = (PEXCEPTIONREPORTRECORD) pSecondRepRec;
    }
    pTCBCur->TCBXcptRepRec = (PVOID) XcptStack.pERepRec;

    /*
     * We will never just return dispatch
     */
    XcptStack.ReturnAddr = 0;

#ifdef INCDEBUG
    /*
     * Make sure that there are 32-bit handlers for this thread or that
     * the process is being debugged before altering the contents of the
     * user stack.  However, if the exception is XCPT_GUARD_PAGE_VIOLATION,
     * then pass control to ring 3 regardless because the default action for
     * that exception is handled up there.  Also, if the exception is a
     * floating point exception, (XCPT_FLOAT_) then pass control to ring 3
     * regardless because DOS32R3FRESTOR requires it due to the emulator.
     */

    if (!pPTDACur->ptda_DbgEnabled &&
        ulExceptionNum != XCPT_GUARD_PAGE_VIOLATION &&
        !(XcptStack.Context.ContextFlags & CONTEXT_FLOATING_POINT) &&
        XCPTCheckExceptionHandler(pTCBCur) == FALSE) {

       xcptExceptionCallBack(ulTrapNumber, NULL, NULL, SSToDS(&XcptStack.ERepRec),
                             SSToDS(&XcptStack.Context), XCPT_CONTINUE_SEARCH);
       return(NO_ERROR);
    }

    if (SendXcptToDbger) {

       /* 024751
        * If the process is being debugged and the debugger is using
        * DosPtrace don't alter the user's context.
        */
       if ((pPTDACur->ptda_DbgEnabled) &&
           (pPTDACur->ptda_flDbg & DBGP_PTRACE)) {

          if ((dc=DbgException(SSToDS(&(XcptStack.ERepRec)), XcptStack.pERepRec,
               SSToDS(&(XcptStack.Context)), XcptStack.pContext,
               DBG_X_FIRST_CHANCE)) != XCPT_CONTINUE_SEARCH) {

             xcptAssert(dc == XCPT_CONTINUE_STOP);
             xcptExceptionCallBack(EXCSW, NULL, NULL,
                                   SSToDS(&XcptStack.ERepRec),
                                   SSToDS(&XcptStack.Context), dc);
             return(NO_ERROR);
          }
       }
    }
#endif /* DEBUG */

    /*
     * Adjust client's ESP to step over local variables optimization area
     * and then push exception stack image onto the ring 3 stack
     */
    if (((rc = KMSetClientReturnESP(pTCBCur,
          XcptContext.kmreg_esp - LOCALVARSPACE)) != NO_ERROR) ||
        (rc = KMPushClientStack(pTCBCur, sizeof(XcptStack),
         SSToDS(&XcptStack), 0)) != NO_ERROR) {

#ifdef INCDEBUG
       if (SendXcptToDbger) {
          if (pPTDACur->ptda_DbgEnabled) {
             if ((dc=DbgException(SSToDS(&(XcptStack.ERepRec)), NULL,
                              SSToDS(&(XcptStack.Context)), NULL,
                              DBG_X_STACK_INVALID)) != XCPT_CONTINUE_SEARCH) {

                xcptAssert((dc == XCPT_CONTINUE_EXECUTION) ||
                           (dc == XCPT_CONTINUE_STOP));
                xcptExceptionCallBack(EXCSW, NULL, NULL, SSToDS(&(XcptStack.pERepRec)),
                                      SSToDS(&(XcptStack.pContext)), dc);
                return(NO_ERROR);
             }
          }
       }
#endif /* DEBUG */

       if ((pSecondRepRec == NULL) ||
           !(((PEXCEPTIONREPORTRECORD)pSecondRepRec)->fHandlerFlags & EH_STACK_INVALID)) {
          xcptStackOverRun(EXCSW,
                           SSToDS(&XcptStack.ERepRec),
                           SSToDS(&XcptStack.Context));
       }
       return rc;

    } else {

       /*
        * Modify R3 return addrress to return to ring 3 exception dispatcher
        * in DOSCALL1.
        */
       KMSetFlatDispatch (pTCBCur, pfnXcptDispatcher32);
    }

#ifdef INCDEBUG
    /*
     * The debugger gets first chance at all exceptions.
     */
    if (SendXcptToDbger) {
       if ((pPTDACur->ptda_DbgEnabled) &&
          !(pPTDACur->ptda_flDbg & DBGP_PTRACE)) {                   // 024751
          if ((dc=DbgException(SSToDS(&(XcptStack.ERepRec)), XcptStack.pERepRec,
                           SSToDS(&(XcptStack.Context)), XcptStack.pContext,
                           DBG_X_FIRST_CHANCE)) != XCPT_CONTINUE_SEARCH) {

             xcptAssert((dc == XCPT_CONTINUE_EXECUTION) ||
                        (dc == XCPT_CONTINUE_STOP));
             xcptExceptionCallBack(EXCSW, XcptStack.pERepRec, XcptStack.pContext,
                                   SSToDS(&(XcptStack.pERepRec)),
                                   SSToDS(&(XcptStack.pContext)), dc);
             return(NO_ERROR);
          }
       }
    }
#endif /* DEBUG */

    /*
     * Turn off the F_TRACE bit in the client's EFLAGS register if exception
     * is the single step exception so that the first instruction in
     * Dos32R3ExceptionDispatcher won't generate another single step trap.
     * This needs to be done AFTER the debugger, if present, has been given
     * a chance to handle the single step exception.
     */
    if (ulExceptionNum == XCPT_SINGLE_STEP) {
       if (KMSetClientRegister(pTCBCur, KM_REG_EFLAG,
                               (XcptContext.kmreg_eflag & ~F_TRACE))) {
          xcptFatalDispatchError(EXCSW,
                                 SSToDS(&XcptStack.ERepRec),
                                 SSToDS(&XcptStack.Context));
       }
    }

    return rc;
}


#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***EP  XCPTCheckExceptionHandler - See if exception handlers exist.
 *
 *      XCPTCheckExceptionHandler verifies the presence of an
 *      exception handler in the thread whose TCB is passed in.
 *
 *      ENTRY   pTCB    - Linear address of TCB
 *
 *      EXIT    Success - TRUE  --> There is an exception handler
 *                        FALsE --> Could not find exception handler
 *                                  or received error looking for it.
 *
 *      PSUEDO CODE
 *
 *      Create alias to TIB
 *      Lock the alias
 *      look for an exception handler in tib_pexecchain
 *      free alias to TIB
 */

ULONG XCPTCheckExceptionHandler(PTCB pTCB)
{
   VMAC vmac;                             /* VM access structure */
   PEXCEPTIONREGISTRATIONRECORD pExChain; /* Local space for TIB field */
   ULONG rc = FALSE;                      /* return code */
   register ULONG ulTib;                  /* Address of TIB */

   ulTib = (ULONG)pTCB->TCBptib;
   if (((LONG)ulTib != -1) &&                   /* If there is a TIB */
       !(pTCB->TCBMiscFlags & TMF_NO_EXCEPT)) { /* and it's valid. */

      if (VMMapAlias(VMAC_ARENASYS|PG_R|VMAC_LOCANY, /* system arena,readable */
              0,                               /* no SELAlloc flags */
              0,                               /* no alias flags */
              0,                               /* don't map specific alias */
              0,                               /* valid in current context */
              ulTib & ~PAGEMASK,               /* T1 TIB */
              pTCB->TCBpPTDA->ptda_handle,     /* Handle to correct context */
              ((((ulTib & PAGEMASK) + sizeof(struct tibp_s) & ~PAGEMASK) > 0) ?
              2 * PAGESIZE : PAGESIZE),        /* Min alias size */
              SSToDS(&vmac)) != NO_ERROR)      /* return info buffer */
         return (rc);                          /* Can't check, assume NO */

       if (TKFuDWordNF(SSToDS(&pExChain),
           &(((tib_t *)(vmac.ac_va + (ulTib & PAGEMASK)))->tib_pexchain))
                   == NO_ERROR) {
         rc = (pExChain != END_OF_CHAIN);
      }
      VMFreeMem(vmac.ac_va,0,0);        /* free the alias */
   }
   return rc;
}
#pragma code_seg()
#pragma code_seg("EXMGSWAP_TEXT", "CODE")

/***EP  VR32IRaiseException - Kernel entry for DosRaiseException
 *
 *      VR32IRaiseException is called to give the debugger first
 *      chance at raising an exception.
 *
 *      ENTRY   sef      System Entry Frame
 *              pERepRec Pointer to Exception Report Record
 *              pContext Pointer to Exception Context Record
 *
 *      EXIT    Success - Return address modified to point to
 *                        exception dispatcher.
 */

ULONG cdecl VR32IRaiseException(THUNKSEF sef,
                                PEXCEPTIONREPORTRECORD pERepRec,
                                PCONTEXTRECORD pContext)
{
   EXCEPTIONREPORTRECORD R0ERepRec;
   CONTEXTRECORD R0Context;
   ULONG rc;

#ifdef INCDEBUG
   ULONG dc;
#endif /* DEBUG */

   struct _RAISESTACK {
      PVOID ReturnAddr;                    /* return address (place holder) */
      ULONG TrapNumber;                    /* Exception Dispatcher Argument */
      PEXCEPTIONREPORTRECORD pERepRec;     /* Exception Dispatcher Argument */
      PCONTEXTRECORD pContext;             /* Exception Dispatcher Argument */
   } R3Stack;


   if (xcptCopyinXcptStruct(pERepRec, pContext, SSToDS(&R0ERepRec),
       SSToDS(&R0Context)) != NO_ERROR) {
      return(ERROR_PROTECTION_VIOLATION);
   }

#if INCDEBUG
   if (pPTDACur->ptda_DbgEnabled) {

      if ((dc=DbgException(SSToDS(&R0ERepRec), pERepRec, SSToDS(&R0Context),
          pContext,DBG_X_FIRST_CHANCE)) != XCPT_CONTINUE_SEARCH) {

         xcptAssert((dc == XCPT_CONTINUE_EXECUTION) ||
                    (dc == XCPT_CONTINUE_STOP));
         xcptExceptionCallBack(EXCSW, pERepRec, pContext, SSToDS(&R0ERepRec),
                               SSToDS(&R0Context), dc);
         return(NO_ERROR);
      }
   }
#endif /* RAS */

   R3Stack.TrapNumber = EXCSW;

   if (!(R0ERepRec.fHandlerFlags & EH_STACK_INVALID)) {
      R3Stack.pERepRec = pERepRec;
      R3Stack.pContext = pContext;

      if ((rc = KMPushClientStack(pTCBCur, sizeof(struct _RAISESTACK),
         SSToDS(&R3Stack), 0)) != NO_ERROR) {

         xcptStackOverRun(EXCSW,
                          SSToDS(&R0ERepRec),
                          SSToDS(&R0Context));
         return rc;

      } else {

         /*
          * Modify R3 return addrress to return to ring 3 exception dispatcher
          * in DOSCALL1.
          */
         KMSetFlatDispatch (pTCBCur, pfnXcptDispatcher32);
      }
   } else {
      xcptExceptionCallBack(EXCSW, pERepRec, pContext, SSToDS(&R0ERepRec),
                            SSToDS(&R0Context), XCPT_CONTINUE_SEARCH);
   }

   return (NO_ERROR);

}

#pragma code_seg()
#pragma code_seg("SWAP32_10", "CODE")
/***EP   VR32ExceptionCallBack - Ring 3 exception dispatcher callback
 *
 *       VR32ExceptionCallBack is the callback into the kernel from
 *       the ring 3 dispatcher.
 *
 *       ENTRY   sef          - System entry frame
 *               ulTrapNumber - Trap number
 *               pERepRec     - Pointer to user space exception report record
 *               pContext     - Pointer to user space context record.
 *               ulDispostion - Dispostion (CONTINUE SEARCH/EXECUTION)
 *
 *       EXIT    Success - EAX from context record (to restore on kernel exit)
 *               Failure - Generally doesn't exit. Raises termination exception.
 */

ULONG cdecl VR32ExceptionCallBack(THUNKSEF sef,
                                  ULONG ulTrapNumber,
                                  PEXCEPTIONREPORTRECORD pERepRec,
                                  PCONTEXTRECORD pContext,
                                  ULONG ulDisposition)
{
   EXCEPTIONREPORTRECORD R0ERepRec;
   CONTEXTRECORD R0Context;


   if (xcptCopyinXcptStruct(pERepRec, pContext, SSToDS(&R0ERepRec),
       SSToDS(&R0Context)) != NO_ERROR) {

      return(ERROR_PROTECTION_VIOLATION);
   }

   /*
    * The return from xcptExceptionCallBack is the user's EAX, which
    * is not restored by KMExitKmodeCallGate
    */

   return(xcptExceptionCallBack(ulTrapNumber, pERepRec, pContext, SSToDS(&R0ERepRec),
       SSToDS(&R0Context), ulDisposition));
}

/***LP   xcptExceptionCallBack - Worker routine for exception callback
 *
 *       xcptExceptionCallBack is the  worker routine for exception
 *       callback after an exception has been dispatched. This routine
 *       will either restore the user's context from the context record
 *       (XCPT_CONTINUE_EXECUTION) or take the appropriate default action
 *       (XCPT_CONTINUE_SEARCH).
 *
 *       ENTRY   ulTrapNumber  - Trap number
 *               pERepRec      - Pointer to user space exception report record
 *               pContext      - Pointer to user space context record
 *               pR0ERepRec    - Pointer to kernel space exception report record
 *               pR0Context    - Pointer to kernel space context record
 *               ulDisposition - Disposition
 *
 *       EXIT    Success - User's EAX from context record
 */

ULONG xcptExceptionCallBack(ULONG ulTrapNumber,
                           PEXCEPTIONREPORTRECORD pERepRec,
                           PCONTEXTRECORD pContext,
                           PEXCEPTIONREPORTRECORD pR0ERepRec,
                           PCONTEXTRECORD pR0Context,
                           ULONG ulDisposition)
{
   ULONG ulContextFP;

   /*
    * If this is an EXIT_UNWIND callback, kill the thread.
    */
   if (ulDisposition == EXIT_UNWIND) {

      pTCBCur->TCBForcedActions |= TK_FF_EXIT;
      pTCBCur->TCBMiscFlags |= TMF_EXIT_TERM;

      return (NO_ERROR);
   }

   /*
    * If this is a signal let the signal subsystem process it first.
    * Handled signals are acknowledged and unhandled signals are
    * re-posted.
    */
   if (pR0ERepRec->ExceptionNum == XCPT_SIGNAL) {
      SigReset(pR0ERepRec->ExceptionInfo[0], ulDisposition);
   }
   if (pR0ERepRec->ExceptionNum == XCPT_ASYNC_PROCESS_TERMINATE) {
      SigReset(SIGAPTERM, ulDisposition);
   }

   /*
    * Update the pointer saved in the TCB to reference the previous
    * exception's exception report record, if any.
    */
   pTCBCur->TCBXcptRepRec = (PVOID) pR0ERepRec->NestedExceptionReportRecord;

   /*
    * If the exception was not handled, then default action processing
    * is needed.
    */

   if (ulDisposition == XCPT_CONTINUE_SEARCH) {

      /*
       * Change a software generated exception to reflect the corresponding
       * hardware trap if possible.
       */
      if (ulTrapNumber == EXCSW) {
         if (!xcpt2Trap(pR0ERepRec->ExceptionNum, SSToDS(&ulTrapNumber)))
            ulTrapNumber = EXCSW;
      }

      /*
       * If offending code is 16-bit and a 16-bit handler is registered
       * for the exception, deliver exception to 16-bit handler.
       * Otherwise, take default action for the exception.
       */
      if (((pR0Context->ctx_SegCs & TABLE_MASK) == 4) &&
          (((1L << ulTrapNumber) & EXCEPT16) != 0) &&
          (TRAPCheckExcept16Handler(ulTrapNumber) != 0)) {
         /*
          * If the FP context is to be restored via a piece of R3
          * trampoline code, we need to set up the trap return
          * *first*. Therefore we'll delay 'restoring' the FP
          * context until after arming the trap handler.
          */
         ulContextFP = pR0Context->ContextFlags & CONTEXT_FLOATING_POINT;
         pR0Context->ContextFlags &= ~CONTEXT_FLOATING_POINT;
         xcptRestoreXcptContext(pR0Context);
         (VOID) TRAPArmExcept16Handler(ulTrapNumber);

         /*
          * If we are arming the Processor_Extension_Error handler then
          * the FP status word needs to be pushed on the stack.
          */

         if (ulTrapNumber == EXCFPERR) {
            KMPushClientStack(pTCBCur, 2, &(pR0Context->ctx_env[1]), 0);
         }
         if (ulContextFP) {
            pR0Context->ContextFlags = ulContextFP;
            pR0Context->ctx_env[0] |= 0x3F;          /* Mask FP Exceptions */
            xcptRestoreXcptContext(pR0Context);
         }
         return (pR0Context->ctx_RegEax);

      } else {

         /*
          * Take Default Action to this exception
          *
          * This happens when the faulting code was 32 bit and 32 bit
          * handler(s) did not handle the exception or when the faulting
          * code is 16 bit and there is not 16 bit handler registered
          * for the exception.
          */
         return(xcptDefaultAction(ulTrapNumber, pERepRec, pContext, pR0ERepRec, pR0Context));
      }

   } else if (ulDisposition == XCPT_CONTINUE_EXECUTION) {
      xcptRestoreXcptContext(pR0Context);
      return (pR0Context->ctx_RegEax);
   }

   return(NO_ERROR);

}

/***LP   xcptDefaultAction - Execute default action
 *
 *       xcptDefaultAction takes the appropriate default action for
 *       system exceptions. In general, this is to kill the process.
 *
 *       ENTRY   ulTrapNumber  - Trap number
 *               pERepRec      - Pointer to user space exception report record
 *               pContext      - Pointer to user space context record
 *               pR0ERepRec    - Pointer to kernel space exception report record
 *               pR0Context    - Pointer to kernel space context record
 *
 *       EXIT    none
 */

ULONG xcptDefaultAction(ULONG ulTrapNumber,
                       PEXCEPTIONREPORTRECORD pERepRec,
                       PCONTEXTRECORD pContext,
                       PEXCEPTIONREPORTRECORD pR0ERepRec,
                       PCONTEXTRECORD pR0Context)
{
    extern   CHAR     DumpProcess;                   /* D26150             */

#ifdef INCDEBUG
   /*
    * The debugger gets last chance at all exceptions.
    */
   if (pPTDACur->ptda_DbgEnabled) {
      if (DbgException(pR0ERepRec, pERepRec, pR0Context, pContext,
                       DBG_X_LAST_CHANCE) == XCPT_CONTINUE_EXECUTION) {

         return(xcptExceptionCallBack(EXCSW, pERepRec, pContext, pR0ERepRec, pR0Context,
                XCPT_CONTINUE_EXECUTION));
      }
   }

#endif /* DEBUG */

   /*
    * If the exception is not fatal and is continuable, then just restore
    * the context and continue running.
    */

   if (((pR0ERepRec->ExceptionNum & XCPT_SEVERITY_CODE) != XCPT_FATAL_EXCEPTION) &&
       (!(pR0ERepRec->fHandlerFlags & EH_NONCONTINUABLE))) {

      xcptRestoreXcptContext(pR0Context);
      return(pR0Context->ctx_RegEax);

   /*
    * An unhandled PTERM should cause a thread to die w/o running
    * exception handlers again.
    */

   } else if ((pR0ERepRec->ExceptionNum == XCPT_PROCESS_TERMINATE) ||
              (pR0ERepRec->fHandlerFlags & EH_STACK_INVALID)) {

      pTCBCur->TCBForcedActions |= TK_FF_EXIT;
      pTCBCur->TCBMiscFlags |= TMF_EXIT_TERM;

   /*
    * XCPT_ASYNC_PROCESS_TERMINATE and valid XCPT_SIGNALs are dealt
    * w/ in the signal subsystem, so no action is necessary here.
    */

   } else if ((pR0ERepRec->ExceptionNum == XCPT_ASYNC_PROCESS_TERMINATE) ||
              ((pR0ERepRec->ExceptionNum == XCPT_SIGNAL) &&
               (pR0ERepRec->ExceptionInfo[0] >= 1) &&
               (pR0ERepRec->ExceptionInfo[0] <= 32) &&
               (pR0ERepRec->ExceptionInfo[0] & SIG_SIGNALS))) {
      xcptRestoreXcptContext(pR0Context);
      return(pR0Context->ctx_RegEax);

   /*
    * For exceptions that are fatal and *not* process termination, we
    * need to dispatch the PTERM exception with the an identical context
    * record and a new report record with the link field filled in.
    */

   } else {
      if (DumpProcess != '\0')              /* Take a process dump  D26150 */
       { PLD32ApiDump (pPTDACur->ptda_Pid, ulTrapNumber); } /* D26150      */

      /* Perform this call regardless, this is the orginal call            */
      xcptFormatAndSendHardErr(ulTrapNumber, pR0ERepRec, pR0Context);
   }
   return(NO_ERROR);
}

/***LP   xcptRestoreXcptContext - Restore user's context.
 *
 *       xcptRestoreXcptContext restores the user's register set from
 *       the context record.
 *
 *       ENTRY   pContext - Context record
 *
 *       EXIT    none
 */

VOID xcptRestoreXcptContext( PCONTEXTRECORD pContext)
{
   ULONG   eflag;

   if (pContext->ContextFlags & CONTEXT_SEGMENTS) {
      if (KMSetClientRegister(pTCBCur, KM_REG_GS, pContext->ctx_SegGs) ||
          KMSetClientRegister(pTCBCur, KM_REG_FS, pContext->ctx_SegFs) ||
          KMSetClientRegister(pTCBCur, KM_REG_ES, pContext->ctx_SegEs) ||
          KMSetClientRegister(pTCBCur, KM_REG_DS, pContext->ctx_SegDs)) {
        panic("xcptRestoreXcptContext:KMSetClientRegister");
      }
   }

   if (pContext->ContextFlags & CONTEXT_INTEGER) {
      if (KMSetClientRegister(pTCBCur, KM_REG_EDI, pContext->ctx_RegEdi) ||
          KMSetClientRegister(pTCBCur, KM_REG_ESI, pContext->ctx_RegEsi) ||
          KMSetClientRegister(pTCBCur, KM_REG_EAX, pContext->ctx_RegEax) ||
          KMSetClientRegister(pTCBCur, KM_REG_EBX, pContext->ctx_RegEbx) ||
          KMSetClientRegister(pTCBCur, KM_REG_ECX, pContext->ctx_RegEcx) ||
          KMSetClientRegister(pTCBCur, KM_REG_EDX, pContext->ctx_RegEdx)) {
        panic("xcptRestoreXcptContext:KMSetClientRegister");
      }
   }

   if (pContext->ContextFlags & CONTEXT_CONTROL) {
      /*
       * Don't accept the user values for reserved EFLAGS bits. Use the
       * bits from the current flag image instead.
       */
      if (KMQueryClientRegister(pTCBCur, KM_REG_EFLAG, SSToDS(&eflag))
          != NO_ERROR) {
         panic("xcptRestoreXcptContext: KMQueryClientRegister");
      }

#ifdef INCDEBUG
      eflag &= DBG_RESERVED_FLAGS;    /* do not change reserved flag values */
      eflag |= (pContext->ctx_EFlags & ~DBG_RESERVED_FLAGS);
#endif /* DEBUG */

      if (KMSetClientRegister(pTCBCur, KM_REG_EBP, pContext->ctx_RegEbp) ||
          KMSetClientRegister(pTCBCur, KM_REG_EIP, pContext->ctx_RegEip) ||
          KMSetClientRegister(pTCBCur, KM_REG_CS,  pContext->ctx_SegCs) ||
          KMSetClientRegister(pTCBCur, KM_REG_EFLAG, eflag) ||
          KMSetClientRegister(pTCBCur, KM_REG_SS,  pContext->ctx_SegSs) ||
          KMSetClientReturnESP(pTCBCur,pContext->ctx_RegEsp)) {
        panic("xcptRestoreXcptContext:KMSetClientRegister");
      }
   }

   if (pContext->ContextFlags & CONTEXT_FLOATING_POINT) {

      xcptRestoreFPState((PFPSTATE)&(pContext->ctx_env[0]));

   }



}

/***LP   xcptCopyinXcptStruct - Copy exception info from R3 to R0
 *
 *       xcptCopyinXcptStruct copies the context and report records from
 *       the user's ring 3 stack to ring 0 kernel space.
 *
 *       ENTRY   pERepRec      - Pointer to user space exception report record
 *               pContext      - Pointer to user space context record
 *               pR0ERepRec    - Pointer to kernel space exception report record
 *               pR0Context    - Pointer to kernel space context record
 *
 *       EXIT    Success - NO_ERROR
 *               Failure - ERROR_PROTECTION_VIOLATION
 */

ULONG xcptCopyinXcptStruct(PEXCEPTIONREPORTRECORD pERepRec,
                           PCONTEXTRECORD pContext,
                           PEXCEPTIONREPORTRECORD pR0ERepRec,
                           PCONTEXTRECORD pR0Context)
{
   ULONG   rc;

   if ((rc = TKFuBuff((PCHAR) pR0ERepRec, (PCHAR) pERepRec,
       sizeof(EXCEPTIONREPORTRECORD), 0)) != NO_ERROR) {
      /*
       * The following values are a kludge, since that wasn't the user
       * exception but access violation is certainly an appropriate
       * message to put up at this point.
       */
      pR0ERepRec->ExceptionNum = XCPT_ACCESS_VIOLATION;
      pR0ERepRec->ExceptionAddress = (PVOID)XCPT_DATA_UNKNOWN;
      pR0ERepRec->fHandlerFlags = 0;
      pR0ERepRec->cParameters = 2;
      pR0ERepRec->ExceptionInfo[0] = XCPT_READ_ACCESS;
      pR0ERepRec->ExceptionInfo[1] = (ULONG)pERepRec;
      xcptFatalDispatchError(EXCSW, pR0ERepRec, NULL);
      return (rc);
   }
   if ((rc = TKFuBuff((PCHAR) pR0Context, (PCHAR) pContext,
        sizeof(CONTEXTRECORD), TK_UM_FATAL)) != NO_ERROR) {
      xcptFatalDispatchError(EXCSW, pR0ERepRec, NULL);
   }

   return (rc);
}

#pragma code_seg()
#pragma code_seg("EXMGSWAP_TEXT", "CODE")
/***LP  xcptRestoreFPState - Set up to restore the FP state on kernel exit.
 *
 *      xcptRestoreFPState sets up the thread to return to a piece of
 *      trampoline code that restores the current FP state and IRETs back
 *      to the original return code.
 *
 *      ENTRY   gfp_pFPEnv - Pointer to space to store FP environment
 *
 *      EXIT    None
 *
 *      USES    FLAGS
 */

VOID xcptRestoreFPState(PFPSTATE pFPEnv)
{
   ULONG ulReg;
   PULONG pulReg = SSToDS(&ulReg);

   /*
    * Put an IRET frame on the stack.
    */

   KMQueryClientRegister(pTCBCur, KM_REG_EFLAG, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_CS, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_EIP, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);

   /*
    * Now the old stack pointer, which might be 16-bit
    */
   KMQueryClientRegister(pTCBCur, KM_REG_SS, pulReg);
   KMPushClientStack(pTCBCur, 2, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_ESP, pulReg);
   *pulReg += 2;
   KMPushClientStack(pTCBCur, 4, pulReg, 0);

   /*
    * Now the selectors, which might not be flat.
    */
   KMQueryClientRegister(pTCBCur, KM_REG_DS, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_ES, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_FS, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);
   KMQueryClientRegister(pTCBCur, KM_REG_GS, pulReg);
   KMPushClientStack(pTCBCur, 4, pulReg, 0);

   /*
    * Stick the FP environment on the stack.
    */
   KMPushClientStack(pTCBCur, 108, pFPEnv, 0);


   KMSetFlatDispatch (pTCBCur, pfnXcptFRestore32);
}

