/*static char *SCCSID = "@(#)dynapi.c   6.6 91/10/17";*/
#define SCCSID  "@(#)dynapi.c   6.6 91/10/17"
/*      This file implements the tracepoint insertion, removal,
 *      clearing, and detaching features of DosDynamicTrace.
 *
 *      The DynTrc external interfaces to other components are
 *      contained mainly in this file.
 *
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 */

#define INCL_TYPES                      // for basedef.h typedefs
#define INCL_DOSERRORS                  // for bseerr.h
#define INCL_OS2STD                     // 7-29 NR not sure if needed so put in

#include <basemac.h>
#include <basedef.h>
#define FNO_NUM FNO_DYNAPI_C
#include <fnotask.h>
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <infoseg.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <mi.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 <vmowner.h>
#include <dynxport.h>
#include <dynapi.h>
#include <dynlocal.h>
#include <mmph.h>

#if RAS
//                      #include <pglocal.h>
extern ulong_t *pgPageToPte(ulong_t page, ptda_t *pptda);

//      Lock Region Boundaries

extern  char EndDYNLOCK_TEXT;           // Segment Labels
extern  char StartDYNLOCK_TEXT;         // Segment Labels


/***ED  pbDYNResetPoint - Pointer to DynTrc instrucion reset location
 ***ED  DYNNullResetPoint - Normal target for pbDynResetPoint
 *
 *      pbDYNResetPoint is an always-valid flat pointer which
 *      points to the place to store an INT_3 on a fault.
 *
 *      If pbDYNResetPoint points to DYNNullResetPoint, then
 *      a DynamicTrace single step is not in progress.
 *
 *      DYNNullResetPoint is simply a garbage location which is
 *      the normal target of the pbDYNResetPoint pointer.
 *
 *      If pbDYNResetPoint does not point to DYNNullResetPoint, then
 *      a DynamicTrace single step is expected, and NO DISPATCHING
 *      MAY OCCUR until pbDynResetPoint is restored.
 *
 *      This strange design makes all trap handlers a few clocks faster.
 *      As pbDYNResetPoint normally points to DYNNullResetPoint, it
 *      is always safe to set the INT 3 in memory.
 */

uchar_t DYNNullResetPoint = MI_INT3;
uchar_t *pbDYNResetPoint = &DYNNullResetPoint;


/***LD  dynsemTrap - DYNTrap3 semaphore
 *
 *      dynsemTrap protects the kernel debugger from blowing up on
 *      breakpoints in DYNTrap3.  If -1, a tracepoint can be accepted.
 *      If not, it is treated as a breakpoint.
 */

char            dynsemTrap = -1;


/***LD  dynselDTB - DTB selector
 *
 *      dynselDTB is a GDT selector to the Dynamic Trace Buffer area.
 *      Its limit is adjusted based on how much data may be logged.
 *      This allows us to log data in the interpreter without constantly
 *      checking if the log is full, handling this as a rare error case.
 *      Zero means that the DTB is not allocated.
 */

SEL     dynselDTB = 0;


/***LD  pdynTraceAlias - DynTrc alias region pointer
 ***LD  pptedynTraceAlias - DynTrc alias pte pointer
 *
 *      pdynTraceAlias is a pointer to the linear start of a UVIRT
 *      page which is used to alias to the tracepoint's code.
 *
 *      pptedynTraceAlias is a pointer to the pte for this page, and
 *      is used to store the aliasing information quickly.
 */

ulong_t pbdynTraceAlias = NULL;
ulong_t *pptedynTraceAlias = NULL;


/***LD  pdynTraceIntAlias - DynTrc alias region pointer for interrupt time
 ***LD  pptedynTraceIntAlias - DynTrc alias pte pointer for interrupt time
 *
 *      pdynTraceIntAlias is a pointer to the linear start of a UVIRT
 *      page which is used to alias to the tracepoint's code to remove
 *      an interrupt time tracepoint.
 *
 *      pptedynTraceIntAlias is a pointer to the pte for this page, and
 *      is used to store the aliasing information quickly.
 */

ulong_t pbdynTraceIntAlias = NULL;
ulong_t *pptedynTraceIntAlias = NULL;


/***ED  DYNSavedUserFlags - Trap 3 Flags image
 *
 *      DYNSavedUserFlags is used to save the original flags image
 *      in trap03 so that trap01 can determine if the F_TRACE flag was
 *      set when the tracepoint was hit.
 *
 */

ulong_t DYNSavedUserFlags = 0;


/***ED  DynamicTrace Resident Global Data
 *
 *      fDYNActive      - Nonzero only if some DTO exists.  (aka. pdtoHeadAll)
 *
 *      pdtoHeadAll     - Head of list of all DTOs (overlayed with fDYNActive)
 *      pdtoHeadScan    - Head of list of active DTOs (scanned by DYNTrap3)
 *
 *      pdtoHeadAll is linked via dti_pdtoNext.
 *      pdtoHeadScan is linked via dti_pdtoScan.
 *
 *      cdtoDetached    - Count of DTO_DETACHED DTOs. (pdtoHeadAll subset)
 *
 *      cdtoLoading     - Count of DTO_LOADING DTOs.  (pdtoHeadAll subset)
 *
 *      dynlh           - DynTrc Interpreter lock handle
 *
 *      dynDTB          - DTB data for dynInterpreter.
 *
 */

