/*static char *SCCSID = "@(#)dyndtr.c	6.3 91/07/29";*/
#define SCCSID  "@(#)dyndtr.c	6.3 91/07/29"
/*
 *      This file implements routines to manage the DynamicTrace
 *      DTR (Dynamic Trace Record) structures.  Most of these
 *      routines function on a single DTR at a time, and are
 *      also given the address of the containing DTO.
 *
 *      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_DYNDTR_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 <dynxport.h>
#include <dynapi.h>
#include <dynlocal.h>
#include <kutil.h>


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


#if     RAS


/***LP  dynGetObjNum - Get object number for an address, using dto_padtl.
 *
 *      dynGetObjNum returns the object number for a given flat address
 *      that is known to be within the dto_padtl array.
 *
 *      ENTRY   pdto    - DTO pointer
 *              p       - linear address to match.
 *
 *      EXIT    ulong_t, object number
 *              panic if not found.
 *
 */

ulong_t
dynGetObjNum(dto_t *pdto,
             ulong_t p)
{
        register ulong_t i;
        register dtl_t *pdtl;

        TKMayBlock();

        // For each object, if within the object, return the object number.

        pdtl = pdto->dto_dti.dti_padtl;
        for (i=1; i <= pdto->dto_dti.dti_cdtl; i++, pdtl++)     // one-based
                if (((ulong_t) p - pdtl->dtl_pBase) < pdtl->dtl_cb)
                        return (i);

        panic ("dynGetObjNum");
}


/***LP  dynDTRFixup - Fixup addresses contained in a DTR
 *
 *      If dynDTRFixup succeeds, the DTR has the following properties:
 *              DTR_FIXEDUP set
 *              dtr_Offset adjusted to flat address using dtr_Object
 *              pdtr is linked into the hash table (via dtr_Object)
 *              pdto-> dto_dti.dti_lAddrMin adjusted
 *              pdto-> dto_dti.dti_lAddrMax adjusted
 *
 *              dynDTRCompile was called on pdtr successfully.
 *
 *      If dynDTRFixup fails, the contents of the tracepoint are unchanged,
 *      However, dti_lAddrMin and dti_lAddrMax may have changed.
 *
 *      Once sucessfully fixed up, there is no need to UnFixup the DTR.
 *
 *      The loader semaphore must be owned here.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, not set up.
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynDTRFixup     (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        ulong_t rc = NO_ERROR;
        register dtl_t *pdtl;

        TKMayBlock();

        //      If not already fixed up, perform the fixup.

        if (!(pdtr->dtr_Reserved & DTR_FIXEDUP)) {

                // Bounds-check dtr_Object.

                if (pdtr->dtr_Object > pdto->dto_dti.dti_cdtl)
                        return (ERROR_INVALID_SEGMENT_NUMBER);

                // Get locator information.

                pdtl = &(pdto->dto_dti.dti_padtl[pdtr->dtr_Object-1]);

                // Verify dtr_Offset.  The dtr_Object must be valid code.

                if ((pdtl-> dtl_cb <= pdtr->dtr_Offset) ||
                    (!(pdtl-> dtl_fs & DTL_CODE)) ||
                    (pdtl-> dtl_pBase == (ulong_t) -1)) {
                        return (ERROR_INVALID_SEGMENT_NUMBER);
                }

                pdtl-> dtl_fs |= DTL_TRACED;

                // Convert pdtr->dtr_Offset to it's flat equivalent.

                pdtr->dtr_Offset += pdtl-> dtl_pBase;

                // Adjust dti_lAddrMin and dti_lAddrMax as needed.

                if (pdto-> dto_dti.dti_lAddrMin > pdtr->dtr_Offset)
                        pdto-> dto_dti.dti_lAddrMin = pdtr->dtr_Offset;

                if (pdto-> dto_dti.dti_lAddrMax < pdtr->dtr_Offset)
                        pdto-> dto_dti.dti_lAddrMax = pdtr->dtr_Offset;

                // Link DTR into the hash table via pdtr->dtr_Object.

                pdtr->dtr_Object = (ulong_t) pdto->dto_dti.dti_apDTRHash
                        [dynHashCalc(pdtr->dtr_Offset)];

                pdto->dto_dti.dti_apDTRHash [dynHashCalc(pdtr->dtr_Offset)] =
                        pdtr;

                pdtr->dtr_Reserved |= DTR_FIXEDUP;
        }

        //      Attempt the compile.  If this succeeds, the DTR_COMPILED
        //      bit will be set; otherwise DTR_COMPILED will be clear.

        rc = dynDTRCompile (pdto, pdtr, pdtlv);

        return (rc);
}


/***LP  dynDTRSet - Set a tracepoint in memory.
 *
 *      dynDTRSet validates and sets the tracepoint in memory.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, with these fields set:
 *              pdtlv-> dtlv_pAliasedPoint
 *              pdtlv-> dtlv_cbAlias
 *              pdtlv-> dtlv_pAlias
 *
 *      EXIT    ulong_t, standard error code
 *
 *      NOTES
 */

