/*static char *SCCSID = "@(#)tklibi.c   6.12 92/03/04";*/
#define SCCSID  "@(#)tklibi.c   6.12 92/03/04"
/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      This module implements library module initialization.
 *
 *      Summary of Library Initialization :
 *
 *      1) TKLibiRecordMTE is called for each newly attached library during a
 *         load.  This can be due to either a DOSEXECPGM or DOSLOADMODULE call.
 *         For a DOSEXECPGM, we use ptda_pTCB1 in the child's PTDA as the target
 *         thread, otherwise its the current TCB (pTCBCur).
 *
 *      2) If the Load or Exec succeeds, TKLibiStartDispatch must be called
 *         after ALL the new libraries are attached. TKLibiStartDispatch sets
 *         up to return to LibiDispatcher. The LIBLOAD Debug event is also
 *         taken into account at this point by calling Debug to take appropriate
 *         action. The LibiDispatcher resides in doscall1.dll whose address is
 *         picked up by TKLibiStartDispatch first time through.
 *
 *      3) A failing DOSLOADMODULE needs to call TKLibiError to do cleanup,
 *         but not call TKLibiStartDispatch. Once TKLibiStartDispatch is
 *         called successfully, it is too late to abort LibInit.
 *
 *      4) If Debug is enabled, it detects the LIBLOAD event at ExitKmode,
 *         after the loader semaphore is cleared.  Debug then calls
 *         TKLibiNewLibrary to get the MTE handles one at a time.
 *         Debug must call TKLibiDbgDone when it is finished with the
 *         LibInit notification.  See TKLibiDbgDone for more.
 *
 *      If there are actual library initialization routines to be run :
 *
 *      5) Instead of returning directly to the user's link address,
 *         control returns to the ring 3 LibiDispatcher procedure.
 *         LIBRARY INITIALIZATION MAY NOT OCCUR FOR RING 2 MODULES, OR
 *         FROM A RING 2 DOSLOADMODULE CALL TO A RING 3 INIT ROUTINE!
 *         Ring 2 DOSLOADMODULE calls are supported only if there are no
 *         LibInit routines to be run during the entire load.
 *
 *      6) LibiDispatcher calls TKLibiInitNextDLL (via DOSLIBINIT) to select
 *         the next LibInit routine to run.
 *
 *      7) TKLibiInitNextDLL then selects a LibInit routine to run, edits the
 *         kernel stack to setup return to the libinit routine address. The
 *         user stack is also modified if a 32-bit exe is loading a 16-bit DLL,
 *         in which case the tiled stack is presented to the init routine, or a
 *         16-bit module is loading a 32-bit DLL, in which case the stack is
 *         linearized for the init routine.
 *         For 16-bit DLLs the user DS, ESI and EDI are edited to contain the
 *         auto_ds, heapsize and MTE handle of the module respectively. For a
 *         32-bit DLL, these values are grafted on the stack.
 *         A return address to the dispatcher is grafted on user stack so that
 *         the DLL init routine returns to the dispatcher. The return address is
 *         that of LibiDispatcher16 for a 16-bit DLL and LibiDispatcher32 for a
 *         a 32-bit DLL. If there are no more libinit routines to be run,
 *         TKLibiInitNextDLL returns to the caller of DOSLOADMODULE or the
 *         start address in case of DOSEXECPGM.
 *
 *      8) If the LibiInit routine returns error TKLibiInitNextDLL starts
 *         error processing. For DOSEXECPGM case the process is terminated.
 *         For DOSLOADMODULE, all modules whose MTEs are recorded for this
 *         level and its descendants are free'd via LDRFreeModule.
 *         ERROR_INIT_ROUTINE_FAILED is returned to the caller of
 *         DOSLOADMODULE. If the initialization completes successfully
 *         LibiDispatcher repeats from (6).
 *
 *      Library initialization may be nested, that is, a DOSLOADMODULE
 *      call may be made within a library init routine.  In this case, any
 *      library initialization pertinent to the newest DOSLOADMODULE is
 *      performed BEFORE returning from the DOSLOADMODULE.  To support
 *      this, the LibInit blocks are chained together.  See tkLibiAddMTE.
 *
 * Design change for Borg :
 * _tkLibiRecordMte may now be called multiple times for each DLL per load.
 * This is to allow the loader to walk the tree completely to implement DLL
 * init ordering. Tasking changes are to support this ordering change and
 * only call each init routine once. Paul.
 *
 *                           Modifications
 *
 *   07/28/94   86186   Change Team - Set owner during VMalloc to HOBNULL
 *                      rather than TKR3STACKOWNER so that the memory manager
 *                      does not trap referencing the ob_hobowner field.
 *   10/11/94   94251   Change Team - Rather than tkAssert if TCBpLibiCurr is
 *                      null, just don't attempt to call tkLibiCleanup twice.
 *
 */

#define INCL_TYPES              // for basedef.h typedefs
#define INCL_ERROR_H            // for bseerr.h
#define INCL_ERROR2_H           // for bseerr.h

#include        <basemac.h>
#include        <basedef.h>

#define FNO_NUM FNO_TKLIBI_C
#include        <fnotask.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        <screen.h>
#include        <sem.h>
#include        <find.h>
#include        <tkptda.h>
#include        <bseerr.h>
#include        <ldrxport.h>

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

#include        <tklibi.h>
#include        <bsetib.h>
#include        <tklocal.h>
#include        <ordinals.h>
#include        <string.h>
#define FOR_EXEHDR      1
#include        <newexe.h>
#include        <exe386.h>
#include        <kmode.h>                      /* Must be before ldrvars.h */
#include        <ldrvars.h>


/***ED+ TKLibiCur - chain of libint records for level being built
 *
 *      TKLibiCur is the most recently added libinit record in the
 *      chain for the current DOSLOADMODULE or DOSEXECPGM call.
 *      Note that the links are backwards, i.e., the newest record
 *      points to the next newest and the second oldest points
 *      to the oldest (which doesn't point to anybody).
 *
 *      Note that we must own the loader semaphore while we are
 *      using this structure.
 */

LIBI TKLibiCur = { 0, 0, LIBI_TOPBLOCK, 0, 0, 0 };       //p025236

/*end*/

extern PFN     pfnDLLTermDisp32;       // 32-bit DLL termination dispatcher
PFN            pfnXcptDispatcher32;    // 32-bit Exception Dispatcher
PFN            pfnXcptFRestore32;      // FRestore trampoline code
PFN            pfntkDos32Exit;         // 32-bit Dos32Exit return thunk
struct FarPtr  pfntkDosExit;           // 16-bit DosExit return thunk
struct FarPtr  pfnSigDispatch;         // Signal dispatcher

/***ED+ Libinit dispatcher addresses
 *
 *      Libinit dispatcher addresses are initialised by calling loader
 *      to get these addresses from doscall1.dll. The dispatchers currently
 *      reside in doscall1.dll
 */

struct FarPtr LibiDispatcher  = { 0, 0 };     // Generic Dispatcher (16-bit)
struct FarPtr LibiDispatcher16= { 0, 0 };     // 16-bit Dispatcher
ULONG         LibiDispatcher32= NULL;         // 32-bit Dispatcher

/*end*/




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