void   *fDYNActive      = NULL;         // DynTrc not acive by default.
//      pdtoHeadAll     = NULL;         // No DTOs (aka. fDYNActive)
dto_t  *pdtoHeadScan    = NULL;         // All Active DTOs (with tracepoints)
ulong_t cdtoDetached    = 0;            // Count of detached DTOs
ulong_t cdtoLoading     = 0;            // Count of DTOs being loaded
struct  vmlh_s dynlh;                   // DynTrc Interpreter lock handle
dtb_t   dynDTB;                         // Interpreter data

/***ED  DynamicTrace Swappable Global Data
 *
 *      fdynCodeLocked  - Nonzero if DynTrc Interpreter code is locked.
 *
 */

#pragma data_seg ("CSWAP_DATA", "CSWAPDATA")

ulong_t fdynCodeLocked;                 // Interpreter code lock flag.

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

/***EP  VR32DYNAMICTRACE - Dos32DynamicTrace API entry point
 *
 *      VR32DYNAMICTRACE is the verification routine for the
 *      Dos32DynamicTrace entry point.
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY
 *              lCmd = DT_INSERT = Insert a module's trace points
 *                      preqUser points at a dth_s request packet.
 *                      presUser not used.
 *
 *              lCmd = DT_REMOVE = Remove a module's trace points
 *                      preqUser points at a module handle (ushort_t)
 *                      presUser not used.
 *
 *              lCmd = DT_CLEARALL = Clear all trace points
 *                      preqUser not used.
 *                      presUser not used.
 *
 *              lCmd = DT_GETVAR = Get and/or zero tracepoint object variables
 *                      preqUser points at a request packet consisting of
 *                              a dtgh_s followed by 1 or more dtgp_s.
 *                      presUser points to a dtgp_s, followed by 1 or more
 *                              DWORD dynamic trace variables, followed by
 *                              another dtgp_s, followed by 1 or more DWORD
 *                              variables, and so on.
 *
 *              lCmd = DT_QUERY = Get and/or zero tracepoint object variables
 *                      preqUser points at a request packet consisting of
 *                              a dtgh_s followed by 1 or more dtgp_s.
 *                      presUser points to a dtgp_s, followed by 1 or more
 *                              DWORD dynamic trace variables, followed by
 *                              another dtgp_s, followed by 1 or more DWORD
 *                              variables, and so on.
 *
 *      EXIT    ulong_t, standard error code
 *
 */

#endif  // RAS
ulong_t cdecl VR32DYNAMICTRACE (sef, lCmd, preqUser, presUser)
THUNKSEF sef;
ulong_t lCmd;
char    *preqUser;
char    *presUser;
{
        TKMayBlock();

#if     RAS
        return (DYNDynamicTrace (lCmd, preqUser, presUser));
#else
        return (ERROR_SYSTEM_TRACE);
#endif  // RAS
}
#if     RAS


/***    DYNDynamicTrace - Perform Dynamic Trace control functions.
 *
 *      Both 16-bit and 32-bit entry points wind up here.
 *
 *      DYNDynamicTrace (lCmd, preq, pres);
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY
 *              lCmd = DT_INSERT = Insert a module's trace points
 *                      preqUser points at a dth_s request packet.
 *                      presUser not used.
 *
 *              lCmd = DT_REMOVE = Remove a module's trace points
 *                      preqUser points at a module handle (ushort_t)
 *                      presUser not used.
 *
 *              lCmd = DT_CLEARALL = Clear all trace points
 *                      preqUser not used.
 *                      presUser not used.
 *
 *              lCmd = DT_GETVAR = Get and/or zero tracepoint object variables
 *                      preqUser points at a request packet consisting of
 *                              a dtgh_s followed by 1 or more dtgp_s.
 *                      presUser points to a dtgp_s, followed by 1 or more
 *                              DWORD dynamic trace variables, followed by
 *                              another dtgp_s, followed by 1 or more DWORD
 *                              variables, and so on.
 *
 *              lCmd = DT_QUERY = Query regarding tracepoint objects
 *                      preqUser points at a request packet consisting of
 *                              a dtqi_s.
 *                      presUser points to a dtqo_s, followed by a string
 *                              containing the associated module pathname,
 *                              followed by another dtqo_s, followed by
 *                              another pathname, and so on.
 *
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNDynamicTrace (lCmd, preqUser, presUser)
ulong_t lCmd;
char    *preqUser;
char    *presUser;
{


        MMPHPreDynamicTrace()           /* Performance Hook (1C/09) */

        TKMayBlock();

        if (RASselSTDA == 0) {

                MMPHPostDynamicTrace()   /* Performance Hook (1C/89) */
                return (ERROR_SYSTEM_TRACE);
        }


        MMPHPostDynamicTrace()          /* Performance Hook (1C/89) */
        switch  (lCmd) {
                case    DT_INSERT:
                        return (dynAPIInsert ((dth_t*) preqUser));

                case    DT_REMOVE:
                        return (dynAPIRemove ((VMHOB *) preqUser));

                case    DT_CLEARALL:
                        return (dynAPIClearAll ());

                case    DT_GETVAR:
                        return (dynAPIGetVar ((dtgh_t *) preqUser,
                                                (dtgp_t *) presUser));

                case    DT_QUERY:
                        return (dynAPIQuery ((dtqi_t *) preqUser,
                                                (dtqo_t *) presUser));

                default:


                        return (ERROR_INVALID_PARAMETER);
        }
}