ulong_t dynDTRSet       (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        ulong_t rc = NO_ERROR;

        TKMayBlock();

        // All tracepoints should be fixed up at this point, and
        // the DTO_SOMESET flag should already be set for us.

        dynAssert ((pdto-> dto_dti.dti_lfState & (DTO_FIXEDUP | DTO_SOMESET))==
                                                 (DTO_FIXEDUP | DTO_SOMESET));

        // In order to set a tracepoint, it must be fixed up and compiled,
        // but not set.  We don't care about the other bits.

        dynAssert ((pdtr->dtr_Reserved &
                   (DTR_COMPILED | DTR_FIXEDUP | DTR_SET)) ==
                   (DTR_COMPILED | DTR_FIXEDUP));

        // If not validated, then validate the tracepoint.

        if (!(pdtr->dtr_Reserved & DTR_VALID))
                if (NO_ERROR != (rc = dynDTRValidate (pdto, pdtr, pdtlv)))
                        return (rc);

        // Verify that the tracepoint lies within the aliased page.

        dynAssert ((pdtr-> dtr_Offset - pdtlv-> dtlv_pAliasedPoint) <
                   PAGESIZE);

        // We have an alias to this tracepoint, so there must be code here.

        pdtr->dtr_Reserved &= ~DTR_NOCODE;

        // Tracepoint set ok.

        pdtr->dtr_Reserved |= DTR_SET;

        // If it's been removed, don't set it again.

        if (!(pdtr->dtr_Reserved & DTR_REMOVED)) {

                // Set the tracepoint in memory.

                *((uchar_t *) (pdtlv-> dtlv_pAlias -
                               pdtlv-> dtlv_pAliasedPoint +
                               pdtr-> dtr_Offset)) = MI_INT3;
        }

        return (NO_ERROR);

}


/***LP  dynDTRUnSet - Remove a tracepoint from memory.
 *
 *      dynDTRUnSet replaces the INT3 in memory with the original opcode,
 *      and clears DTR_SET.
 *
 *      dynDTRUnSet may only be called on tracepoints that have been
 *      fixed up, compiled, and validated.  It need not be set.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, with these fields set:
 *              pdtlv-> dtlv_pAliasedPoint
 *              pdtlv-> dtlv_pAlias
 *
 *      EXIT    ulong_t, standard error code
 *
 *      NOTES
 */