/****LP tkLibiInitStatic - Inititialilize TKLibiCur structure
 *
 *      Initialize TKLibiCur structure to virgin state.
 *
 *      ENTRY   pprev           - "previous" block
 *
 *      EXIT    None
 */
void tkLibiInitStatic(PLIBI pprev)
{
    memset( (PCHAR)&TKLibiCur, 0, sizeof(LIBI) );

    if (pprev)              /* If not first block */
    {
        /* Inherit libi_pregs */
        TKLibiCur.libi_pregs  = pprev->libi_pregs;

        /* Inherit libi_status */
        TKLibiCur.libi_status = pprev->libi_status & (UCHAR) ~LIBI_TOPBLOCK;

        /* Link to preceding block */
        TKLibiCur.libi_pprev = pprev;

        /* Link preceding block to this block */
        pprev->libi_pnext = &TKLibiCur;
    }
    else
        TKLibiCur.libi_status = LIBI_TOPBLOCK;
                                        /* Else mark as first block */
}

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

/****LP tkLibiCleanup - Free all the libi data blocks
 *
 *      This is called from TKLibiInitNextDLL when the top most loadmodule
 *      has completed successfully or from thread termination code.
 *
 *      ENTRY:  None
 *
 *      EXIT:   None
 *              The allocated heap for the current level is free'd up.
 *
 *      CALLS   tkLibiFreeHeap
 */
void tkLibiCleanup( register PTCB pTCB )
{
    PLIBI   plibitmp;
    ULONG   rc;

    if (pTCB->TCBpLibiHead != NULL)
    {

        /* p025236
         * Find the top most block pointed to by TCBpLibiHead.
         * If a stack was allocated for libinit free it.
         */
        plibitmp = pTCB->TCBpLibiHead;
        if ( !IsTopBlock(plibitmp) )
        {
            for ( ;
                  plibitmp->libi_pprev != NULL;
                  plibitmp = plibitmp->libi_pprev );
        }
        tkAssert( IsTopBlock(plibitmp) );

        if( StackAllocated(plibitmp) )
        {
            tkAssert( plibitmp->libi_stackSel );
            rc = VMFreeMem( SELSelToLa( plibitmp->libi_stackSel, pPTDACur ),
                            pPTDACur->ptda_handle,
                            0 );
           tkAssert( !rc );
        }

        tkLibiFreeHeap(pTCB->TCBpLibiHead, FALSE);
    }

    pTCB->TCBpLibiCurr = pTCB->TCBpLibiHead = NULL;
}


/***LP  tkLibiFreeHeap - Free all the heap associated with this level down
 *
 *      Free up all the heap allocated for all the levels lower than and
 *      including this  one. Delink this block from its ancestor and its
 *      siblings. Make its ancestor current.
 *
 *      ENTRY   plibi   Pointer to the libinit block
 *              fSibl   Free siblings, if TRUE
 *
 *      EXIT    None
 *
 *      CALLS   VMFreeKHB, and self (recursive)
 */
void tkLibiFreeHeap( register PLIBI plibi,
                     int            fSibl )
{
    register PLIBI  plibic;         // Current libinit block being processed
    register int    i;
    PLIBI           plibifree;      // libinit block to be freed
    PLIBI           plibisibl;      // Pointer to sibling block

    plibic = plibi;
    do
    {
        plibisibl = plibic->libi_psibl;
        if (plibic->libi_pregs != NULL)
        {
            VMFreeKHB( VM_HKH_PUB_SWAPRW,
                       plibic->libi_pregs );
        }
        for ( plibi = plibic ; plibi != NULL; )
        {
            for ( i = plibi->libi_tcnt - 1; i >= plibi->libi_rcnt; i-- )
            {
                if (plibi->libi_pdesc[i] != NULL)
                {
                    tkLibiFreeHeap( plibi->libi_pdesc[i],
                                    TRUE );
                }
            }
            plibifree = plibi;
            plibi = plibi->libi_pprev;
            if (plibifree == &TKLibiCur)
            {
                tkLibiInitStatic( NULL );
            }
            else
            {
                VMFreeKHB( VM_HKH_PUB_SWAPRW,
                           plibifree );
            }
        }
        plibic = plibisibl;

    } while ( fSibl && plibic != NULL );
}



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


/***EP  TKLibiRecordMTE - Record applicable MTE handles in LibInit block
 *
 *      TKLibiRecordMTE is called with the MTE handle for each newly attached
 *      library in a single DOSLOADMODULE or DOSEXECPGM call.
 *
 *      TKLibiRecordMTE determines if this MTE handle needs to be saved for
 *      future use.  If so, the MTE handle is added to the LibInit block.
 *
 *      If there exists a LibInit routine that needs to be run, LIBI_HASINIT
 *      will be set in libi_status. The decision to set up the LibInit
 *      dispatcher is delayed until the end of the load.
 *
 *      tkLibiRecordMTE saves away the MTE handle in the LibInit block,
 *      possibly allocating and linking a new LibInit block.
 *
 *      If there is an actual LibInit routine for this module,
 *      then LIBI_HASINIT will be set in libi_status.  Setting up the
 *      LibInit dispatcher is delayed until the end of the load, based
 *      on this flag.
 *
 *      A single DOSLOADMODULE call can have multiple Libinit blocks, if the
 *      number of MTE handles exceed MAXHDL. In this case the first block will
 *      be marked LIBI_TOPBLOCK.
 *      In case of nested DOSLOADMODULE, the blocks will be nested bottom up,
 *      that is in LIFO order.
 *
 *      DLLs that have no code or data (ie, forwarders) are not passed to
 *      tkLibiRecordMTE by the loader. This means that the debugger will not
 *      be notified of the load event.
 *
 *      ENTRY   hMTE    MTE Handle to be recorded.
 *      ENTRY   fDispReqd  - TRUE if dispatcher required.
 *
 *      EXIT    NO_ERROR
 *                      MTE recorded.
 *              ERROR
 *                      Error code
 *
 *
 */