/***LP  dynAPIInsert - Perform DynamicTrace 'insert' subfunction
 *
 *      ulong_t dynAPIInsert (pdthUser)
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY   pdthUser        - USER SPACE Request Packet Pointer
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynAPIInsert (pdthUser)
dth_t  *pdthUser;
{
        ulong_t rc;
        ulong_t lLevel;
        dto_t  *pdto;
        dtlv_t  dtlv;

        TKMayBlock();

        rc = TKFuDWord ((ulong_t *) SSToDS (&lLevel), (ulong_t *) pdthUser);

        if (rc != NO_ERROR) {
                return (rc);
        }

        // lLevel is a WORD in DTLEVEL11, but is a DWORD in DTLEVEL20.

        if ((lLevel & 0xffff) == DTLEVEL11)
                rc = dynDTORead11 (SSToDS (&pdto), pdthUser);
        else if (lLevel == DTLEVEL20)
                rc = dynDTORead20 (SSToDS (&pdto), pdthUser);
        else
                rc = ERROR_INVALID_LEVEL;

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

        // A DTO exists at this point.  Use dynDTODestroy if we get an error.

        if (NO_ERROR != (rc = LDRGetSem ())) {
                dynDTODestroy (pdto, SSToDS (&dtlv));
                return (rc);
        }

        if (NO_ERROR == (rc = dynInitialize())) {
            if (NO_ERROR == (rc = dynAPIVerify (pdto))) {
                if (NO_ERROR == (rc = dynDTOFixup (pdto, SSToDS (&dtlv)))) {
                    dynDTOLink (pdto);
                    rc = dynDTOSet (pdto, SSToDS (&dtlv));
                }
            }
        }

        if (rc != NO_ERROR) {
                dynDTODestroy (pdto, SSToDS (&dtlv));
                dynShutdown();
        }

        LDRClearSem ();

        return (rc);
}


/***LP  dynAPIRemove - Perform DynamicTrace 'remove' subfunction
 *
 *      ulong_t dynAPIRemove (phmteUser)
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY   phmteUser       - USER SPACE Pointer to Module Handle
 *
 *      EXIT    ulong_t, standard error code
 *
 *      Compatability note:  The old system returned ERROR_INVALID_HANDLE
 *      if the MTE handle was invalid, and ERROR_INVALID_DATA if the
 *      tracepoints were not set on that module.  Here, we return
 *      ERROR_INVALID_DATA regardless of whether the module handle is
 *      valid or not.  We could add a call to LDRCheckModHandle in the
 *      error path to fix this, but there is no real pressing need...
 *
 */

ulong_t dynAPIRemove (phmteUser)
VMHOB  *phmteUser;
{
        ulong_t rc;
        dtlv_t  dtlv;

        TKMayBlock();

        if (NO_ERROR != (rc = TKFuWord (SSToDS(&dtlv.dtlv_hmte), phmteUser)))
                return (rc);

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

        rc = dynDTOScan (&pdtoHeadAll,          // For All DTOs,
                        DYN_DTOS_MATCHMTE   |   // matching the given hmte,
                        DYN_DTOS_SHOULDEXIST,   // return error if not found
                        0,                      // Across all addresses
                        (ulong_t) -1,           // Across all addresses
                        dynDTODestroy,          // completely destroy the DTO
                        SSToDS(&dtlv));         // hmte is setup already

        // Shut down DynTrc if possible.

        dynShutdown();

        LDRClearSem ();

        if (rc != NO_ERROR)                     // If no tracepoints set,
                rc = ERROR_INVALID_DATA;        // transform error code
                                                // (ERROR_NOT_SET in 1.1)

        return (rc);
}


/***LP  dynAPIClearAll - Perform DynamicTrace 'clear all' subfunction
 *
 *      ulong_t dynAPIClearAll ()
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY   NONE
 *
 *      EXIT    ulong_t, standard error code
 *
 *      This subfunction returns either ERROR_INTERRUPT or NO_ERROR.
 *
 */

ulong_t
dynAPIClearAll(void)
{
        ulong_t rc;
        dtlv_t  dtlv;

        TKMayBlock();

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

        dynDTOScan      (&pdtoHeadAll,          // For All DTOs,
                        0,                      // (no special considerations)
                        0,                      // Across all addresses
                        -1,                     // Across all addresses
                        dynDTODestroy,          // completely destroy the DTO
                        SSToDS (&dtlv));        // no data required.

        // Shut down DynTrc if possible.

        dynShutdown();

        LDRClearSem ();

        return (NO_ERROR);
}


/***LP  dynAPIGetVar   - Perform DynamicTrace 'get/zero variable' subfunction
 *
 *      ulong_t dynAPIGetVar(pdtghUser, pdtgpUser)
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY   pdtghUser       USER SPACE request packet pointer
 *              pdtgpUser       USER SPACE result packet pointer
 *
 *      EXIT    ulong_t, standard error code
 *
 *      Optimizations needed:
 *              - keep frequently used sizeof's in variables
 *              - remove indirection (dereferences) where possible
 */

