#define SCCSID  "@(#)tkextlst.c 6.10 92/03/27"

/*      Tasking - ExitList processing
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Jameel Hyder    Jan 1988        New file Created
 *
 *      Modifications:
 *      6/01/91 B722978 Edd Doutre   Added calls to TKTakeKernelSem() and
 *                                   TKClearKernelSem() instead of the
 *                                   Request/ClearMutex() stuff.  This
 *                                   allows easier debugging of nasty
 *                                   deadlocks by inserting checks into
 *                                   the macros (TKTake/ClearKernelSem).
 *
 *
 *
 */

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

#define INCL_TYPES                      // for basedef.h typedefs
#define INCL_ERROR_H
#define FNO_NUM FNO_TKEXTLST_C

#include <fnotask.h>
#include <basemac.h>
#include <basedef.h>
#include <osmac.h>
#include <ostypes.h>
#include <mi.h>
#include <infoseg.h>
#include <vmowner.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.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 <ldrxport.h>
#include <bseerr.h>
#include <kmode.h>
#include <bsetib.h>
#include <tklocal.h>
#include <mmph.h>
#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")
/***LP  tkExitList - dispatcher for 16- and 32-bit exit list functions
 *
 *      This function validates its parameters and calls the appropriate
 *      exitlist function.
 *
 *      tkExitList (pfunc, usftype, uscode)
 *
 *      ENTRY   pfunc           - 16:16 or 0:32 user function address
 *              usftype         - says what kind of pointer pfunc is
 *              uscode          - exitlist function code (and order number)
 *
 *      RETURN  int             - error code (NO_ERROR if successful)
 *
 *      CALLS   tkAddExitListRoutine, tkDeleteExitListRoutine,
 *              tkNextExitListRoutine
 *
 *      WARNING NONE
 *
 *      EFFECTS Adds or deletes exitlist routine from list or sets up for
 *              dispatch to next exitlist routine.
 */

int     tkExitList(ptr_t  pfunc,        // Function pointer
                   USHORT usftype,      // Function type
                   USHORT uscode)       // ExitList function code
{
        USHORT  usclass;                // Execution order class

        switch (uscode & 0x7F) {        // switch on function code
          case 1:                       // Add function to list
                usclass = (USHORT)(uscode >> 8); // get order class
                if (!(uscode & 0x80))
                        usclass += 0x100; // increment class if p-bit not set
                return (tkAddExitListRoutine(pfunc, usftype, usclass));

          case 2:                       // Delete function from list
                if (uscode & 0xFF80)    // Must not have order specified
                        return (ERROR_INVALID_DATA);
                return (tkDeleteExitListRoutine(pfunc, usftype));

          case 3:                       // Execute next routine
                if (uscode & 0xFF80)    // Must not have order specified
                        return (ERROR_INVALID_DATA);
                if (pPTDACur->ptda_ProcStatus & LIS_PS_EXITLIST)
                        return (tkNextExitListRoutine());
                                        // Process must be exiting
                // drop through
          default:
                return (ERROR_INVALID_FUNCTION);
        }
}