int TKLibiRecordMTE( register VMHOB   hMTE,
                     int              fDispReqd )
{
    register PLIBI  plibi;
             PLIBI  ptmp;
    int             rc;
    register char   i;
    int             fFound;

    // Reorder MTEs in blocks, in case this is a handle we already have.

    // Find start.
    plibi = &TKLibiCur;
    while (plibi)
    {
        ptmp = plibi;
        plibi = plibi->libi_pprev;
    }
    plibi = ptmp;  // found the first block

    fFound = 0;
    ptmp = 0;
    while (plibi)
    {
        // shuffling date across boundaries
        if ( (fFound) && (ptmp) )
        {
            ptmp->libi_handles[MAXHDL-1] = plibi->libi_handles[0];
            plibi->libi_handles[0] = hMTE;
        }

        i = (char)fFound;  // start at 1 or 0 ?
        while (i<plibi->libi_tcnt)
        {
            if (fFound)
            {
                plibi->libi_handles[i-1] = plibi->libi_handles[i];
                plibi->libi_handles[i] = hMTE;
            }
            else
            {
                if (plibi->libi_handles[i] == hMTE )
                {
                    fFound = 1;
                }
            }
            i++;
        }

        ptmp = plibi;
        plibi = plibi->libi_pnext;
    }

    if (fFound)
        return(NO_ERROR);  // all done - list just reordered.

    // Check if new record needed
    if ( (plibi = &TKLibiCur)->libi_tcnt == MAXHDL )
    {
        if ( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                              sizeof(LIBI),
                              (VMHOB)TKLIBIOWNER,
                              0,
                              SSToDS(&ptmp) ) )
        {
            return (rc);
        }
        *(ptmp) = *(plibi);
        tkLibiInitStatic(ptmp);     /* Reset static block */

        // Save TCB_ObjNameBuf and length of buffer
        plibi->libi_objnamebuf  = pTCBCur->TCB_ObjNameBuf;
        plibi->libi_objnamebufL = pTCBCur->TCB_ObjNameBufL;
    }

    plibi->libi_handles[plibi->libi_tcnt] = hMTE;   // save MTE handle

    /*
     * We do not allocate libi_pregs till we actually come across a
     * module that needs initialization. Now it may happen that the
     * the 1st such module is not in the TOPBLOCK. If that is the case
     * then we need to propagate the libi_pregs pointer all the way up
     * up to the TOPBLOCK.
     */
    if (fDispReqd)
    {
        plibi->libi_status |= LIBI_HASINIT;  // set flag that dispatch is reqd
        if (plibi->libi_pregs == NULL)      // Need to allocate pregs ?
        {
            if ( ( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                                    sizeof(KMREG),
                                    (VMHOB)TKKMREGOWNER,
                                    0,
                                    &plibi->libi_pregs ) ) != NO_ERROR )
            {
                return (rc);
            }

            if (!IsTopBlock(plibi))
            {
                for ( ptmp = plibi;
                      ptmp->libi_pprev != NULL;
                      ptmp = ptmp->libi_pprev )
                {
                    ptmp->libi_pprev->libi_pregs = ptmp->libi_pregs;
                }
            }
        }
    }

    // increment count of recorded handles
    plibi->libi_rcnt = ++plibi->libi_tcnt;

    return ( NO_ERROR );
}


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


/***EP  TKLibiStartDispatch - Setup initial dispatch to LibInit dispatcher
 *
 *      TKLibiStartDispatch is called after a child process or new DLL has
 *      been successfully loaded, and prepared for dispatch.  If there is
 *      any LibInit required, the first LibInit block will have been set up
 *      as TKLibiCur.  TKLibiCur is copied and linked to the TCB.
 *
 *      If there is a init routine to run then the TCB is modified so that
 *      when we leave the kernel to run the thread, we return to LibiDispatcher
 *      instead of the thread's normal start (or return) address.
 *
 *      TKLibiStartDispatch may not be called twice for the same LibInit level.
 *      Once this routine has returned successfully, TKLibiError may not be
 *      called for this level.
 *
 *      If the LibInit block exists and debugging is enabled, the LIBLOAD Debug
 *      event is set up.
 *
 *      NOTE: Even though we are dispatching to a 16-bit address(LibiDispatcher)
 *            we don't linearize the stack if the caller is 32-bit code. This is
 *            because LibiDispatcher doesn't use the stack.
 *
 *      ENTRY   UserEAX - Value in EAX when DOSLOADMODULE returns
 *                        or when program starts if DOSEXECPGM
 *
 *      EXIT    int     - return code (NO_ERROR if successful)
 *
 *      CALLS   DbgDLLEvent, tkLibiGetDispatcherAddr, KMSetClientRegister
 *              KMQueryAllClientRegisters, KMQueryClientRegister,
 */

int TKLibiStartDispatch( ULONG UserEAX )
{
    register PTCB   pTCB;
    register PLIBI  plibi;
    register PLIBI  plancs;
    PLIBI           plibitmp;
    int             fDbg;
    int             rc;
    unsigned short  cnest;

    plibi = &TKLibiCur;

    if ( pPTDACur->pPTDAExecChild == NULL )
    {
        pTCB = pTCBCur;
        plibi->libi_status |= LIBI_LOADMODULE;
    }
    else
    {
        pTCB = pPTDACur->pPTDAExecChild->ptda_pTCB1;
    }

    fDbg = ( pTCB->TCBpPTDA->ptda_DbgEnabled != 0 );

    /*
     * If this is the first level load with no init routine to dispatch to
     * and the process is not debugging, then nothing to do, so cleanup
     * and exit.
     */
    if (    !HasInit( plibi )
         && !fDbg
         && pTCB->TCBpLibiHead == NULL )
    {
        tkLibiInitStatic(NULL);
        return(NO_ERROR);
    }

    if ( ( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                            sizeof(LIBI),
                            (VMHOB)TKLIBIOWNER,
                            0,
                            SSToDS(&plibitmp) ) ) != NO_ERROR )
    {
        return(rc);
    }

    *(plibitmp) = *(plibi);         /* Copy static block to heap block */
    plibi = plibitmp;               /* Point at copy */
    tkLibiInitStatic(NULL);         /* Reset static block */

    /*
     *  THIS IS THE POINT OF NO RETURN!  TKLibiStartDispatch
     *  may not return an error after this point!
     */

    if ( pTCB->TCBpLibiHead == NULL ) // Create head of chain if 1st load
        pTCB->TCBpLibiHead = plibi;

    /*
     * Link the ancestor and sibling pointers, if there is an ancestor.
     * Note that if there is no ancestor, then there are no siblings.
     */
    if ( (plancs = pTCB->TCBpLibiCurr) != NULL )
    {
        plibi->libi_pancs = plancs;
        /* Propagate plancs up through the pprev chain since
         * the loop in TKLibiInitNextDll checks if there are
         * no more handles in this block to dispatch and this
         * is not the TOPBLOCK, if so it goes to the pprev
         * block. Eventually it sets TCBpLibiCurr to plancs.
         */
        for ( plibitmp = plibi->libi_pprev;
              plibitmp != NULL;
              plibitmp = plibitmp->libi_pprev )
        {
            plibitmp->libi_pancs = plancs;
        }

        plibi->libi_psibl = plancs->libi_pdesc[plancs->libi_rcnt];
        plancs->libi_pdesc[plancs->libi_rcnt] = plibi;
    }

#ifdef INCDEBUG
    if (    (fDbg)
         && (!(pPTDACur->ptda_flDbg & DBGP_ALLDONE)) )
    {
        pTCB->TCBpLibiCurr = plibi;         // Insert at current level
        DbgDLLEvent(DBG_LOADDLL, pTCB);     // notify debug of a load
    }