ulong_t dynAPIGetVar(pdtghUser, pdtgpUser)
dtgh_t  *pdtghUser;
dtgp_t  *pdtgpUser;
{
        dtgp_t  *pdtgpCur;
        dtgp_t  *pdtgpMax;
        dtgp_t  *pdtgpMin;
        ulong_t rc;
        ulong_t rcTmp;
        ulong_t cbParams;
        dtgh_t  dtghTmp;
        dtlv_t  dtlv;

        TKMayBlock();

        // Copy in request packet header.

        if (NO_ERROR != (rc = TKFuBuff ((PCHAR) SSToDS(&dtghTmp),
            (PCHAR) pdtghUser, sizeof(dtgh_t), TK_UM_FATAL)))
                return(rc);

        // Verify dynamic trace format level.

        if (dtghTmp.dtgh_ulLevel < DTLEVEL20)
                return(ERROR_INVALID_LEVEL);

        // Verify the remaining request packet size.  We've already
        // copied in the dtgh header.  It is followed by 0 or more
        // dtgp (request parameter) structures.

                                        // Account for request header
        cbParams = dtghTmp.dtgh_cbDTGH - sizeof(dtgh_t);
        if ((cbParams % sizeof(dtgp_t)) != 0)
        {
                return(ERROR_INVALID_DATA);
        }

        // Verify that the request actually requires some action to occur.
        // If the command does not specify any action, or if there are no
        // request packet parameter structures as part of the request, then
        // there is no work to do.  Set the size of result packet to zero,
        // and return.

        dtlv.dtlv_cbResult = 0;

        if ((dtghTmp.dtgh_ulCmd == 0) || (cbParams == 0))
        {
                // No work requested.

                return(TKSuDWord(dtlv.dtlv_cbResult, dtghTmp.dtgh_pcbResult));
        }


        // Allocate resident heap to hold the remaining portion of the request
        // packet (i.e., the array of dtgp (request parameter) structures).

        if ((rc = VMAllocKHB(VM_HKH_PUB_RESRW, cbParams, DYNDTGPTMPOWNER,
                    NA, SSToDS(&pdtgpMin))) != NO_ERROR)
                return(rc);

        // Copy in the remaining request packet (i.e., the dtgp structures).

        if ((rc = TKFuBuff((PCHAR)pdtgpMin, (PCHAR) pdtghUser + sizeof(dtgh_t),
              cbParams, TK_UM_FATAL)) != NO_ERROR)
        {
                // Free the resident heap allocated to hold a copy of the
                // packet dtgp array.

                VMFreeKHB(VM_HKH_PUB_RESRW, pdtgpMin);
                return(rc);
        }

        // Set up invariant portions of dtlv from request header:
        //      - request packet address
        //      - command (get and/or zero)

        dtlv.dtlv_pResultUser = (PCHAR) pdtgpUser;
        dtlv.dtlv_ulCmd   = dtghTmp.dtgh_ulCmd;
        dtlv.dtlv_cbAllocRes = dtghTmp.dtgh_cbResult;

        // Grab the loader semaphore.

        if (NO_ERROR != (rc = LDRGetSem ()))
        {
                // Free the resident heap allocated to hold a copy of the
                // packet dtgp array.

                VMFreeKHB(VM_HKH_PUB_RESRW, pdtgpMin);
                return (rc);
        }

        // Process each request parameter structure.

        for (pdtgpCur = pdtgpMin,
             pdtgpMax = (dtgp_t *) ((PCHAR) pdtgpMin + cbParams);
                                            pdtgpCur < pdtgpMax; pdtgpCur++)
        {
                ulong_t flCmd;
                dto_t   **ppdtoHead;

                // Set up hMTE and ID fields in dtlv, and determine
                // tracepoint object matching for dynDTOScan().

                flCmd = 0;
                dtlv.dtlv_hmte = (VMHOB)(pdtgpCur->dtgp_hMTE);
                if (pdtgpCur->dtgp_hMTE != DT_NZHMTE)
                        flCmd |= DYN_DTOS_MATCHMTE;     // match on hMTE
                dtlv.dtlv_ID = (ushort_t)(pdtgpCur->dtgp_usID);
                flCmd |= DYN_DTOS_MATCHID;              // match on TDF ID

                // If request is for inactive modules, search pdtoHeadAll,
                // skipping over active modules.  Otherwise, (the normal
                // case), search pdtoHeadScan.  Note that the MTE handle is
                // zero in tracepoint objects associated with inactive
                // modules.

                if (dtlv.dtlv_hmte == 0)
                        ppdtoHead = &pdtoHeadAll;
                else
                        ppdtoHead = &pdtoHeadScan;

                // Pass pointer to parameter structure in the dtlv.
                dtlv.dtlv_pdtgp = pdtgpCur;

                // Call dynDTOScan() to process the param request.

                rc = dynDTOScan(ppdtoHead,      // For requested DTOs,
                        flCmd|DYN_DTOS_ERRORPROP// Matching as requested,
                             | DYN_DTOS_ERROREXIT,
                        0,                      // Across all addresses,
                        (ulong_t) -1,           // Across all addresses,
                        dynDTOGetVar,           // Collect/zero DTO variables,
                        SSToDS(&dtlv));         // Scan info is setup already.

                if (rc != NO_ERROR)
                        break;

        }

        // Relinquish the loader semaphore.
        LDRClearSem ();

        // Free the resident heap allocated to hold a copy of the request
        // packet dtgp array.

        VMFreeKHB(VM_HKH_PUB_RESRW, pdtgpMin);

        // Copy out the size of the result packet.  Do not trash 'rc' here,
        // we need to propagate DTO worker errors back to the caller.

        rcTmp = TKSuDWord(dtlv.dtlv_cbResult, dtghTmp.dtgh_pcbResult);
        if ((rcTmp != NO_ERROR) && (rc == NO_ERROR))
                rc = rcTmp;

        // Returns error encountered by DTO worker, if applicable; otherwise,
        // returns NO_ERROR.

        return(rc);
}