/***LP  tkAddExitListRoutine - Add Exit List Routine for Current Task
 *
 *      This routine adds an Exit List routine to the current task's
 *      Exit List.
 *
 *      ENTRY   pRoutine = Address of routine to be added to the Exit List if
 *                         Type equals TK_TYPE16 or TK_TYPE32 (a far 16-bit or
 *                         flat 32-bit address can be specified)
 *                       = Module handle of DLL whose termination routine is to
 *                         be added to the Exit List if Type equals TK_TYPEDT
 *              Type = Type of Exit List routine
 *                = TK_TYPE16 if far 16-bit routine
 *                = TK_TYPE32 if flat address
 *                = TK_TYPEDT if DLL termination routine
 *              Class & position = Execution Order (0 thru 0x1FF)
 *                All routines of order 0 are executed first     (DLL)
 *                All routines of order 255 are executed last    (DLL)
 *                All routines of order 0x100 are executed first (non-DLL)
 *                All routines of order 0x1FF are executed last  (non-DLL)
 *
 *      Note that all routines of a given class are executed in LIFO order.
 *
 *      EXIT    NO_ERROR
 *                      Routine added to exit-list
 *              ERROR_ALREADY_EXISTS
 *                      Routine already in exit-list
 *              ERROR_INVALID_EXITROUTINE_RING
 *              ERROR_PROTECTION_VIOLATION
 *              salloc error
 *
 *      FORMAT OF EXIT LISTS
 *
 *      The Exit List for a task is a singly linked list of fixed size
 *      entries. See tklocal.h for the layout.
 *
 *      Exit List entries appear in ascending order of class, there being
 *      one or more entries per class for which a routine has been specified.
 *      Each entry in the Exit List contains the address of an Exit List
 *      routine together with additional information such as its class and
 *      type (16 or 32-bit).  Each new entry is inserted at the head of the
 *      chain of entries for its class (if any).
 *
 *      At exit list time, the Exit List is processed from beginning to end.
 *      In this way, the Exit Routines are run in order of numerically
 *      ascending classes and in LIFO order within each class.
 *
 */

int     tkAddExitListRoutine(pRoutine, Type, Class)
ptr_t           pRoutine;
USHORT          Type;
register USHORT Class;
{
        register PEXENT  pExEnt, *ppExEntCur;
        PEXENT           *ppExEntSlot, ptemp;
        struct seldesc_s desc;
        int              fSlotFound;
        int              rc;

        if (Type == TK_TYPE16)
        {
            // Make sure its either RING 3 CODE or CONFORMING RING 2 CODE

            if ((pRoutine.ptr_sel & SEL_RPLMASK) != SEL_RPL3)
               return (ERROR_INVALID_EXITROUTINE_RING);

            if ((SELGetDescInfo(pRoutine.ptr_sel, pPTDACur->ptda_handle,
                                                SSToDS(&desc)) != NO_ERROR) ||
               (((desc.desc_acc & (D_SEG | D_CODE | D_PRIV | D_CONFORM)) !=
                                (D_SEG | D_CODE | D_DPL2 | D_CONFORM))      &&
                ((desc.desc_acc & (D_SEG | D_CODE | D_PRIV)) !=
                                                (D_SEG | D_CODE | D_DPL3))))
                return (ERROR_PROTECTION_VIOLATION);
        }

        if ( (rc = TKTakeKernelSem()) != NO_ERROR)
          return (rc);

        fSlotFound = FALSE;
        ppExEntCur = ppExEntSlot = &pPTDACur->ptda_pexllist;

        for (pExEnt = *ppExEntCur; pExEnt != NULL; pExEnt = *ppExEntCur)
        {
            if (Class <= pExEnt->exl_class)
            {
                if (!fSlotFound)
                {
                    ppExEntSlot = ppExEntCur;   // Mark slot
                    fSlotFound = TRUE;          // we have found our slot
                    if (Class < pExEnt->exl_class)
                       break;                   // we needn't look beyond this
                }
                if (pExEnt->exl_addr.ptr_flat   == pRoutine.ptr_flat &&
                    pExEnt->exl_class           == Class &&
                    pExEnt->exl_type            == Type)
                {
                       TKClearKernelSem();
                       return (ERROR_ALREADY_EXISTS);
                }
            }
            // If Class is greater then any recorded so far, add at end
            else if (!fSlotFound && ((*ppExEntCur)->exl_next == NULL))
            {
                ppExEntSlot = &(*ppExEntCur)->exl_next;
                break;
            }
            ppExEntCur = &(*ppExEntCur)->exl_next;
        }

        if ( (rc = VMAllocKHB( VM_HKH_PUB_RESRW,   // PERF: defect 98488
                               sizeof (EXENT),
                               (VMHOB)TKEXITLISTOWNER,
                               0,
                               SSToDS(&ptemp) ) ) == NO_ERROR )
        {
            pExEnt = ptemp;
            pExEnt->exl_next = *ppExEntSlot;
            *ppExEntSlot = pExEnt;
            pExEnt->exl_class = Class;
            pExEnt->exl_addr.ptr_flat = pRoutine.ptr_flat;
            pExEnt->exl_type = Type;
        }

        TKClearKernelSem();
        return (rc);
}

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