#endif /* DEBUG */

    // Get dispatcher address, if first time through
    // This relies on the fact that DOSCALL1.DLL is the FIRST DLL loaded
    // by the system. If that ever changes, then this mechanism should too.

    // BUGBUG - there is NO guarantee that DOSCALL1 will be the first DLL
    //          loaded - a device driver can import a DLL - whatever DLL
    //          gets imported first will be the first DLL loaded.

    if (LibiDispatcher32 == NULL)   // Dispatcher address not initialized
        tkGetLibiDispatcherAddr();

    /*
     * Get all user registers and save it. This is restored after all the
     * init routines have comleted their execution. This is done only if
     * LIBI_HASINIT is set. Otherwise we return directly to the caller or
     * program start address whichever is valid.
     */
    if ( HasInit(plibi) )
    {

#ifdef LDRSTRICT
        if ( ldrDebugFlags & LDR_PRINT_DLM_SEM )
            printf( "tk SD has-init slot=%x\n", TaskNumber );
#endif

        if ( IsLoadMod(plibi) )
        {
            if ( KSEMQueryMutex( (HKSEM)&pPTDACur->ptda_DLMsem,
                                 SSToDS(&cnest) ) )
            {

#ifdef LDRSTRICT
                if ( ldrDebugFlags & LDR_PRINT_DLM_SEM )
                {
                    printf( "tk SD pre-inc slot=%x cnest=%x\n",
                            TaskNumber ,
                            cnest );
                }
#endif

                if (cnest)   // came in thru w_loadmodule
                {
                    // increment reference count - this sem call can not be
                    // allowed to fail ... see the comment up above, so
                    // loop for things like error interrupt
                    while( KSEMRequestMutex( (HKSEM)&pPTDACur->ptda_DLMsem,
                                             -1 ) );
                }
            }

#ifdef LDRSTRICT
            else  // bad ret code from KSEMRequestMutex
                panic("StartDispatch - bad QueryMutex");
#endif

        }

        pTCB->TCBpLibiCurr = plibi;         // Insert at current level

        KMQueryAllClientRegisters( pTCB,
                                   plibi->libi_pregs );
        KMQueryClientReturnESP( pTCB,
                                &plibi->libi_pregs->kmreg_esp );

        plibi->libi_pregs->kmreg_eax  = UserEAX;

        // put dispatcher address on current threads TCB

        KMSetClientRegister( pTCB,
                             KM_REG_CS ,
                             LibiDispatcher.Segmt );
        KMSetClientRegister( pTCB,
                             KM_REG_EIP,
                             LibiDispatcher.Offst );
    }
    else
    {

#ifdef LDRSTRICT
        if ( ldrDebugFlags & LDR_PRINT_DLM_SEM )
        {
            printf( "tk SD no-init slot=%x\n",
                    TaskNumber );
        }
#endif

        plibi->libi_rcnt = 0;
    }

    return(NO_ERROR);
}
#pragma code_seg()
#pragma code_seg("TKSWAP_TEXT", "CODE")


/***EP  TKLibiError - Free the level of libinit records being built
 *
 *      TKLibiError frees the current level of Libinit records that is
 *      being constructed as a result of a module load or an exec.
 *
 *      TKLibiError and TKLibiStartDispatch are mutually exclusive calls, and
 *      neither may be called twice, for a single LibInit level.
 *
 *      ENTRY           None
 *
 *      EXIT            None
 *
 *      CALLS           tkLibiFreeHeap
 *
 *      EFFECTS The chain of libinit records to which TKLibiCur points is freed
 */
void TKLibiError()
{
    tkLibiFreeHeap( &TKLibiCur,
                    FALSE );
}


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

/***EP  TKLibiInitNextDLL - Select next LibInit routine for LibInit Dispatch
 *
 *      This routine is the worker routine for the call gate DOSLIBINIT,
 *      which is called by the Library Initialization Routine Dispatcher
 *      (LibiDispatcher) to setup the user stack for dispatch to the next
 *      LibInit routine.  The stack is set up so that we return to the libinit
 *      routine, and a far-16 or a near-32 return from that routine transfers
 *      us back into LibiDispatcher.
 *
 *      TKLibiInitNextDLL selects the next module to initialize, sets up the
 *      user stack and the user registers (esi,edi,ds and possibly ss and esp).
 *      Listed modules which do not have an actual LibInit routine are skipped.
 *      libi_rcnt is decremented each time a module is selected or
 *      skipped, so it is also used to access the 'next' MTE handle.
 *
 *      If TKLibiInitNextDLL cannot select another module to run, the user
 *      registers from TCBpLibiCurr are copied back to kernel stack. The user
 *      return address is the caller to DOSLOADMODULE, or the start address
 *      in case of DOSEXECPGM.
 *
 *      We examine the CS of caller on entry. If the caller is not the Libinit
 *      Dispatcher, we exit immediately.
 *
 *      The dispatcher passes a flag in eax whether the previous libinit routine
 *      failed (0 failed, non-zero success). In case of failure we check if we
 *      are in the midst of a DOSLOADMODULE. In this case we return an error to
 *      the caller that a libinit routine errored. All modules loaded as a
 *      result of this DOSLOADMODULE are free'd. If we are in the midst of
 *      DOSEXECPGM, then we kill the process.
 *
 *      ENTRY   None
 *
 *      EXIT
 *              NO_ERROR        If dispatching to a 32-bit DLL init
 *                              In this case the user stack on entry to the
 *                              init routine looks as follows:
 *
 *                              (ESP+0) return address to the ring 3 dispatcher
 *                              (ESP+4) module handle of this library
 *                              (ESP+8) PDLL group handle, 0 if none
 *
 *              MTE handle      If dispatching to a 16-bit DLL init
 *                              In this case the user registers of interest are:
 *                              EDI = MTE handle
 *                              ESI = heap size
 *                              DS  = Auto DS
 *
 *              Saved EAX       If returning to caller of DOSLOADMODULE
 *                              or program startup.
 *                              (This value is saved by TKLibiStartDispatch)
 *
 *              ERROR_INIT_ROUTINE_FAILED
 *                              If it's a DOSLOADMODULE and an init routine
 *                              has failed.
 *
 *      CALLS   KMQueryClientRegister, KMSetAllClientRegisters,
 *              KMSetClientRegister, KMPushClientStack, tkLibiFreeModules,
 *              tkLibiGlobalDone, tkLibiCleanup, tkLibiFreeHeap, TKDieSoonPTDA,
 *              LDRGetTKModuleInfo, TKSleep
 *
 */

#define VMFSTACK  (VMAC_LOCANY | VMAC_ALIGNPAGE | VMAC_ARENAPRV | \
                   VMAC_SELALLOC | VMAC_OBAPI | PG_SWAPPABLE | \
                   PG_U | PG_R | PG_W)
#define SELFR3STACK     (SELAL_SELMAP | SELAL_W | D_DPL3)