/***LP  dynAPIQuery    - Perform DynamicTrace 'query' subfunction
 *
 *      ulong_t dynAPIQuery(pdtqiUser, pdtqoUser)
 *
 *      The loader semaphore must NOT be owned here.
 *
 *      ENTRY   pdtqiUser       USER SPACE request packet pointer
 *              pdtqoUser       USER SPACE result packet pointer
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynAPIQuery(pdtqiUser, pdtqoUser)
dtqi_t  *pdtqiUser;
dtqo_t  *pdtqoUser;
{
        ulong_t rc;
        dtlv_t  dtlv;
        dtqi_t  dtqiTmp;
        ulong_t flCmd;
        dto_t   **ppdtoHead;
        ulong_t rcTmp;

        TKMayBlock();

        // Copy in request packet header.

        if (NO_ERROR != (rc = TKFuBuff ((PCHAR) SSToDS(&dtqiTmp),
            (PCHAR) pdtqiUser, sizeof(dtqi_t), TK_UM_FATAL)))
                return(rc);

        // Verify dynamic trace format level.

        if (dtqiTmp.dtqi_ulLevel < DTLEVEL20)
                return(ERROR_INVALID_LEVEL);

        // Set up dtlv from request header.  Determine tracepoint object
        // matching for dynDTOScan().

        dtlv.dtlv_hmte = (VMHOB)(dtqiTmp.dtqi_hMTE);
        flCmd = 0;
        if (dtqiTmp.dtqi_hMTE != DT_NZHMTE)
                flCmd |= DYN_DTOS_MATCHMTE;     // match on hMTE
        dtlv.dtlv_ID = (ushort_t)(dtqiTmp.dtqi_usID);
        flCmd |= DYN_DTOS_MATCHID;              // match on TDF ID

        dtlv.dtlv_cbResult = 0;
        dtlv.dtlv_cbAllocRes = dtqiTmp.dtqi_cbResult;
        dtlv.dtlv_pResultUser = (PCHAR) pdtqoUser;

        // If request is for inactive modules, search pdtoHeadAll,
        // skipping over active modules.  Otherwise, (the normal
        // case), search pdtoHeadScan.  Note that the MTE handle is
        // zero in tracepoint objects associated with inactive
        // modules.

        if (dtlv.dtlv_hmte == 0)
                ppdtoHead = &pdtoHeadAll;
        else
                ppdtoHead = &pdtoHeadScan;

        // Grab the loader semaphore.

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

        // Call dynDTOScan() to process the query request.

        rc = dynDTOScan(ppdtoHead,      // For requested DTOs,
                flCmd|DYN_DTOS_ERRORPROP// Matching as requested,
                     | DYN_DTOS_ERROREXIT,
                0,                      // Across all addresses,
                (ulong_t) -1,           // Across all addresses,
                dynDTOQuery,            // Query the DTO,
                SSToDS(&dtlv));         // Scan info is setup already.


        // Relinquish the loader semaphore.
        LDRClearSem ();

        // Copy out the size of the result packet.  Do not trash 'rc' here,
        // we need to propagate DTO worker errors back to the caller.

        rcTmp = TKSuDWord(dtlv.dtlv_cbResult, dtqiTmp.dtqi_pcbResult);
        if ((rcTmp != NO_ERROR) && (rc == NO_ERROR))
                rc = rcTmp;

        // Returns error encountered by DTO worker, if applicable; otherwise,
        // returns NO_ERROR.

        return (rc);

}


/***LP  DYNDetach - Detach tracepoints from a module
 *
 *      DYNDetach is used to detach tracepoints from a module
 *      (if any) just before we free the MTE.
 *
 *      If tracepoints are set on the module, we remove the DTO from
 *      the scan list, mark the DTO as DETACHED, then we clear DTR_SET
 *      and DTR_VALID and set DTR_NOCODE for each DTR in the DTO.
 *
 *      Once the DTO has been detached, the DTO is made swappable.
 *
 *      ulong_t DYNDetach ()
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   NONE
 *
 *      EXIT    ulong_t, standard error code
 *
 *      Pseudocode
 *
 */

ulong_t DYNDetach       (hmte)
VMHOB hmte;
{
        dtlv_t  dtlv;

        TKMayBlock();

        dtlv.dtlv_hmte = hmte;

        dynDTOScan      (&pdtoHeadAll,          // For All DTOs,
                        DYN_DTOS_MATCHMTE,      // matching the given hmte,
                        0,                      // Across all addresses
                        (ulong_t) -1,           // Across all addresses
                        dynDTODetach,           // DTO_ALLSET ==> DTO_DETACHED
                        SSToDS(&dtlv));         // hmte is setup already

        // Shut down DynTrc if possible.  Level 1 tracepoints may have
        // been destroyed during dynDTODetach.

        dynShutdown();

        return (NO_ERROR);
}