/***LP  tkDeleteExitListRoutine(pRoutine, Class, Type)
 *
 *      This routine deletes an entry for a routine from the Exit List.
 *
 *      ENTRY   pRoutine = Address of routine to be deleted from the Exit List
 *                A far 16-bit or flat 32-bit address can be specified
 *              Class = Execution class
 *                This is the execution order class to which the routine
 *                should belong.
 *              Type = Type of Exit List routine
 *                = TK_TYPE16 if far 16-bit routine
 *                = TK_TYPE32 if flat address
 *
 *      EXIT    NO_ERROR
 *                      Routine deleted
 *              ERROR_PROC_NOT_FOUND
 *                      If routine not found
 *
 */

int     tkDeleteExitListRoutine(pRoutine, Type)
ptr_t   pRoutine;
USHORT  Type;
{
        register PEXENT pExEnt;
        register PEXENT *ppExEnt;
        int             rc;

        if ( (rc = TKTakeKernelSem()) != NO_ERROR)
          return (rc);

        ppExEnt = &pPTDACur->ptda_pexllist;
        for (pExEnt = *ppExEnt;
             pExEnt != NULL &&
             (pExEnt->exl_type != Type ||
              pExEnt->exl_addr.ptr_flat != pRoutine.ptr_flat);
             pExEnt = *ppExEnt) {
                ppExEnt = &pExEnt->exl_next;
        }
        if (pExEnt != NULL) {
            *ppExEnt = pExEnt->exl_next;
            VMFreeKHB(VM_HKH_PUB_RESRW, pExEnt);  // PERF: defect 98488
        }
        TKClearKernelSem();
        return ((pExEnt == NULL) ? ERROR_PROC_NOT_FOUND : NO_ERROR);
}


#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***LP  tkNextExitListRoutine - Setup to Run Next Exit List Routine
 *
 *      This routine sets up the kernel stack so that the next Exit List
 *      routine will be run when the current thread (thread 1) of the task
 *      next leaves the kernel.
 *
 *      Note that this doesn't need the exitlist semaphore protection against
 *      threads of the same process because when it runs, there is only
 *      thread #1 of the process left alive.
 *
 *      ENTRY   None
 *
 *      EXIT    NO_ERROR
 *                      Next exit-list routine is setup
 *              else, control transfers to TKTermTask and does not return
 *
 *      On entry to the exit-list routine, the stack looks like:
 *
 *              (SP)   -> Return address - DWORD
 *              (SP+4) -> Exit Code      -  WORD for a 16-bit exit-list routine
 *                                       - DWORD for a 32-bit exit-list routine
 */