int TKLibiInitNextDLL()
{
    register PLIBI  plibi;
    PLIBI           plibistart;
    PLIBI           plibitmp;
    register VMHOB  hMTE;
    register PTCB   pTCB = pTCBCur;
    ldrmi_t         libinfo;
    ldrmi_t         exeinfo;
    int             userCodeType;
    KMREG           usrregs;
    ULONG           rc;
    VMAC            ac;         // VMAllocMem() return info buffer
    unsigned short  cnest;      // for QueryMutex
    struct
    {                        // User stack on entry to a 32-bit DLL
        ULONG   s_retaddr;   // return address (LibiDispatcher32)
        ULONG   s_hmte;      // module handle
        ULONG   s_fterm;     // Termination flag (0)
    } usrstk;

#ifdef MISCSTRICT
    ULONG           callerCS;

    /*
     * We do a sanity check on the code that called DOSLIBINIT. Only the
     * libi dispatcher is authorised to call it. Right now the dispatcher
     * resides in a 16-bit segment. If it is moved to a 32-bit segment
     * this code will change.
     */

    KMQueryClientRegister( pTCB,
                           KM_REG_CS,
                           SSToDS(&callerCS) );
    if (callerCS != LibiDispatcher.Segmt)
        return( NO_ERROR );                // Do nothing - ok to return from
                                           // here as this was an invalid call.
#endif

    //84537 - Reset task's LibiFlags for DLL init routine failed condition
    pTCB->TCBLibiFlags &= ~INIT_ROUTINE_FAILED;

    // WARNING ///////////////////////////////////////////////////////////////
    //
    // WARNING - do NOT return from within this code - jump to the end of the
    // routine to retrieve any previous DLL error code.


    plibi = pTCB->TCBpLibiCurr;

#ifdef MISCSTRICT
    if (!HasInit(plibi))
        panic("InitNextDLL : No init routine");
#endif

    /*
     * If the previous init routine errored, begin error processing.
     * (rc == 0) ==>> error.
     */

    KMQueryClientRegister( pTCB,
                           KM_REG_EAX,
                           SSToDS(&rc) );

    if (rc == 0)
    {   // previous libinit routine error'd

        KMSetAllClientRegisters( pTCB,
                                 plibi->libi_pregs );

        if (IsLoadMod(plibi))
        {   // DOSLOADMODULE

            //84537 - Set task's LibiFlags to denote DLL init routine failed
            pTCB->TCBLibiFlags |= INIT_ROUTINE_FAILED;

            // release mutex on per process DosLoadmodule() semaphore
            if ( KSEMQueryMutex( (HKSEM)&pPTDACur->ptda_DLMsem,
                                 SSToDS(&cnest) ) )
            {

#ifdef LDRSTRICT
                if ( ldrDebugFlags & LDR_PRINT_DLM_SEM )
                    printf( "tk IN pre-dec slot=%x cnest=%x\n", TaskNumber , cnest );
#endif

                if (cnest)
                    KSEMReleaseMutex( (HKSEM)&pPTDACur->ptda_DLMsem );
            }

#ifdef LDRSTRICT
            else  // bad ret code from KSEMQueryMutex
                panic("InitNextDLL 1 - bad QueryMutex");
#endif

            // Free modules from this subtree
            tkLibiFreeModules( plibi,
                               TRUE,
                               FALSE );

            // Unlink this subtree from the tree
            if (plibi->libi_pancs != NULL)
                plibi->libi_pancs->libi_pdesc[plibi->libi_pancs->libi_rcnt]
                                                    = plibi->libi_psibl;
            pTCB->TCBpLibiCurr = plibi->libi_pancs;

            // Free the heap space allocated for this subtree
            tkLibiFreeHeap( plibi,
                            FALSE );

            // If this is the topmost level DOSLOADMODULE, mark it free
            if (plibi == pTCB->TCBpLibiHead)
                pTCB->TCBpLibiHead = NULL;

        }
        else
        {   // DOSEXECPGM

#ifdef LDRSTRICT
            if ( KSEMQueryMutex( (HKSEM)&pPTDACur->ptda_DLMsem,
                                 SSToDS(&cnest) ) )
                if (cnest)
                    panic("exec exit with DLMsem");
#endif

            tkLibiCleanup( pTCB );                    // Do cleanup
            TKDieSoonPTDA( pPTDACur,
                           ERROR_INIT_ROUTINE_FAILED,
                           TC_NORMAL,
                           0 );
        }

        rc = ERROR_INIT_ROUTINE_FAILED;
        goto LibiInit_exit;
    }

    userCodeType = ( plibi->libi_pregs->kmreg_cs == R3CS )
                   ? LDR_32bit
                   : LDR_16bit;

    plibistart = plibi;
    while (TRUE)
    {

        if (plibi->libi_rcnt == 0)
        {
            if (IsTopBlock(plibi))
            {
                // All done get ready to return to DosLoadModule caller

#ifdef LDRSTRICT
                if ( plibi->libi_pprev )
                    panic ("InitNextDLL: not finished");
#endif

                // release mutex on per process DosLoadmodule() semaphore
                if ( KSEMQueryMutex( (HKSEM)&pPTDACur->ptda_DLMsem,
                                     SSToDS(&cnest) ) )
                {

#ifdef LDRSTRICT
                    if ( ldrDebugFlags & LDR_PRINT_DLM_SEM )
                        printf( "tk LIn slot=%x cnest=%x\n", TaskNumber, cnest );
#endif
                    if (cnest)
                        KSEMReleaseMutex( (HKSEM)&pPTDACur->ptda_DLMsem );
                }

#ifdef LDRSTRICT
                else
                    panic("InitNextDLL 2 - bad QueryMutex");
#endif

                // restore user registers from TCBpLibiCurr and return.
                KMSetAllClientRegisters(pTCB, plibi->libi_pregs);

                rc = plibi->libi_pregs->kmreg_eax;

                if (plibistart == pTCB->TCBpLibiHead)
                {
                    tkLibiFreeModules( plibistart,
                                       FALSE,
                                       FALSE );
                    tkLibiCleanup(pTCB);
                }
                else
                    pTCB->TCBpLibiCurr = plibi->libi_pancs;

                goto LibiInit_exit;
            }
            else
            {
                plibi = plibi->libi_pprev;
#ifdef LDRSTRICT
                if (!plibi)
                    panic ("InitNextDLL: Libi list");
#endif
                continue;
            }

        }  // end of if libi_rcnt == 0

        else
        {
            // more handles to process in this libi init block

            // handle the case of a zero handle
            plibi->libi_rcnt--;
            hMTE = plibi->libi_handles[plibi->libi_rcnt];
            if (!hMTE)
            {
#ifdef LDRSTRICT
                panic("tkInitNextDLL - null hMTE");
#endif
                continue;
            }

            *(PKMREG)SSToDS(&usrregs) = *plibi->libi_pregs;

            /* p025236
             * If we are doing libinit for a 16 bit exe check to see
             * if it has a stack of 4k or greater. If not allocate a
             * temporary stack for libinit.
             * This is done because in OS/2 v1.x we did not force DOSCALL1
             * to be attached to processes so some 16 bit exe's have
             * stacks which are too small to run libinit.
             */

            /* Find the top most block pointed to by TCBpLibiHead */
            plibitmp = pTCB->TCBpLibiHead;
            if ( !IsTopBlock(plibitmp) )
            {
                for ( ;
                      plibitmp->libi_pprev != NULL;
                      plibitmp = plibitmp->libi_pprev );
            }
            tkAssert( IsTopBlock(plibitmp) );

            if( !StackChecked(plibitmp) )
            {
                if ( !IsLoadMod(plibitmp) )
                {
                    rc = LDRGetTKModuleInfo( pPTDACur->ptda_module,
                                             SSToDS(&exeinfo) );
                    tkAssert( !rc );
                    if (!rc)
                    {
                        if (exeinfo.ldrmi_ldtype == LDR_16bit)
                        {
                            if ((usrregs.kmreg_esp &= 0xFFFF) < (_4K-2))
                            {
                                rc = VMAllocMem( _4K,                   /* Virtual size */
                                                 _4K,                   /* Commit size */
                                                 VMFSTACK,              /* VM flags */
                                                 pPTDACur->ptda_handle, /* Context */
//86186                                          (USHORT)TKR3STACKOWNER,/* Owner */
                                                 HOBNULL,               /* 86186 - Owner */
                                                 (USHORT)TKR3STACKOWNER,/* MTE handle */
                                                 SELFR3STACK,           /* Selector Mgr flgs */
                                                 0,                     /* No block number */
                                                 SSToDS(&ac));          /* Pointer to ret buf*/
                                tkAssert( !rc );
                                if ( !rc )
                                {
                                    usrregs.kmreg_ss = ac.ac_sel;
                                    usrregs.kmreg_esp = _4K - 2; /* word aligned esp */
                                    KMSetAllClientRegisters( pTCB,
                                                             SSToDS(&usrregs) );
                                    plibitmp->libi_stackSel = ac.ac_sel;
                                    plibitmp->libi_status |= LIBI_STK_ALLOC;
                                }
                            }
                        }
                    }
                }
                plibitmp->libi_status |= LIBI_STK_CHK;
            }

            rc = LDRGetTKModuleInfo( hMTE, SSToDS(&libinfo) );
            if (rc==ERROR_INIT_ROUTINE_FAILED)
            {
                rc = NO_ERROR;
                continue;
            }
            else if (rc)
                panic("TKLibiInitNextDLL: ModuleInfo 1");

            if ( !(libinfo.ldrmi_attrib & LDRLIBINIT) )
            {
                // If not per instance initialization
                if (libinfo.ldrmi_attrib & LDRGLOBALSETUP)
                {
                    if (libinfo.ldrmi_attrib & LDRGLOBALDONE)
                            continue;       // Global init complete

                    /*
                     * If global Setup done elsewhere
                     * wait till it completes
                     * Block, no timeout, uninterruptible
                     */
                    do
                    {
                         TKSleep( (void *)( (hMTE << 16) + hMTE), -1, 1, 0 );

                         if ( LDRGetTKModuleInfo( hMTE, SSToDS(&libinfo) ) )
                              panic("TKLibiInitNextDLL: ModuleInfo 2");

                         if (!(libinfo.ldrmi_attrib & LDRGLOBALSETUP))
                             break;

                    } while (!(libinfo.ldrmi_attrib & LDRGLOBALDONE));

                    if ((libinfo.ldrmi_attrib & LDRGLOBALSETUP))
                        continue;
                }
                LDRSetTKGlobalSetup(hMTE); // Global Setup done
            }

            plibi->libi_status |= LIBI_DISPATCHED;

            if (libinfo.ldrmi_ldtype == LDR_16bit)
            {   // 16 bit DLL

                if (userCodeType == LDR_32bit)
                {   // 32 bit caller

                    rc = usrregs.kmreg_esp;                 // Save ESP
                    usrregs.kmreg_ss = LaToSelTiled(usrregs.kmreg_esp)|RPL_RING3;
                    usrregs.kmreg_esp = usrregs.kmreg_esp & 0xFFFF;

                    if (usrregs.kmreg_esp < _4K)
                    {
                        /* If, after tiling, the stack space in this segment
                         * is < 4K, then possibly bump the stack down to the
                         * next selector. This decision is based on:
                         * a, The stack object spans the next lower selector
                         * b, After bumping down the stack, the space in
                         *    that segment is greater than in this segment
                         * NOTE: Since every object is PAGE ALIGNED we know
                         *       for sure that (b) is TRUE i.e. there will
                         *       be atleast 4K of space in the next lower
                         *       selector, if it exists.
                         */
                        if ( LDRGetTKModuleInfo( pPTDACur->ptda_module,
                                                 SSToDS(&exeinfo) ) )
                            panic("TKLibiInitNextDLL: ModuleInfo 3");

                        // rc has the orignal ESP
                        if ((rc & 0xFFFF0000) > exeinfo.ldrmi_stackbase)
                        {
                            usrregs.kmreg_ss -= SEL_INCR;
                            usrregs.kmreg_esp = 0xFFFE;
                        }
                    }
                }

                /*
                 * If this is a porthole app let the porthole init
                 * routine get control before the real init routine
                 */
                if (libinfo.ldrmi_phflag == LDR_PORTHOLE)
                {
                    usrregs.kmreg_cs = libinfo.ldrmi_phaddr.ptr_sel;
                    usrregs.kmreg_eip = (ULONG)libinfo.ldrmi_phaddr.ptr_off;
                }

                /*
                 * Else let the true init routine get control first
                 */
                else
                {
                    usrregs.kmreg_cs = libinfo.ldrmi_startaddr.ptr_sel;
                    usrregs.kmreg_eip = (ULONG)libinfo.ldrmi_startaddr.ptr_off;
                }

                usrregs.kmreg_esi = (ULONG) libinfo.ldrmi_heap;
                usrregs.kmreg_edi = (ULONG) hMTE;

                if (libinfo.ldrmi_autods != 0)
                    usrregs.kmreg_ds = libinfo.ldrmi_autods;

                KMSetAllClientRegisters( pTCB,
                                         SSToDS(&usrregs) );
                KMPushClientStack( pTCB,
                                   sizeof(ULONG),
                                   &LibiDispatcher16,
                                   TK_UM_FATAL );

                /*
                 * If this is a porthole return to the true init routine
                 * after the porthole init routine runs.
                 */
                if (libinfo.ldrmi_phflag == LDR_PORTHOLE)
                {
                    KMPushClientStack( pTCB,
                                       sizeof(ULONG),
                                       SSToDS(&libinfo.ldrmi_startaddr),
                                       TK_UM_FATAL );
                }

                LDRSetTKGlobalDone(hMTE);
                TKWakeup((void *)((hMTE << 16) + hMTE), 0, NULL);

                // Note - returns, instead of jump to exit. This is because
                // 16-bit init calls have the module handle in ax, not an
                // error code. Also, 16-bit DLLs do not have term routines.

                return ((int) hMTE);
            }
            else
            {                                          // 32 bit DLL
                if (userCodeType == LDR_16bit)
                {    // 16 bit caller
                    usrregs.kmreg_esp = SelToLaTiled(usrregs.kmreg_ss) +
                                            (USHORT) usrregs.kmreg_esp;
                    usrregs.kmreg_ds  = usrregs.kmreg_es
                                      = usrregs.kmreg_ss
                                      = R3DS;
                }

                if (ldrpMTEFromHandle(hMTE)->mte_flags1 & MTEMODPROT)
                {
                    usrregs.kmreg_ds  =
                    usrregs.kmreg_es  = R3PDS;
                }

                usrregs.kmreg_cs  = R3CS;
                usrregs.kmreg_eip = libinfo.ldrmi_startaddr.ptr_flat;
                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;

                KMSetAllClientRegisters(pTCB, SSToDS(&usrregs));
                usrstk.s_retaddr = LibiDispatcher32;
                usrstk.s_hmte = hMTE;
                usrstk.s_fterm = 0;

                KMPushClientStack( pTCB,
                                   sizeof(usrstk),
                                   SSToDS(&usrstk),
                                   TK_UM_FATAL);

                LDRSetTKGlobalDone(hMTE);
                TKWakeup((void *)((hMTE << 16) + hMTE), 0, NULL);

                rc = NO_ERROR;
                goto LibiInit_exit;
            }
        }

    } /* end: while TRUE loop */



LibiInit_exit:

    return( rc );



} /* end: TKLibiInitNextDLL() */

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