ulong_t dynDTRUnSet     (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        TKMayBlock();

        // All tracepoints should be fixed up at this point, and
        // the DTO_SOMESET flag should still be set.

        dynAssert ((pdto-> dto_dti.dti_lfState & (DTO_FIXEDUP | DTO_SOMESET))==
                                                 (DTO_FIXEDUP | DTO_SOMESET));

        // In order to unset a tracepoint, it must be fixed up, compiled,
        // and validated.  It need not be set.

        dynAssert ((pdtr->dtr_Reserved &
            (DTR_FIXEDUP | DTR_COMPILED | DTR_VALID)) ==
            (DTR_FIXEDUP | DTR_COMPILED | DTR_VALID));

        // If there is no code here, or is removed, there is no
        // tracepoint to UnSet.  Also, only unset set tracepoints.

        if ((pdtr->dtr_Reserved & (DTR_NOCODE | DTR_REMOVED | DTR_SET)) ==
            DTR_SET) {

                // Verify that the tracepoint lies within the aliased page.

                dynAssert ((pdtr-> dtr_Offset - pdtlv-> dtlv_pAliasedPoint) <
                            PAGESIZE);

                // UnSet the tracepoint from memory.

                *((uchar_t *) (pdtlv-> dtlv_pAlias -
                               pdtlv-> dtlv_pAliasedPoint +
                               pdtr-> dtr_Offset)) = pdtr-> dtr_Opcode;
        }

        pdtr->dtr_Reserved &= ~DTR_SET;

        // There is no such call as 'dynDTRUnValidate' required.
        // We need validate the tracepoint only once.

        return (NO_ERROR);
}


/***LP  dynDTRDetach - Mark a tracepoint as detached.
 *
 *      dynDTDetach marks the tracepoint as being detached, by clearing
 *      DTR_SET and DTR_VALID, and by setting DTR_NOCODE.  We also note
 *      that because no process is using the tracepoints, we can clear
 *      the DTR_PURGED bit now.  We must keep the DTR_REMOVED bit on
 *      to prevent reinserting those tracepoints across module loads.
 *
 *      Because dynDTDetach can only be called when the module is
 *      no longer in use, there is never any need to clear the
 *      tracepoint in memory.  Hence, no aliases are used.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer.
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynDTRDetach    (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        TKMayBlock();

        // All tracepoints should be fixed up at this point.

        dynAssert ((pdto-> dto_dti.dti_lfState & DTO_FIXEDUP) == DTO_FIXEDUP);

        // In order to detach a tracepoint, it must be fixed up and
        // compiled.  It need not be set or validated.

        dynAssert ((pdtr->dtr_Reserved &
            (DTR_FIXEDUP | DTR_COMPILED)) ==
            (DTR_FIXEDUP | DTR_COMPILED));

        // Clearing VALID and SET will cause dynDTRSet to validate and
        // set the tracepoint again when we reinsert the tracepoints.

        pdtr->dtr_Reserved &= ~(DTR_VALID | DTR_SET | DTR_PURGED);
        pdtr->dtr_Reserved |= DTR_NOCODE;
        return (NO_ERROR);
}


/***LP  dynDTRMove - Move a tracepoint to another address.
 *
 *      dynDTRMove moves a tracepoint to a new address, as given
 *      by it's new and old locator information.  Tracepoints move
 *      across module loads.
 *
 *      dynDTRMove only works on tracepoints which are fixed up and
 *      compiled.  dynDTRMove cannot be called on a set tracepoint.
 *
 *      dynDTRMove does not need to modify the hash table, because
 *      the hash algorithm only uses bits inside PAGEMASK, and all
 *      objects are aligned on page boundaries.  (Note: This will
 *      not always be true for device drivers, but device driver
 *      tracepoints cannot move as the device cannot be reloaded.)
 *
 *      dynDTRMove cannot be allowed to fail.  It must trust that
 *      the new locator information is identical to the old locator
 *      information, except for the base addresses.  The sizes and
 *      types of the objects may not change.  This must be verifyed
 *      before calling dynDTRMove.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer.
 *              pdtlv->dtlv_padtlOld must be set up.
 *              pdtlv->dtlv_padtlNew must be set up.
 *
 *              pdtlv->dtlv_padtlNew and pdtlv->dtlv_padtlOld must
 *              correspond to each other.
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynDTRMove      (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        register int Object;

        TKMayBlock();

        // All tracepoints should be fixed up at this point, but
        // No tracepoints may be set in the entire DTO.

        dynAssert ((pdto-> dto_dti.dti_lfState &
                        (DTO_SOMESET | DTO_FIXEDUP)) == DTO_FIXEDUP);

        // In order to move a tracepoint, it must be fixed up and
        // compiled, but it must not be set or validated.

        dynAssert ((pdtr->dtr_Reserved &
            (DTR_FIXEDUP | DTR_COMPILED | DTR_VALID | DTR_SET)) ==
            (DTR_FIXEDUP | DTR_COMPILED));

        // Get the object number for the tracepoint.   Note that
        // dtr_Object is the hash table link at this point, so we
        // must use dynGetObjNum here.

        Object = dynGetObjNum (pdto, pdtr->dtr_Offset);

        // Move the tracepoint.  All that needs to change is the
        // tracepoint's linear address (dtr_Offset).

        pdtr->dtr_Offset = pdtr->dtr_Offset -
                           pdtlv-> dtlv_padtlOld [Object-1].dtl_pBase +
                           pdtlv-> dtlv_padtlNew [Object-1].dtl_pBase;

        return (NO_ERROR);
}


/***LP  dynDTRPurge - Purge a tracepoint from memory.
 *
 *      dynDTRPurge removes a tracepoint from memory at a specified
 *      alias address.  This is used to purge tracepoints from
 *      DosDebug alias regions when needed.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, with the folloing set up:
 *                      dtlv_pAlias
 *                      dtlv_pAliasedPoint
 *                      dtlv_lAddrMin
 *                      dtlv_lAddrMax
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t dynDTRPurge     (pdto, pdtr, pdtlv)
dto_t   *pdto;
dtr_t   *pdtr;
dtlv_t  *pdtlv;
{
        TKMayBlock();

        // dynDTRPurge may be called with the tracepoint in any state,
        // but is only effective when DTR_SET.

        if (pdtr->dtr_Reserved & DTR_SET) {

                *((uchar_t *) (pdtlv-> dtlv_pAlias -
                               pdtlv-> dtlv_pAliasedPoint +
                               pdtr-> dtr_Offset)) = pdtr-> dtr_Opcode;
        }

        // Mark that the tracepoint has been purged from memory,
        // to begin checking for a debugged processes.

        pdtr->dtr_Reserved |= DTR_PURGED;

        return (NO_ERROR);
}


/***LP  dynDTRMapAlias - Map a writable alias to the tracepoint's code.
 *
 *      dynDTRMapAlias uses VMMapAlias to map an alias to the
 *      tracepoint's code
 *
 *      The alias defaults to two pages in length.  If a two-page
 *      alias can't be obtained, a one-page alias is used instead.
 *      This occurs in the last page of an object.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, not set up.
 *
 *
 *      EXIT    ulong_t, standard error code
 *
 *              NO_ERROR : These fields are set up:
 *                      pdtlv-> dtlv_pAliasedPoint
 *                      pdtlv-> dtlv_cbAlias
 *                      pdtlv-> dtlv_pAlias
 */