/***EP  DYNAttachCheck - Check for and remember any tracepoint reattachments
 *
 *      DYNAttachCheck is called for each module just after it is
 *      newly loaded.  DYNAttachCheck determines if there are any
 *      deatched tracepoint objects for this module, and if so, it
 *      moves those objects from the DTO_DETACHED state into the
 *      DTO_LOADING state.
 *
 *      Later, during DYNAttach, each DTO in the DTO_LOADING state is
 *      set again on the particular module, using the saved MTE handle.
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   DYNAttachCheck (hmte, pchPathName)
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNAttachCheck (hmte)
VMHOB   hmte;
{
        dtlv_t  dtlv;

        TKMayBlock();

        // If there are no detached DTOs, just skip the scan.

        if (cdtoDetached == 0) {
                return (NO_ERROR);
        }

        // We know we have at least one DTO in the DTO_DETACHED state.
        // Look for and check it.

        dtlv.dtlv_hmte = hmte;
        LDRGetLibNameToHeap (hmte, SSToDS(&(dtlv.dtlv_cchLibName)),
            SSToDS(&(dtlv.dtlv_pchLibName)));

        dynDTOScan      (&pdtoHeadAll,          // For All DTOs,
                        0,                      // no special considerations
                        0,                      // Across all addresses
                        (ulong_t) -1,           // Across all addresses
                        dynDTOAttachCheck,      // DTO_DETACHED ==> DTO_LOADING
                        SSToDS(&dtlv));

        // Free the LibName swappable heap block.

        VMFreeKHB(VM_HKH_PUB_SWAPRW, dtlv.dtlv_pchLibName);

        return (NO_ERROR);

}

#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")
/***EP  DYNAttach - Complete reattachment of tracepoints after load success.
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   DYNAttach       ()
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNAttach       ()
{
        ulong_t rc = NO_ERROR;
        dtlv_t  dtlv;

        TKMayBlock();

        // If there are any loading DTOs, attempt to change them to DTO_ALLSET

        if (cdtoLoading != 0)
                rc = dynDTOScan (&pdtoHeadAll,
                                DYN_DTOS_ERRORPROP |
                                DYN_DTOS_ERROREXIT,
                                0,
                                (ulong_t) -1,
                                dynDTOAttach,
                                SSToDS(&dtlv));
        return (rc);
}
#pragma code_seg()
#pragma code_seg("DYNSWAP_TEXT", "CODE")

/***EP  DYNAttachError - Cancel reattachment of tracepoints due to load error
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   DYNAttachError  ()
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNAttachError  ()
{
        dtlv_t  dtlv;

        TKMayBlock();

        // If there are any loading DTOs, change them to DTO_DETACHED.

        if (cdtoLoading != 0)
                dynDTOScan      (&pdtoHeadAll,
                                0,
                                0,
                                (ulong_t) -1,
                                dynDTOAttachError,
                                SSToDS(&dtlv));
        return (NO_ERROR);
}


#if 0   // STUB

/***EP  DYNReAttachRange - Set a range of tracepoints again
 *
 *      DYNReAttachRange is used to set tracepoints in a range of
 *      memory.  This is used to set tracepoints in an object that
 *      had tracepoints, was freed, and then was reloaded again.
 *
 *      As the module has not been unloaded, expect no errors.
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   DYNReAttachRange(hmte, lAddrMin, lAddrMax)
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNReAttachRange(hmte, lAddrMin, lAddrMax)
VMHOB   hmte;
ulong_t lAddrMin;
ulong_t lAddrMax;
{
        ulong_t rc;
        dtlv_t  dtlv;

        TKMayBlock();

        dtlv.dtlv_hmte = hmte;                  // for dynDTOScan
        dtlv.dtlv_lAddrMin = lAddrMin;          // for dynDTOReAttachRange
        dtlv.dtlv_lAddrMax = lAddrMax;          // for dynDTOReAttachRange

        rc = dynDTOScan (&pdtoHeadAll,          // For All DTOs,
                        DYN_DTOS_MATCHMTE   |   // matching the given hmte,
                        DYN_DTOS_MUSTEXIST,     // panic if not found
                        lAddrMin,               // within the given range...
                        lAddrMax,               // within the given range...
                        dynDTOReAttachRange,    // Reattach the given range
                        SSToDS(&dtlv));
        if (rc != NO_ERROR) {
                panic ("DYNReAttachRange");
        }

        return (NO_ERROR);
}

#endif  // STUB


/***EP  DYNPurgeRange - Purge tracepoints from a range of memory.
 *
 *      DYNPurgeRange is used to purge a range of tracepoints from
 *      a region of memory.  This is used to remove any set tracepoints
 *      from read-only pages after making pages private.  This is
 *      used by VMMapDebugAlias.
 *
 *      pAlias must be a writeable linear alias.  This pointer should
 *      correspond to lAddrMin in the linear address space.  That is,
 *      for all set tracepoints at address 'p' in the (read-only)
 *      range [lAddrMin, lAddrMin+cb), there exists a tracepoint
 *      to purge at the read-write address (p-lAddrMin+pAlias).
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   DYNPurgeRange (hmte, lAddrMin, lAddrMax, pAlias)
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t DYNPurgeRange   (hmte, lAddrMin, cb, pAlias)
VMHOB   hmte;
ulong_t lAddrMin;
ulong_t cb;
char    *pAlias;
{
        ulong_t rc;
        dtlv_t  dtlv;

        TKMayBlock();

        dynAssert (cb != 0);                    // nonzero length only...

        dtlv.dtlv_hmte = hmte;
        dtlv.dtlv_pAlias = (ulong_t) pAlias;
        dtlv.dtlv_lAddrMin = dtlv.dtlv_pAliasedPoint = lAddrMin;
        dtlv.dtlv_lAddrMax = lAddrMin + cb - 1;

        dynDTOScan     (&pdtoHeadAll,           // For All DTOs,
                        DYN_DTOS_MATCHMTE,      // matching the given hmte,
                        lAddrMin,               // within the given range...
                        dtlv.dtlv_lAddrMax,     // within the given range...
                        dynDTOPurgeRange,       // Purge the range.
                        SSToDS(&dtlv));

        return (NO_ERROR);
}


/***LP  dynAPIVerify - Verify a user's request packet after reading it.
 *
 *      dynAPIVerify checks that the API request packet makes sense.
 *      dynAPIVerify checks the following:
 *              The log is not too large, but exists.
 *              The MTE handle is valid
 *              The MTE does not already have a DynTrc object attached to it
 *              The format of the DTR list is OK.
 *
 *      The loader semaphore must be owned here.
 *
 *      ulong_t dynAPIVerify  (dto_t *pdto);
 *
 *      ENTRY   pdto    - pointer to a DTO (UNTRUSTED DATA)
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynAPIVerify  (pdto)
dto_t   *pdto;
{
        ulong_t rc;
        dtlv_t  dtlv;

        TKMayBlock();

        // Verify that we will have room to log information, that
        // the log size is not too large, and that the reserved
        // flags are all zero.

        if ((0 == pdto->dto_dth.dth_cbLogMax) ||
            (pdto->dto_dth.dth_cbLogMax > CBLOGMAX) ||
            (pdto->dto_dth.dth_flDT != 0))
                return (ERROR_INVALID_DATA);

        // Verify that the MTE handle exists, and if so, get
        // information about the MTE from the loader.

        if (NO_ERROR != (rc = LDRGetDYNMteInfo ((VMHOB) pdto->dto_dth.dth_hMTE,
            &(pdto-> dto_dti.dti_padtl), &(pdto-> dto_dti.dti_cdtl),
            &(pdto-> dto_dti.dti_lfState))))
                return (rc);

        // Get the library name.  If this works, we will have attached a
        // swappable kernel heap entry to the DTO, which will need to be
        // freed later, in dynDTOFree.  However, we won't actually touch
        // this name until tracepoint reattachment is happening; therefore,
        // it is swappable.

        if (NO_ERROR != (rc = LDRGetLibNameToHeap ((VMHOB)
            pdto->dto_dth.dth_hMTE, &(pdto-> dto_dti.dti_cchLibName),
            &(pdto-> dto_dti.dti_pchLibName))))
                return (rc);

        // Verify that this is not the second DTO for this module.
        // We disallow this, although it's really not a major internal
        // concern in 2.0.  This was not allowed in 1.1.

        dtlv.dtlv_hmte = (VMHOB) pdto->dto_dth.dth_hMTE;

        rc = dynDTOScan (&pdtoHeadAll,          // For All DTOs,
                        DYN_DTOS_MATCHMTE   |   // matching the given hmte,
                        DYN_DTOS_SHOULDEXIST,   // return error if not found
                        0,                      // Across all addresses
                        (ulong_t) -1,           // Across all addresses
                        dynDTONullProc,         // No actual worker-just scan.
                        SSToDS(&dtlv));         // hmte is setup already

        if (rc == NO_ERROR)
                return (ERROR_ALREADY_EXISTS);  // another dto found - error

        // Verify the DTR list in the DTO to insure that scanning works OK.

        return (dynDTOVerify (pdto, SSToDS(&dtlv)));
}


/***LP  dynInitialize - Prepare DynTrc Component for use.
 *
 *      dynInitialize prepares the DynTrc component for initial use.
 *      If the DynTrc component is already up and running, dynInitialize
 *      will do nothing.
 *
 *      If an error code is returned, dynShutDown must be called.
 *
 *      dynInitialize is only called
 *
 *
 *      ulong_t dynInitialize  ();
 *
 *      ENTRY   none
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynInitialize (void)
{
        ulong_t rc = NO_ERROR;
        VMAC    ac;

        TKMayBlock();

        // If any tracepoint objects exist, don't reinitialize DynTrc.

        if (pdtoHeadAll != NULL)
                return (rc);

        // Initialize dynselDTB.  This selector is used to
        // optimize the interpreter by causing a GP fault
        // if the log buffer overflows.  The limit is adjusted
        // just before we start the interpreter.

        dynAssert (dynselDTB == 0);

        // Alloc a GDT descriptor into dynselDTB.

        rc = SELAllocGDT(&dynselDTB);

        if (rc != NO_ERROR) {           // In case of error,
                dynselDTB = 0;          // be sure dynselDTB is still zero,
                return (rc);            // then propogate the error.
        }

        // Setup the new GDT UVIRT descriptor base to
        // point to dynDTB.  The limit doesn't really
        // matter here - it is adjusted in DYNTrap3.

        SELSetDescInfo(SELSD_ALL,
                ((ushort_t) (D_UVIRT<<8) | D_DATA0),
                dynselDTB, sizeof(dynDTB), (ulong_t) &dynDTB,
                HOBNULL, HOBNULL, 0);

        // Initialize pbdynTraceAlias, pptedynTraceAlias,
        // pbdynTraceIntAlias and pptedynTraceIntAlias.

        dynAssert (pbdynTraceAlias == NULL);
        dynAssert (pptedynTraceAlias == NULL);
        dynAssert (pbdynTraceIntAlias == NULL);
        dynAssert (pptedynTraceIntAlias == NULL);

        // Alloc two pages for aliasing.  The first page's
        // frame is adjusted by DYNTrap3 to alias to the
        // (physical) page of code, to replace the opcode
        // before the single-step, and replace the INT3
        // afterwards.   This page is NOT to be used for
        // API-time aliasing.

        // The second page's frame is also adjusted by
        // DYNTrap3, but is used to remove interrupt
        // time tracepoints.  All adjustments to this
        // page must be done with interrupts off.

        // VMAllocMem : Reserve 2 pages, Commit 2 pages,
        // SYSTEM WRITABLE RESIDENT UVIRT
        // no hptda, DYNDTOOWNER, no hmte,
        // no flselmgr, no blkno.  pbdynTraceAlias

        rc = VMAllocMem (2*PAGESIZE, 2*PAGESIZE,
                VMAC_ARENASYS | VMAC_LOCANY | VMAC_ALIGNPAGE |
                PG_W | PG_UVIRT,
                HOBNULL, DYNDTOOWNER, HOBNULL,
                0, -1, SSToDS(&ac));

        if (rc != NO_ERROR) {
                dynShutdown();          // Clean up in case of error.
                return (rc);            // Propogate Errors.
        }

        // Success.  Initialize variables.
        // dynShutdown depends on (pbdynTraceAlias = ac.ac_va) here.

        pbdynTraceIntAlias =
                (pbdynTraceAlias = ac.ac_va) + PAGESIZE;

        pptedynTraceAlias = pgPageToPte (
                (pbdynTraceAlias >> PAGESHIFT), NULL);

        pptedynTraceIntAlias = pgPageToPte (
                (pbdynTraceIntAlias >> PAGESHIFT), NULL);

        // Lock the DYNLOCK_TEXT region.

        dynAssert (!fdynCodeLocked);

        rc = VMLockMem((ulong_t) &StartDYNLOCK_TEXT,
                        &EndDYNLOCK_TEXT - &StartDYNLOCK_TEXT,
                        VML_LONG, &dynlh,
                        (struct pagelist_s *) VM_NO_ADDR,
                        (ulong_t *) VM_NO_ADDR);

        // If no error, mark code as locked.
        // If error, clean up.

        if (!(fdynCodeLocked = (rc == NO_ERROR)))
                dynShutdown();          // Clean up in case of error.

        return (rc);
}


/***LP  dynShutdown - Attempt to Shut down the DynTrc component.
 *
 *      dynShutdown is called to free any DynTrc memory management
 *      structures.  If DynTrc is still in use, dynShutdown will not
 *      actually shut down the component.
 *
 *      dynShutdown depends on some code in dynInitialize.
 *
 *      dynShutdown must be called with the loader semaphore set.
 *
 *      ulong_t dynShutdown  ();
 *
 *      ENTRY   none
 *
 *      EXIT    ulong_t, standard error code
 *
 */