/***LP  tkLibiFreeModules - Free Modules if there are any waiting to be freed
 *
 *      Called by TKLibiInitNextDLL either when a libinit routine errors
 *      or when a IsLoadMod has just completed at the outermost level.
 *      All modules at level below and including the one specified are freed
 *      up if either fErr is TRUE or if the block is marked LIBI_FREEMODULE.
 *
 *      ENTRY   plibi   Pointer to the LIBI block which errored.
 *              fErr    if TRUE, then its an error path
 *              fSibl   if TRUE, look at siblings too
 *
 *      EXIT    None
 *
 *      CALLS   LDRFreeModule and self (recursive)
 */
void    tkLibiFreeModules(plibi, fErr, fSibl)
register PLIBI  plibi;
int             fErr;
int             fSibl;
{
    register PLIBI  plibic;         // Current libinit block being processed
    register int    i;

    plibic = plibi;
    do
    {
        for (plibi = plibic; plibi != NULL; plibi = plibi->libi_pprev)
        {
            for (i = plibi->libi_tcnt; i > plibi->libi_rcnt; i--)
                if (plibi->libi_pdesc[i-1] != NULL)
                    tkLibiFreeModules(plibi->libi_pdesc[i-1], fErr, TRUE);

            while (!IsTopBlock(plibi))
                plibi = plibi->libi_pprev;

            // Note we might call ldr here to free a null handle - this will
            // give back a bad return code.
            if (fErr || IsFreeMod(plibi))
                LDRFreeModule(plibi->libi_handles[0], TRUE);
        }
        plibic = plibic->libi_psibl;

    } while (fSibl && plibic != NULL);
}

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