ulong_t
dynDTRMapAlias(dto_t *pdto,
               dtr_t *pdtr,
               dtlv_t *pdtlv)
{
        ulong_t rc;
        VMAC    ac;
        ulong_t cb = 2*PAGESIZE;
        VMOD    od;

        TKMayBlock();

        // The tracepoint must be fixed up at this point, so that we
        // can use dtr_Offset as a flat address.

        dynAssert (pdtr->dtr_Reserved & DTR_FIXEDUP);

        pdtlv-> dtlv_pAliasedPoint = pdtr-> dtr_Offset & ~PAGEMASK;

//      If it's a DLL, we have no context information.  Get it now.

        if (pdto->dto_dti.dti_lfState & DTO_DLL) {
                od.od_laddr = pdtlv->dtlv_pAliasedPoint;
                od.od_pPTDA = NULL;
                rc = ERROR_INVALID_ADDRESS;
                if (!VMShrArena(od.od_laddr) ||
                    (rc = (ulong_t) VMGetOd(VMGS_TAKESEM | VMOD_ANY_CTXT,
                                            SSToDS(&od)))
                  != NO_ERROR) {
                    return (rc);
                }

                pdtlv->dtlv_hptda = VMHobPTDAOd(od);
                pdtlv->dtlv_hobAlias = VMHobOd(od);


        }

//      Since it is not a dll, get a handle and set the semaphore

        else
        {
            if (NO_ERROR !=
                (rc = (ulong_t) VMGetHandle(pdtlv-> dtlv_pAliasedPoint,
                 pdtlv-> dtlv_hptda, &pdtlv-> dtlv_hobAlias))) {
                     return (rc);
             }
             if (NO_ERROR != (rc = (ulong_t) VMGetSem(pdtlv-> dtlv_hobAlias,
                 VMGS_TAKESEM, NULL))) {
                    return (rc);
             }
        }

//      We now have the object semaphore and the alias is still
//      valid so the alias won't suddenly disappear if we decide to
//      block.

        do {
                rc = (ulong_t) VMMapAlias(VMAC_ARENASYS |
                                PG_W | PG_R |
                                VMAC_LOCANY | VMAC_ALIGNPAGE,   // flalloc
                                0,                              // flsel
                                0,                              // fs
                                0,                              // laddral
                                0,                              // hptdaal
                                pdtlv-> dtlv_pAliasedPoint,     // laddr
                                pdtlv-> dtlv_hptda,             // hptda
                                cb,                             // cb
                                SSToDS (&ac));                  // pac

        } while ((rc != NO_ERROR) && ((cb -= PAGESIZE) != 0));

        // In case of error, set dtlv_pAlias == NULL.

        pdtlv-> dtlv_pAlias = (rc == NO_ERROR) ? ac.ac_va : NULL;
        pdtlv-> dtlv_cbAlias = cb;

        if (NO_ERROR != rc) {
                VMClearSem (pdtlv-> dtlv_hobAlias);
        }

        return (rc);
}