void    tkNextExitListRoutine()
{
        register PPTDA  pPTDA;
        register PEXENT pExEnt;
        ldrmi_t         modinfo;
        KMREG           usrregs;
        ULONG           exittype;
        struct seldesc_s seldesc;
        TIBP            tibp;           /* TIB & TIB2         92257   */
        extern  CRI     criTSF;
        struct {                        // stack on 32-bit DLL term entry
            ULONG d_hmte;               // module handle B727937 Make same as .exe
            ULONG d_fterm;              // termination flag (1)
        } dllstk;

error_try_next:    // jump back on error to try the next one

        pPTDA = pPTDACur;
        pExEnt = pPTDA->ptda_pexllist;
        if (     pExEnt == NULL
              || LDRGetTKModuleInfo( pPTDA->ptda_module,
                                     SSToDS(&modinfo)) != NO_ERROR)
        {
                tkCleanUpExitList();    // No more exit list routines to run
                TKTermTask();           // does not return
        }

        MMPHPreExitListCall()           // Performance Hook (12/02)

        pPTDA->ptda_pexllist = pExEnt->exl_next;
        KMQueryAllClientRegisters(pTCBCur, SSToDS(&usrregs));

        if (pExEnt->exl_type == TK_TYPE16)
        {    // 16-bit exit-list routine
                usrregs.kmreg_cs  = pExEnt->exl_addr.ptr_sel;
                usrregs.kmreg_eip = pExEnt->exl_addr.ptr_off;
                if (modinfo.ldrmi_ldtype == LDR_32bit)
                { // 32-bit EXE
                    // If the stack base pointer is a multiple of 64K we
                    // need to reduce it by four bytes so that SS gets
                    // filled with the proper selector
                    usrregs.kmreg_ss =
                        LaToSelTiled(modinfo.ldrmi_stackptr.ptr_flat & 0xFFFFL
                                     ? (modinfo.ldrmi_stackptr.ptr_flat)
                                     : (modinfo.ldrmi_stackptr.ptr_flat -
                                        sizeof(ULONG)))
                                     | RPL_RING3;
                    usrregs.kmreg_esp =
                        (modinfo.ldrmi_stackptr.ptr_flat & 0xFFFFL
                        ? (modinfo.ldrmi_stackptr.ptr_flat)
                        : (modinfo.ldrmi_stackptr.ptr_flat -
                           sizeof(ULONG))) & 0xFFFFL;     //d58228

                    /* 92257 Begin */
                    tibp.tibp_indep.tib_pstack =
                        ((modinfo.ldrmi_stackptr.ptr_flat & 0xFFFFL
                        ? (modinfo.ldrmi_stackptr.ptr_flat)
                        : (modinfo.ldrmi_stackptr.ptr_flat -
                           sizeof(ULONG))) - pTCBCur->TCBcbStackMax);


                    tibp.tibp_indep.tib_pstacklimit =
                       (void *)(modinfo.ldrmi_stackptr.ptr_flat & 0xFFFFL
                        ? (modinfo.ldrmi_stackptr.ptr_flat)
                        : (modinfo.ldrmi_stackptr.ptr_flat -
                           sizeof(ULONG)));

                    /*
                     *  Set up TIB in user space.
                     */

                      TKSuBuff((char *) &(((TIBP *)pTCBCur->TCBptib)->tibp_indep.tib_pstack),
                               SSToDS(&(tibp.tibp_indep.tib_pstack)),
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstack)) +
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstacklimit)),
                               TK_UM_FATAL);

                    /* 92257 End */

                    // If stack needs bumping AND there is room to do it,...
                    if (usrregs.kmreg_esp < _4K &&
                        modinfo.ldrmi_stackptr.ptr_flat -
                        modinfo.ldrmi_stackbase > _64K)
                    {
                        usrregs.kmreg_ss -= HUGEINCR;
                        usrregs.kmreg_esp = 0xFFFE;
                    }
                    usrregs.kmreg_ds  = R3DS;
                }
                else
                {
                    // see if program's orignal stack is valid. If so we use
                    // it, else we attempt to use the stack it entered the
                    // system with.

                    if ( SELGetDescInfo( modinfo.ldrmi_stackptr.ptr_sel,
                                         pPTDA->ptda_handle,
                                         SSToDS(&seldesc) ) == NO_ERROR )
                    {
                   // This next 3-lines were moved from within the loop as they should
                   // deal only with the stack. " Ray" #76497.

                            usrregs.kmreg_ds = (modinfo.ldrmi_autods)
                                               ? modinfo.ldrmi_autods
                                               : modinfo.ldrmi_stackptr.ptr_sel;

                        /* p025236
                         * Reset the stack if it is greater or equal to 4k,
                         * or the stack we are currently running on is a
                         * ring 2 stack
                         */
                        if ((modinfo.ldrmi_stackptr.ptr_off >= _4K) ||
                            ((usrregs.kmreg_ss & RPL_MASK) == RPL_RING2))
                        // if ( modinfo.ldrmi_NEStack < _4K )
                        // Hack to avoid changing H file for r205 ship.
                        {
                            usrregs.kmreg_ss = modinfo.ldrmi_stackptr.ptr_sel;
                            usrregs.kmreg_esp= modinfo.ldrmi_stackptr.ptr_off;
                            /* 92257 Begin */
                            tibp.tibp_indep.tib_pstack = (void *)((ulong_t)seldesc.desc_laddr);
                            tibp.tibp_indep.tib_pstacklimit = (void *)((ulong_t)seldesc.desc_laddr +
                                 (ulong_t)modinfo.ldrmi_stackptr.ptr_off);
                            /*
                             *  Set up TIB in user space.
                             */

                              TKSuBuff((char *) &(((TIBP *)pTCBCur->TCBptib)->tibp_indep.tib_pstack),
                                       SSToDS(&(tibp.tibp_indep.tib_pstack)),
                                        (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstack)) +
                                        (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstacklimit)),
                                       TK_UM_FATAL);
                            /* 92257 End */

                         // This next 3-lines were moved above "p025236" Ray #76497.
                         // usrregs.kmreg_ds = (modinfo.ldrmi_autods)
                         //                    ? modinfo.ldrmi_autods
                         //                    : modinfo.ldrmi_stackptr.ptr_sel;

                        }
                        else
                           goto Change_SS_ESP;
                    }
                    else {
                        /*
                         * If ss is flat de-linearize ss:esp since we are
                         * going to run a 16 bit exit list routine.
                         */
Change_SS_ESP:
                        if (usrregs.kmreg_ss == R3DS) {
                           usrregs.kmreg_ss =
                              LaToSelTiled(usrregs.kmreg_esp)|RPL_RING3;
                           usrregs.kmreg_esp = usrregs.kmreg_esp & 0xFFFF;
                        }
                    }
                }
        }
        else if (pExEnt->exl_type == TK_TYPEDT)
        { // DLL termination routine
                // Get information on DLL and return ESP

                // B735243 - ship issue - check for bad return code -
                // do not execute garbage

                if ( LDRGetTKModuleInfo( (VMHOB)pExEnt->exl_addr.ptr_flat,
                                         SSToDS(&modinfo)) )
                {
                    pPTDA->ptda_pexllist = pExEnt->exl_next;
                    goto error_try_next;
                }
             /* Added - 74177, commented out - 75809 */
             /* LDRResetDLLOnExitList( (VMHOB)pExEnt->exl_addr.ptr_flat );      75809 */

                KMQueryClientReturnESP(pTCBCur, SSToDS(&usrregs.kmreg_esp));

                // If 16-bit module, detile the user's stack and use flat DS
                if (usrregs.kmreg_cs & SEL_LDTBIT) {
                    // Only detile the user's stack if it is not already
                    if (usrregs.kmreg_ss != R3DS) {
                        usrregs.kmreg_esp = SelToLaTiled(usrregs.kmreg_ss) +
                            (USHORT) usrregs.kmreg_esp;
                        usrregs.kmreg_ss = R3DS;
                    }
                    usrregs.kmreg_ds = R3DS;
                    usrregs.kmreg_es = R3DS;
                }

                // Set up flat dispatch
                usrregs.kmreg_cs  = R3CS;
                usrregs.kmreg_eip = modinfo.ldrmi_startaddr.ptr_flat;
        }
        else {                                  // 32 bit exit-list routine
                usrregs.kmreg_eip = pExEnt->exl_addr.ptr_flat;
                usrregs.kmreg_cs  = R3CS;
                if (modinfo.ldrmi_ldtype == LDR_16bit) { /* 92257 */
                    usrregs.kmreg_esp = SelToLaTiled(modinfo.ldrmi_stackptr.ptr_sel)
                                        + modinfo.ldrmi_stackptr.ptr_off;
                    /* 92257 Begin */
                    tibp.tibp_indep.tib_pstack = SelToLaTiled(modinfo.ldrmi_stackptr.ptr_sel);

                    tibp.tibp_indep.tib_pstacklimit =
                        SelToLaTiled(modinfo.ldrmi_stackptr.ptr_sel)
                                    + modinfo.ldrmi_stackptr.ptr_off;

                    /*
                     *  Set up TIB in user space.
                     */

                      TKSuBuff((char *) &(((TIBP *)pTCBCur->TCBptib)->tibp_indep.tib_pstack),
                               SSToDS(&(tibp.tibp_indep.tib_pstack)),
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstack)) +
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstacklimit)),
                               TK_UM_FATAL);

                    /* 92257 End */
                }    /* 92257 */
                else {  /* 92257 */
                    usrregs.kmreg_esp = modinfo.ldrmi_stackptr.ptr_flat;
                    /* 92257 Begin */

                    tibp.tibp_indep.tib_pstack =
                           (void *)(modinfo.ldrmi_stackptr.ptr_flat -
                                    pTCBCur->TCBcbStackMax);

                    tibp.tibp_indep.tib_pstacklimit = modinfo.ldrmi_stackptr.ptr_flat;
                                                      pTCBCur->TCBcbStackMax;

                    /*
                     *  Set up TIB in user space.
                     */

                      TKSuBuff((char *) &(((TIBP *)pTCBCur->TCBptib)->tibp_indep.tib_pstack),
                               SSToDS(&(tibp.tibp_indep.tib_pstack)),
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstack)) +
                                (sizeof(&((PTIBP) 0)->tibp_indep.tib_pstacklimit)),
                               TK_UM_FATAL);

                    /* 92257 End */
                }    /* 92257 */
                usrregs.kmreg_ss  = R3DS;
                usrregs.kmreg_ds  = R3DS;
                usrregs.kmreg_es  = R3DS;
        }
        usrregs.kmreg_eax =
        usrregs.kmreg_ebx =
        usrregs.kmreg_ecx =
        usrregs.kmreg_edx =
        usrregs.kmreg_esi =
        usrregs.kmreg_edi =
        usrregs.kmreg_ebp =
        usrregs.kmreg_gs  = 0;
        usrregs.kmreg_fs  = R3TIB;

        // Copy users register on kernel stack.

        KMSetAllClientRegisters(pTCBCur, SSToDS(&usrregs));

        // Set the Return ESP to the desired value.

        KMSetClientReturnESP(pTCBCur, usrregs.kmreg_esp);

        // Place the appropriate information on the stack. For a DLL
        // termination routine, this is the termination flag and the
        // module handle.  For an exitlist routine, this is the exit
        // type.

        if (pExEnt->exl_type == TK_TYPEDT) {
                dllstk.d_fterm = 1;
                dllstk.d_hmte  = (VMHOB)pExEnt->exl_addr.ptr_flat;

                // Copy flag and handle to user's stack.

                KMPushClientStack(pTCBCur, sizeof (dllstk), SSToDS(&dllstk),
                    TK_UM_FATAL);
        }
        else {
                exittype = pPTDA->ptda_ulExitType;

                if (pExEnt-> exl_type == TK_TYPE16) {

                    // FEATURE FEATURE: Near 16 exitlist routines.
                    //
                    // Copy exittype to the return address location on stack
                    // such that if a 16:16 routine is declared either near or far,
                    // it'll get the exittype right.  This is done only for
                    // compatibility with 1.1/1.2.  Note that the "correct"
                    // semantics don't require this.

                    exittype |= exittype << 16;
                }

                // Copy exittype to user's stack.

                KMPushClientStack(pTCBCur, sizeof (ULONG), SSToDS(&exittype),
                    TK_UM_FATAL);
        }

        // Now place the return address on the stack.

        // If either a far 16 or a near 32 exitlist routine just does
        // a RET, it returns to a Dos(32)Exit thunk, which will (nearly)
        // emulate a Dos(32)ExitList(TRANSFER).  The sole difference is a
        // possible 32-bit PTERM exception if a 32-bit exception handler
        // is installed in ExitList.
        // Naturally, this doesn't work for near 16 ExitList handlers...

        KMPushClientStack(pTCBCur, sizeof(ptr_t),
            ((pExEnt-> exl_type == TK_TYPE16) ?
            (PVOID) &pfntkDosExit : (PVOID) &pfntkDos32Exit), TK_UM_FATAL);

        VMFreeKHB(VM_HKH_PUB_RESRW, pExEnt);   // unlink this routine from list
                                               // PERF: defect 98488
}

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