void dynShutdown (void)
{
        ulong_t rc;

        TKMayBlock();

        // If any tracepoint objects exist, don't shut down DynTrc.

        if (pdtoHeadAll != NULL)
                return;

        // If the DynTrc code is locked, unlock the region,
        // and mark the region free.

        if (fdynCodeLocked) {
                rc = VMUnlock (&dynlh);
                dynAssert (rc == NO_ERROR);
                fdynCodeLocked = FALSE;
        }

        // If dynselDTB is nonzero, free it and mark it free.

        if (dynselDTB != 0) {
                rc = SELFreeGDT(dynselDTB);
                dynAssert (rc == NO_ERROR);
                dynselDTB = 0;
        }

        // If the alias pages exist, free them and mark them free.

        if (pbdynTraceAlias != NULL) {
                rc = VMFreeMem (pbdynTraceAlias, HOBNULL, 0);
                dynAssert (rc == NO_ERROR);
                pbdynTraceAlias = NULL;
                pptedynTraceAlias = NULL;
                pbdynTraceIntAlias = NULL;
                pptedynTraceIntAlias = NULL;
        }

        // All DynTrc data items must be clear at this point.

        dynAssert (pdtoHeadAll  == NULL);
        dynAssert (pdtoHeadScan == NULL);
        dynAssert (cdtoDetached == NULL);
        dynAssert (cdtoLoading  == NULL);

        return;
}

#endif  // DEBUG