/***LP  dynDTRUnMapAlias - UnMap the previously mapped alias page(s)
 *
 *      dynDTRUnMapAlias uses VMUnMapAlias to unmap an existing alias
 *      to the tracepoint's code.
 *
 *      ENTRY   pdto    - DTO pointer
 *              pdtr    - DTR pointer
 *              pdtlv   - Local variables pointer, with these fields set:
 *              pdtlv-> dtlv_cbAlias
 *              pdtlv-> dtlv_pAlias
 *
 *      EXIT    ulong_t, standard error code
 *
 */

ulong_t
dynDTRUnMapAlias(dto_t *pdto,
                 dtr_t *pdtr,
                 dtlv_t *pdtlv)
{
        ulong_t rc;                     // Error code

        TKMayBlock();

        do {
                rc = (ulong_t) VMFreeMem ((ulong_t) pdtlv-> dtlv_pAlias,
                        0, NA);
        } while (rc == ERROR_INTERRUPT);

        dynAssert (rc == NO_ERROR);

        VMClearSem (pdtlv-> dtlv_hobAlias);

        return (NO_ERROR);
}


/***LP  dynDTRSkipScan - return TRUE if this DTR is not to be scanned.
 *
 *      The DTR will be skipped if any of the following are true :
 *
 *      * If fixed-up, and dtr_Offset falls outside of [lAddrMin, lAddrMax],
 *      * Any bits which must be set are not set
 *      * Any bits which must be clear are not clear
 *
 *      ENTRY   pdtr            - DTR pointer
 *              lAddrMin        - Minimum Flat address, inclusive
 *              lAddrMax        - Maximum Flat address, inclusive
 *              lfSet           - Flags that must be set
 *              lfClear         - Flags that must be clear
 *
 *      EXIT    ulong_t, boolean
 *                      TRUE    - Skip this DTR when scanning
 *                      FALSE   - Don't skip this DTR when scanning.
 *
 */

ulong_t
dynDTRSkipScan(dtr_t *pdtr,
               ulong_t lAddrMin,
               ulong_t lAddrMax,
               ulong_t lfSet,
               ulong_t lfClear)
{
        TKMayBlock();

        return(((pdtr-> dtr_Reserved & DTR_FIXEDUP) &&
                 ((pdtr-> dtr_Offset < lAddrMin) ||
                  (pdtr-> dtr_Offset > lAddrMax))) ||
               ((pdtr-> dtr_Reserved & lfSet) != lfSet) ||
               ((pdtr-> dtr_Reserved & lfClear) != 0));
}