/***EP  TKLibiCheckRef - Check if the MTE handle is recorded by any thread.
 *
 *      This checks ALL threads of a process to make sure the MTE being
 *      free'd (DOSFREEMODULE) is not currently recorded for libinit. If
 *      it is recorded, then an error is returned which inhibits freeing
 *      of the module. There are two possible cases if it is recorded:
 *      a, The MTE has finished its init routine.
 *              In this case we set LIBI_FREEMODULE in libi_status and return
 *              ERROR_DELAY_FREE. LDRFreeModule returns NO_ERROR but doesn't
 *              actually free it. After libinit finishes, the module is actually
 *              freed
 *      b, The MTE has yet to do its init routine.
 *              In this case we return ERROR_MODULE_IN_USE. This is passed
 *              back to the caller of LDRFreeModule.
 *
 *      NOTE:   The only module of interest here is the first at a level because
 *              it is the one being loaded. The others are there because of
 *              implicit references (attachments).
 *
 *      ENTRY   hMTE - handle of the MTE being freed.
 *
 *      EXIT    NO_ERROR                if the handle is not recorded.
 *              ERROR_MODULE_IN_USE     if the handle is recorded and not to be
 *                                      freed at all - passed back to caller of
 *                                      LDRFreeModule.
 *              ERROR_DELAY_FREE        if the handle is recorded but free'ing
 *                                      is to be delayed. In this case the API
 *                                      will return NO_ERROR, but module freeing
 *                                      will be delayed till libinit for this
 *                                      level is completed.
 *
 *      CALLS   tkLibiCheckReference
 */
int TKLibiCheckRef( VMHOB hMTE )
{
    register PTCB pTCB;
    int           rc;

    // Look at all the threads
    for ( pTCB=pPTDACur->ptda_pTCB1;
          pTCB!=NULL;
          pTCB=pTCB->TCBpTCBNext )
    {
        if ( rc = tkLibiCheckReference( hMTE,
                                        pTCB->TCBpLibiHead ) )
            return (rc);
    }
    return( NO_ERROR );
}



/***LP  tkLibiCheckReference - Check if module recorded at this level or lower
 *
 *      Check if the specified module is recorded in this libinit block or
 *      its descendants.
 *
 *      ENTRY   hMTE    Module handle of interest
 *              plibi   Libinit block of interest
 *
 *      EXIT    NO_ERROR                module not recorded
 *              ERROR_MODULE_IN_USE     module is recorded, not yet dispatched
 *              ERROR_DELAY_FREE        module is not recorded, dispatched
 *
 *      CALLS   self (recursive)
 */
int tkLibiCheckReference( VMHOB          hMTE,
                          register PLIBI plibi )
{
    register PLIBI  plibic;         // Current libinit block being processed
    register int    i;
    int             rc;

    // Look at all the siblings of this libinit block
    for ( plibic = plibi; plibic != NULL; plibic = plibic->libi_psibl )
    {
        // Look at all the sub-blocks within this logical block
        for ( plibi = plibic; plibi != NULL; plibi = plibi->libi_pprev )
        {
            if (    IsTopBlock(plibi)
                 && ( plibi->libi_handles[0] == hMTE ) )
            {
                if (plibi->libi_rcnt > 0)
                    return( ERROR_MODULE_IN_USE );
                else
                {
                    plibi->libi_status |= LIBI_FREEMODULE;
                    return( ERROR_DELAY_FREE );
                }
            }
            for ( i = 0; i < plibi->libi_tcnt; i++ )
                if ( rc = tkLibiCheckReference( hMTE,
                                                plibi->libi_pdesc[i] ) )
                    return( rc );
        }
    }
    return( NO_ERROR );
}


/***EP  TKLibiSetupUnload - allocate LIBF block for this thread
 *
 *      Called by the loader when a set of modules is being detached from
 *      the process.  If DosDebug is enabled, then a block with the specified
 *      number of entries is allocated for DosDebug notification.
 *
 *      ENTRY   cmte            - count of handles to record
 *
 *      EXIT    int     NO_ERROR, if successful
 *                      VMAllocKHB error
 *
 *      CALLS   VMAllocKHB
 */

int TKLibiSetupUnload( ULONG cmte )
{
    PLIBF plibf;
    int rc;

    if (pPTDACur->ptda_DbgEnabled)
    {
        if ( ( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                                sizeof(LIBF) + cmte*sizeof(VMHOB) - sizeof(VMHOB),
                                (VMHOB)TKLIBIFREEOWNER,
                                0,
                                SSToDS(&plibf) ) ) != NO_ERROR )
            return( rc );
        plibf->libf_count = 0;
        plibf->libf_pprev = pTCBCur->TCBpLibiFree;
        pTCBCur->TCBpLibiFree = plibf;
    }
    return( NO_ERROR );
}


/***EP  TKLibiRecordUnload - Record a module being detached
 *
 *      Called by the loader when a module is being detached from the process
 *      If DosDebug is enabled for the current process, then the module handle
 *      is stored away for DosDebug notification.
 *
 *      ENTRY   hMTE    Handle to module being detached.
 *
 *      EXIT    NONE
 */

void TKLibiRecordUnload( VMHOB hMTE )
{
    register PLIBF  plibf;

    if ((plibf = pTCBCur->TCBpLibiFree) != NULL)
    {
        tkAssert(pPTDACur->ptda_DbgEnabled);
        plibf->libf_handles[plibf->libf_count++] = hMTE;
    }
}



/***EP  TKLibiUnloadDone - Notify DosDebug of all the modules being detached
 *
 *      Called by loader after all the modules being detached have been
 *      detached. DosDebug is notified if enabled for this process.
 *      Note that if there are static links to the modules being freed
 *      as well as runtime links, then a runtime free (DosFreeModule)
 *      will never actually cause any notifications.
 *
 *      ENTRY   None
 *
 *      EXIT    None
 */