/***EP  TKCheckExitListRef - Check for References to Modules From ExitList
 *
 *      This routine is called by the loader when a request to free a
 *      module is received.  The modules that would be freed are first
 *      marked, and this routine is called to determine if any routines
 *      on the Exit List reside in those modules.  Note that an Exit List
 *      routine address may be an alias.
 *
 *      ENTRY   None
 *
 *      EXIT    NO_ERROR : no references to affected modules
 *
 *              ERROR_INVALID_ACCESS
 *                [ This is the 1.1 code, but I'd prefer something like
 *                  ERROR_NEEDED_BY_EXITLIST ]
 *                  One of the modules to be freed contains a routine
 *                  on the current task's Exit List
 *              ERROR_INTERRUPT
 *                  Interrupted by signal
 *              Errors from VMObjectInfo
 *
 */

int     TKCheckExitListRef()
{
        PEXENT  pExEnt;
        VMOD    od;
        int     rc;

        if ( (rc = TKTakeKernelSem()) != NO_ERROR)
          return (rc);

        for ( pExEnt = pPTDACur->ptda_pexllist;
              pExEnt != NULL;
              pExEnt = pExEnt->exl_next )
        {
                // Do not worry about DLL termination routines because they
                // are handled elsewhere
                if (pExEnt-> exl_type != TK_TYPEDT)
                {

                        /*
                         * If error from VMObjectInfo on the exitlist routine's
                         * linear address, break.
                         */

                        VMPTDAOd(od) = pPTDACur;

                        VMLaddrOd(od) = (pExEnt-> exl_type == TK_TYPE32) ?
                                        pExEnt-> exl_addr.ptr_flat :
                                        SelToLaTiled (pExEnt-> exl_addr.ptr_sel) +
                                        pExEnt-> exl_addr.ptr_off;

                        if ((rc = VMGetOd(0, SSToDS(&od))) != NO_ERROR)
                                break;

                        if ((rc = LDRIsMTEDetaching(VMMTEOd(od))) != NO_ERROR)
                        break;
                }
        }

        TKClearKernelSem();

        return (rc);
}