/***LP  dynDTRScan - Traverse a selected set of DTR records within a DTO.
 *
 *      dynDTRScan is used to traverse the list of DTR records for a
 *      single DTO, calling a worker to perform an operation on each
 *      selected DTR.
 *
 *      The order of the traversal and the selected records depend on
 *      the passed parameters.  It is possible to filter the selected
 *      records via an address range, or by bits held in the dtr_Reserved
 *      byte.
 *
 *      If DYN_DTRS_ALIAS is selected, an alias will be created on
 *      each individual page as the list is traversed.  This alias
 *      region will normally be two pages long.  If a two-page region
 *      cannot be created, an attempt will be made to alias one page.
 *      If that fails, dynDTRScan will fail.  The alias is passed to
 *      the worker via pdtlv.dtlv_pAlias.  As the alias is writable,
 *      the memory manager will automatically convert any aliased
 *      discardable pages to swappable.
 *
 *      If DYN_DTRS_ERRORPROP is set, errors from the worker wil be
 *      propogated to the caller of dynDTRScan.  If DYN_DTRS_ERROREXIT
 *      is set, dynDTRScan will exit as soon as the first error occurs,
 *      without calling the worker any more times.
 *
 *      pdtlv may be NULL so long as DYN_DTRS_ALIAS is not selected.
 *      It will be passed to the worker unchanged.
 *
 *      If the DTR is not yet fixed up, the address range test is skipped.
 *
 *      dynDTRScan assumes that there are no extra, missing, or
 *      incomplete DTR records.   This is checked once in dynDTOVerify.
 *
 *      To select the dtr_Reserved flags to pay attention to, do the
 *      following:
 *              To traverse records that have certian bits SET :
 *                      - set the DYN_DTRS_IFSETMASK bits to check in flCmd
 *
 *              To traverse records that have certian bits CLEAR :
 *                      - set the DYN_DTRS_IFCLEARMASK bits to check in flCmd
 *
 *      ENTRY   pdto            - DTO to traverse
 *              flCmd           - Traversal Option Flags :
 *
 *                      DYN_DTRS_ALIAS          Create R/W aliases
 *                      DYN_DTRS_ERRORPROP      Propogate DTR worker errors
 *                      DYN_DTRS_ERROREXIT      Exit if DTR worker error
 *                      DYN_DTRS_IFSETMASK      Scan if these DTR bits set
 *                              (includes all DTR_... flags)
 *                      DYN_DTRS_IFCLEARMASK    Scan if these DTR bits clear
 *                              (includes all DTR_... flags)
 *
 *              lAddrMin        Minimum address of range to scan (inclusive)
 *              lAddrMax        Maximum address of range to scan (inclusive)
 *              pfnWorker       Worker routine to call.
 *              pdtlv           Local variables pointer.  Must not be NULL
 *                              if DYN_DTRS_ALIAS specified.
 *
 *      EXIT    ulong_t, standard error code
 *              NO_ERROR        (exact meaning depends on flags)
 *                      Traversal succeeded
 *
 *              ERROR_CODE
 *                      Traversal failed.
 *
 */