void    TKLibiUnloadDone()
{

#ifdef INCDEBUG
    if (pTCBCur->TCBpLibiFree != NULL)
    {
        tkAssert(pPTDACur->ptda_DbgEnabled);
        DbgDLLEvent(DBG_FREEDLL, pTCBCur);
    }
#endif /* DEBUG*/

}



/***EP  TKLibiDbgNextDLL - Pass the next MTE handle to Debug
 *
 *      This routine passes the next MTE handle at the current level
 *      to the caller.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              NO_ERROR                MTE handle returned, in *phMTE.
 *              ERROR_NO_MORE_HANDLES   if no more MTE handles.
 *
 *      CALLS   NONE
 */
int     TKLibiDbgNextDLL(fEvent, phMTE)
int     fEvent;
VMHOB   *phMTE;
{

#ifdef INCDEBUG
        if (fEvent == DBG_FREEDLL) {
            register PLIBF plibf;

            plibf = pTCBCur->TCBpLibiFree;
            if (plibf->libf_count != 0) {
                *phMTE = plibf->libf_handles[--plibf->libf_count];
                return (NO_ERROR);
            }
            return (ERROR_NO_MORE_HANDLES);     /* done for this level */
        }
        else {          // fEvent == DBG_LOADDLL
            register PLIBI plibi;

            plibi = (pPTDACur->pPTDAExecChild == NULL) ?
                        pTCBCur->TCBpLibiCurr :
                        pPTDACur->pPTDAExecChild->ptda_pTCB1->TCBpLibiCurr;

            for (; plibi != NULL; plibi = plibi->libi_pprev) {
                if (plibi->libi_tcnt > plibi->libi_dbgcnt) {
                    *phMTE = plibi->libi_handles[plibi->libi_tcnt -
                                                        ++plibi->libi_dbgcnt];
                    return (NO_ERROR);
                }
            }
        }
        return (ERROR_NO_MORE_HANDLES);         // done for this level

#else
        return(NO_ERROR);
#endif  /* RAS */
}


/***EP  TKLibiDbgDone - Report Debug notification completion to LibInit
 *
 *      TKLibiDbgDone must be called after Debug has completed working on the
 *      LibInit notification.  The LibInit block MUST exist at this point or
 *      an internal error will result.  If this happens, it means that
 *      TKLibiStartDispatch lied to Debug (DbgLibLoadEvent).
 *
 *      TKLibiDbgDone uses tkLibiCleanup only if there is no LibInit
 *      routine to run.
 *
 *      Libinit is required to notify the debugger of the MTE handles
 *      of all newly attached libraries BEFORE ANY of the LibInit
 *      routines run.  This gives the debugger an opportunity to load
 *      symbols and set breakpoints in the library to debug LibInit
 *      routines.  The LibInit block may exist even though none of the
 *      MTEs listed have LibInit routines.  It would cause a GP fault
 *      if a ring 2 DOSLOADMODULE loaded a ring 2 library (which is OK),
 *      and the dispatcher (ring 3) is selected to run.  Hence, we delay
 *      the call to TKLibiStartDispatch until the end of the load, when the
 *      LIBI_HASINIT flag is used.  TKLibiStartDispatch does not set up
 *      LibiDispatcher if LIBI_HASINIT is not set.
 *
 *      Normally, TKLibiInitNextDLL frees the LibInit blocks, but if
 *      LibiDispatcher does not run, neither will TKLibiInitNextDLL, so the
 *      LibInit blocks would not be freed. We need to free up this level only
 *      if its the top most level. If its not, then it needs to stay around
 *      till the top most level completes.
 *
 *      ENTRY   None
 *
 *      EXIT    None
 *              The libi block is freed if there are no libinit routines to run
 *              and it is the top most level.
 *
 *      CALLS   tkLibiCleanup
 */

void    TKLibiDbgDone(fEvent)
int     fEvent;
{

#ifdef INCDEBUG
        if (fEvent == DBG_LOADDLL) {
            register PLIBI plibi;

            if (plibi = pTCBCur->TCBpLibiCurr) //94251 - Check if TCBpLibiCurr
            {                                  //94251   has already been cleaned
               //94251 tkAssert(plibi != NULL);          up by tkLibiCleanup
               if (!HasInit(plibi)) {
                   if (plibi == pTCBCur->TCBpLibiHead)
                        tkLibiCleanup(pTCBCur);
                   else pTCBCur->TCBpLibiCurr = plibi->libi_pancs;
               }
            }
        }
        else {          // fEvent == DBG_FREEDLL
            register PLIBF plibf;

            plibf = pTCBCur->TCBpLibiFree;
            tkAssert(plibf != NULL);
            pTCBCur->TCBpLibiFree = plibf->libf_pprev;
            VMFreeKHB(VM_HKH_PUB_SWAPRW,plibf);
        }

#endif /* DEBUG*/

}


/***ED+ pafnDoscall1 - List of routine addresses to be initialized
 *                     from doscall1.dll
 */

#define NUMENTRIES      9
struct {
        PVOID   pfnAddr;                // Pointer to where the address is saved
        USHORT  usord;                  // Ordinal number within doscall1
        USHORT  ustype;                 // Type, FLAT if TRUE
} pafnDoscall1[] = {
    {   &LibiDispatcher,        ORD_DOSLIBIDISP,                FALSE   },
    {   &LibiDispatcher16,      ORD_DOSLIBIDISP16,              FALSE   },
    {   &LibiDispatcher32,      ORD_DOSLIBIDISP32,              TRUE    },
    {   &pfnXcptDispatcher32,   ORD_DOS32R3EXCEPTIONDISPATCHER, TRUE    },
    {   &pfnXcptFRestore32,     ORD_DOS32R3FRESTOR,             TRUE    },
    {   &pfntkDosExit,          ORD_DOSR3EXITADDR,              FALSE   },
    {   &pfntkDos32Exit,        ORD_DOS32R3EXITADDR,            TRUE    },
    {   &pfnSigDispatch,        ORD_DOSISIGDISPATCH,            FALSE   },
    {   &pfnDLLTermDisp32,      ORD_DOS32DLLTERMDISP,           TRUE    }
};



 /***LP tkGetLibiDispatcherAddr - Initialize pointers to the dispatchers
 *
 *      Called by LibiSetup to initialize address of routines referenced by
 *      the kernel which reside in doscall1.dll
 *
 *      Entry   None
 *
 *      Exit    None    if ALL dispatcher addresses initialised
 *              panic   if any error encountered
 */
void    tkGetLibiDispatcherAddr()
{
        register int    i;

        for (i = 0; i < NUMENTRIES; i++) {
            if  (LDRGetProcAddr(hMteDoscall1,
                                (int) pafnDoscall1[i].usord,
                                NULL,
                                (ptr_t *)pafnDoscall1[i].pfnAddr,
                                pafnDoscall1[i].ustype,
                                NULL) == NO_ERROR) {

                /*
                 * If 16-bit address, stamp as ring 3
                 */

                if (pafnDoscall1[i].ustype == FALSE)
                   ((struct FarPtr *)pafnDoscall1[i].pfnAddr)->Segmt |= RPL_RING3;
            }   else
                panic("tkLibiGetDispatcherAddr: LDRGetProcAddr");

        }
}
#pragma code_seg()