#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***LP  tkCleanUpExitList - frees the exit-list for this process
 *
 *      This is called from w_Exit before this process is finally killed.
 *      And also from TKNextExitListRoutine if we run into any problems.
 *
 *      ENTRY:  None
 *
 *      EXIT:   None
 *
 */

void    tkCleanUpExitList()
{
        register PEXENT pExEnt;
        register PEXENT pExEnt1;

        pExEnt = pPTDACur->ptda_pexllist;
        while (pExEnt != NULL) {
                pExEnt1 = pExEnt;
                pExEnt = pExEnt->exl_next;
                VMFreeKHB (VM_HKH_PUB_RESRW, pExEnt1); // PERF: defect 98488
        }
}


#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")
/***LP  VRExitList - API entry point for 16-bit exitlist routines
 *
 *      This is called by the DOSEXITLIST API. It just adds a type
 *      parameter and calls tkExitList.
 *
 *      ENTRY   pfunc  - exit-list routine address
 *              uscode - funtion code and class
 *
 *      RETURN  code returned by tkExitList
 *
 *      EFFECTS see tkExitList
 */
int cdecl VRExitList(sef, pfunc, uscode)
SEF             sef;                    // System Entry Frame
USHORT          uscode;                 // ExitList function code
ptr_t           pfunc;                  // Function pointer
{
        pfunc.ptr_sel |= (pTSDCur->TSDUserSS & SEL_RPLMASK);
        return (tkExitList(pfunc, TK_TYPE16, uscode));
}
#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")

/***LP  VR32ExitList - API entry point for 32-bit exitlist routines
 *
 *      This is called by the DOS32EXITLIST API. It just adds a type
 *      parameter and calls tkExitList.
 *
 *      ENTRY   pfunc  - exit-list routine address
 *              uscode - funtion code and class
 *
 *      RETURN  code returned by tkExitList
 *
 *      EFFECTS see tkExitList
 */

int cdecl VR32ExitList(sef, uscode, pfunc)
THUNKSEF        sef;                    // System Entry Frame
USHORT          uscode;                 // ExitList function code
ptr_t           pfunc;                  // Function pointer
{
        return (tkExitList(pfunc, TK_TYPE32, uscode));
}
#pragma code_seg()