ulong_t dynDTRScan      (pdto, flCmd, lAddrMin, lAddrMax, pfnWorker, pdtlv)
dto_t   *pdto;
ulong_t flCmd;
ulong_t lAddrMin;
ulong_t lAddrMax;
ulong_t (*pfnWorker) (dto_t *, dtr_t *, dtlv_t *);
dtlv_t  *pdtlv;
{
        dtr_t   *pdtrCur;               // Current DTR in list
        dtr_t   *pdtrAlias;             // DTR that was aliased.
        dtr_t   *pdtrMax;               // Maximum DTR - just past last DTR
        ulong_t lfSet;                  // 'IFSET' bits for test
        ulong_t lfClear;                // 'IFCLEAR' bits for test
        ulong_t Page;                   // Page number for inner loop.
        ulong_t rcTemp;                 // Temporary Error code
        ulong_t rc = NO_ERROR;          // Returned Error code

        TKMayBlock();

        // The links within the DTO must be valid for dynDTRScan to work
        // properly.

        dynAssert (pdto-> dto_dti.dti_lfState & DTO_TRUSTED);

        lfSet = (flCmd & DYN_DTRS_IFSETMASK) >> DYN_DTRS_IFSETSHIFT;

        lfClear = (flCmd & DYN_DTRS_IFCLEARMASK) >> DYN_DTRS_IFCLEARSHIFT;

        // pdtrCur = Address of first DTR in the DTO.

        pdtrCur = (dtr_t *) ((ulong_t) (pdto + 1) + 4*pdto->dto_dth.dth_clVar);

        // pdtrMax = Address just beyond last DTR in the DTO.

        pdtrMax = (dtr_t *) ((ulong_t) pdto + sizeof (dti_t) +
            pdto->dto_dth.dth_cbDT);

        // No alias yet.

        Page = (ulong_t) -1L;

        // If aliases are required, get context information for dynDTRMapAlias.

        if (flCmd & DYN_DTRS_ALIAS) {
                if (pdto->dto_dti.dti_lfState & DTO_PROGRAM) {
                        if (NO_ERROR != (rc = (ulong_t) LDRGetContext (
                            (VMHOB) pdto->dto_dth.dth_hMTE,
                            &(pdtlv-> dtlv_hptda))))
                                return (rc);
                } else {
                        pdtlv-> dtlv_hptda = HOBNULL;
                }
        }

        // Traverse the selected records.

        do {
                dynAssert (pdtrCur < pdtrMax);

                // Skip this DTR if it has already been scanned, or if
                // it doesn't match the scan requirements.

                if (dynDTRSkipScan (pdtrCur, lAddrMin, lAddrMax, lfSet,
                                    lfClear)) {
                        pdtrCur = pdtrNextSeq(pdtrCur);
                        continue;
                }

                // If a new alias is required, we create it here.
                // We don't create a new alias if we already have one.

                if ((flCmd & DYN_DTRS_ALIAS) &&
                    (Page != pdtrCur-> dtr_Offset >> PAGESHIFT)) {

                        // Unmap an old alias, if any.

                        if (Page != (ulong_t) -1L) {
                                do {
                                        rcTemp = dynDTRUnMapAlias(pdto,
                                                        pdtrCur, pdtlv);
                                } while (rcTemp == ERROR_INTERRUPT);

                                dynAssert (rcTemp == NO_ERROR);
                        }

                        // Map a new alias.

                        if (NO_ERROR != (rcTemp = dynDTRMapAlias(pdto, pdtrCur,
                                                                pdtlv))) {
                                Page = (ulong_t) -1L;
                                rc = rcTemp;
                                break;
                        }

                        pdtrAlias = pdtrCur;
                        Page = pdtrCur-> dtr_Offset >> PAGESHIFT;
                }

                // Call the worker.

                rcTemp = (*pfnWorker) (pdto, pdtrCur, pdtlv);

                // If worker returned an error, propogate
                // the first error into rc, and possibly
                // exit the loop.

                if (rcTemp != NO_ERROR) {
                        if ((flCmd & DYN_DTRS_ERRORPROP) &&
                            (rc == NO_ERROR))
                                rc = rcTemp;
                        if (flCmd & DYN_DTRS_ERROREXIT)
                                break;
                }

                // Move to next dtr in the sequence.

                pdtrCur = pdtrNextSeq(pdtrCur);

                TKTCYield ();

        } while (pdtrCur != pdtrMax);

        // Unmap a leftover alias, if any.

        if (Page != (ulong_t) -1L) {
                do {
                        rcTemp = dynDTRUnMapAlias(pdto, pdtrAlias,
                                                pdtlv);
                } while (rcTemp == ERROR_INTERRUPT);

                dynAssert (rcTemp == NO_ERROR);
        }

        return (rc);
}

#endif  // RAS
