#define SCCSID  "@(#)dbgwp.c    6.9 92/03/05"

/***    DBGWP.C - Watchpoint and Debug Register Manager
 *
 *      THIS IS AN OS/2 VERSION 2.x SPECIFIC FILE
 *
 *      Version 1.7, for dbgwp.c, dosdebug, c.krnl
 *
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *      This file implements the management of the debug registers.
 *
 *      There are four uses for debug registers:  global watchpoints,
 *      local watchpoints, kernel debugger debug registers, and DPMI
 *      (VDM) watchpoints.
 *
 *      To implement context switching efficiently, debug registers are
 *      allocated as follows:
 *
 *      There are DR_COUNT available debug registers.  They are numbered
 *      0 to DR_COUNT-1.
 *
 *      Local watchpoints are assigned debug registers such that a process
 *      with N local watchpoints always has debug registers 0 to N-1 assigned
 *      to those watchpoints.  When local watchpoints are freed, assignments
 *      are shuffled as needed to preserve this scheme.
 *
 *      The kernel debugger is assigned debug registers starting with
 *      DR_COUNT-1 and proceeding down to 0.  If the kernel debugger has N
 *      debug registers allocated to it, they will be debug registers
 *      DR_COUNT-N to DR_COUNT-1.  To ensure this, the kernel debugger is
 *      required to free debug registers in LIFO order.
 *
 *      Global watchpoints are assigned debug registers starting with
 *      DR_COUNT-K-1 and proceeding down to 0, where K is the number of
 *      debug registers allocated to the kernel debugger.  If there are
 *      G global watchpoints, they will be assigned debug registers
 *      DR_COUNT-(K+G) to DR_COUNT-K-1.  Again, assignments are shuffled
 *      when global watchpoints or kernel debugger debug registers are freed
 *      to preserve this scheme.
 *
 *      DPMI watchpoints are completely disjoint from DosDebug watchpoints,
 *      and DPMI watchpoints are always local to a particular VDM process.
 *      Thus, when allocating a DPMI watchpoint, it is only necessary to
 *      insure that the VDM has allocated fewer than DR_COUNT - K registers.
 *      A DPMI process refers to a watchpoint by a handle which is a
 *      small integer in the range 0 to DR_COUNT - 1.  Watchpoints may
 *      be allocated and freed in any order.  So that the scheme of
 *      allocating local debug registers from the bottom up and kernel
 *      debug registers from the top down may be preserved, there is a
 *      mapping table that maps the DPMI watchpoint handle to the debug
 *      register that the watchpoint is currently using.  Thus, a DPMI
 *      watchpoint may use different debug registers over time, but its
 *      handle remains constant for its life.
 *
 *      Consider the following example allocation and freeing order:
 *
 *                                         Owner
 *                              DR0     DR1     DR2     DR3
 *                              ----    ----    ----    ----
 *                              free    free    free    free
 *      Allocate local WP       L1      free    free    free
 *      Allocate global WP      L1      free    free    G1
 *      Allocate kernel DR      L1      free    G1      K1
 *      Allocate local WP       L1      L2      G1      K1
 *      Free kernel DR          L1      L2      free    G1
 *      Free local WP1          L2      free    free    G1
 *
 *****************************************************************************
 *  Change                                                               Chg *
 *  Activity      Date    BY   Reason                                    Num *
 *  --------      ----    ---  -------                                   --- *
 *  PTR B727808 10/25/91  ARJ  Go pass end of array when schuffle WP     001 *
 *  PTR B727809 11/06/91  ARJ  Manipulate LE and GE bits for 386 chip    002 *
 *  PTR B729735 11/14/91  ARJ  Correct IFDEF STATEMENTS (No CHG NUM)         *
 *  PTR B731352 01/15/92  ARJ  Go pass end of array when checking WP     003 *
 *  PTR B732889 02/24/92  ARJ  Correct watchpoint trap "E" GELE problems 004 *
 *  PTR B732471 03/01/92  ARJ  B727808 and B731352 were removed by           *
 *                             B732889 which corrected this problem          *
 *  PTR B734041 03/05/92  ARJ  Array index should start at zero          005 *
 *  DEF   56453 10/28/92  WWM  Fix DPMI query DR6 problems               006 *
 *  DEF 149007  03/21/96  GK   Fix the DR0-DR4 not correspond to DR6         *
 *                             I found this bug while implementing 149007    *
 *****************************************************************************
 */

#define INCL_TYPES                      // for basedef.h typedefs
#include <basemac.h>
#include <basedef.h>
#define FNO_NUM         FNO_DBGWP_C
#include <fnotask.h>
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <vmowner.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 <proc.h>
#include <ldrxport.h>
#include <error.h>
#include <error2.h>
#include <kmode.h>
#include <bsetib.h>
#include <tklocal.h>
#include <kutil.h>
#include <mvdmexp.h>

#include <dbgapi.h>     /* (debug)      API definitions         */
#include <dbgex.h>      /* (debug)      External definitions    */
#include <dbgin.h>      /* (debug)      Internal definitions    */

#ifdef INCDEBUG

#ifdef DBGSTRICT
extern DBGBugBug;               /* DosDebug debug flag         */
#endif

/***LD  DBGcSysDR - count of systemwide debug register allocations
 *                  (includes DosDebug global watchpoints and kernel
 *                  debugger debug register allocations)
 */

int DBGcSysDR;

/***LD  DBGckDR - count of kernel debugger debug register allocations
 */

int DBGckDR;

/***LD  DBGapgwpcb - array of global watchpoint control block pointers
 */

WPCB_t *DBGapgwpcb[DR_COUNT];

/***LD  DBGaDRowner - array of debug register owners
 */

int DBGaDRowner[DR_COUNT];

/***LD  DBGlDR7global - DosDebug global watchpoint DR7 bit settings
 */

ulong_t DBGlDR7global;

/***LD  DBGmpclwpcbcptda - map of a count of local watchpoints to a count of
 *                         of process with that number of local watchpoints.
 *
 *      DBGmpclwpcbcptda[i] processes have at least i+1 local watchpoints.
 */

ushort_t DBGmpclwpcbcptda[DR_COUNT];

/***ED  DBGlmskDR7system - mask of systemwide valid DR7 bits (includes bits
 *                         in use by both DosDebug global watchpoints and
 *                         the kernel debugger, as well as processor reserved
 *                         bits).
 */

ulong_t DBGlmskDR7system = DR7_RESERVED;

/***ED  DBGlmskDR7reserved - mask of bits not used by DosDebug (includes bits
 *                           in use by the kernel debugger and processor
 *                           reserved bits).
 */

ulong_t DBGlmskDR7reserved = DR7_RESERVED;

/***ED  DBGpdcbCur - DCB for current local watchpoints
 */

DCB_t *DBGpdcbCur;


#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* swap DEBUG code */

/***LP  DbgWPAlloc - Allocate a Watchpoint
 *
 *      DbgWPAlloc Allocates a local or global watchpoint for a particular
 *      debuggee process.  DbgWPAlloc will only work on debugged processes.
 *
 *      This procedure is called from the context of the debugger process.
 *
 *      The Watchpoint Control block is allocated from the nonswappable
 *      portion of the kernel heap.
 *
 *      Entry:  ppwpcb    : Address to return the pointer to the WPCB
 *              pptda     : Pointer to the debuggee PTDA
 *              flScope   : DBG_W_Global or DBG_W_Local
 *
 *      Exit:   Return Value == 0
 *                 No Error
 *
 *              Return Value != 0
 *                 Error Allocating Watchpoint
 *
 *      Pseudocode:
 *      verify scope
 *      allocate watchpoint control block
 *      if (debuggee dead)
 *        error = ERROR_INVALID_PROCID
 *      else
 *        watchpoint ptda = debuggee ptda
 *        watchpoint state = NULL
 *        if (global scope && global debug register available)
 *          find free global watchpoint entry
 *          assing watchpoint DRindex
 *        else if (local scope && local debug register available)
 *          find free local watchpoint entry
 *          assign watchpoint DRindex
 *        else
 *          error = ERROR_NOT_ENOUGH_WATCHPOINTS
 *
 *      if (error)
 *        free watchpoint control block
 *      else
 *        assign watchpoint ID
 *        set DBGaDRowner
 */

int DbgWPAlloc (ppwpcb, pptda, flScope)
register WPCB_t **ppwpcb;               // buffer for new WPCB address
PTDA *pptda;                            // debuggee PTDA
ulong_t flScope;                                // watchpoint scope
{
    ulong_t rt;                         // return code
    DCB_t *pdcb;                        // debuggee DCB
    int iapwpcb = 0;                    // WPCB pointer array index

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPAlloc       ");   }
#endif

    if (flScope != DBG_W_Global && flScope != DBG_W_Local)
        return(ERROR_INVALID_PARAMETER);

    pdcb = pptda->ptda_pdcb;

    if ( (rt = VMAllocKHB(VM_HKH_PUB_RESRW, (ulong_t)sizeof(WPCB_t),
                          (VMHOB)DBGWPCBOWNER, NA, ppwpcb)
        ) != NO_ERROR)
                return(rt);

    // debuggee may have died during the alloc
    if (pdcb->flState & DBGDCBS_DBGEEDEAD)
        rt = ERROR_INVALID_PROCID;
    else {
        (*ppwpcb)->wpcb_pptda = pptda;
        (*ppwpcb)->wpcb_bfState = WPCBS_NULL;

        if (flScope == DBG_W_Global && DBGcSysDR < DR_COUNT &&
            DBGmpclwpcbcptda[(DR_COUNT - 1) - DBGcSysDR] == 0) {
        // To allocate a global, there must be less than DR_COUNT globals
        // already allocated and no process may have DR_COUNT - DBGcSysDR
        // locals allocated

            // There must be free slot in the global array;  find it
            while (DBGapgwpcb[iapwpcb] != NULL) iapwpcb++;

            dbgAssert(iapwpcb < DR_COUNT);

            DBGapgwpcb[iapwpcb] = *ppwpcb;

            // Assign a debug register to this watchpoint
            DBGcSysDR++;
            (*ppwpcb)->wpcb_DRindex = (char) (DR_COUNT - DBGcSysDR);

        } else if (flScope == DBG_W_Local &&
                   DBGcSysDR + pdcb->dcb_clwpcb < DR_COUNT) {
        // To allocate a local, the sum of globals and this process' locals
        // must be less than DR_COUNT

            // There must be a free entry in the local array; find it
            while (pdcb->dcb_aplwpcb[iapwpcb] != NULL) iapwpcb++;

            dbgAssert(iapwpcb < DR_COUNT);

            pdcb->dcb_aplwpcb[iapwpcb] = *ppwpcb;

            // Assign a debug register to this watchpoint
            (*ppwpcb)->wpcb_DRindex = (char) pdcb->dcb_clwpcb;

            // Keep track of how many locals each debuggee process has
            DBGmpclwpcbcptda[pdcb->dcb_clwpcb]++;
            pdcb->dcb_clwpcb++;
        } else
            rt = ERROR_NOT_ENOUGH_WATCHPOINTS;
    }

    if (rt != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_RESRW,*ppwpcb);
        *ppwpcb = NULL;
    } else {
        (*ppwpcb)->wpcb_bWid = (char) DBGWID_CREATE(flScope, iapwpcb);

        //  When allocating a global WP, the debug register must be free.
        //  When allocating a local WP, the debug register must either be
        //  be free or already in use as a WP.

        dbgAssert(DBGaDRowner[(*ppwpcb)->wpcb_DRindex] == DBGDR_FREE ||
                  (flScope == DBG_W_Local &&
                   DBGaDRowner[(*ppwpcb)->wpcb_DRindex] == DBGDR_DBG));

        DBGaDRowner[(*ppwpcb)->wpcb_DRindex] = DBGDR_DBG;
    }

    return (rt);
}

/***LP  dbgGlobalWPFree - Free a global watchpoint
 *
 *      dbgGlobalWPFree frees a previously allocated global watchpoint,
 *      shuffling debug register assignments as needed.
 *
 *      Entry:  iapgwpcb: global watchpoint array index
 *
 *      Exit:   Return Value == 0
 *                 No error
 *
 *              Return Value != 0
 *                 Error freeing watchpoint (not previously allocated)
 *
 *      Uses:   DR0, DR1, DR2, DR3, DR7
 *
 *      Pseudocode:
 *      if (invalid watchpoint index || watchpoint not owned by debuggee)
 *        return(ERROR_INVALID_PARAMETER)
 *
 *      watchpoint array entry = NULL
 *      if (watchpoint index is lowest global watchpoint index)
 *        // no shuffle needed
 *        if (watchpoint is armed)
 *          cli
 *          watchpoint state = NULL
 *          turn off watchpoint associated bits in DBGlmskDR7system
 *          turn off watchpoint associated bits in DBGlDR7global
 *          DbgValidateDR7
 *          sti
 *      else // lowest global watchpoint must be shuffled up
 *        find global watchpoint with lowest debug register index
 *        if (either watchpoint is armed)
 *          cli
 *          turn off lowest watchpoint associated bits in DBGlmskDR7system
 *          turn off both watchpoint associated bits in DBGlDR7global
 *          lowest watchpoint DR index = DR index of watchpoint being freed
 *          if (watchpoint being moved is armed)
 *            turn on watchpoint bits in DBGlDR7global
 *            DbgSetDRladdr(moved watchpoint)
 *          DbgValidateDR7
 *          sti
 *        else
 *          swap watchpoint DR indices
 *
 *      clear DBGaDRowner entry
 *      decrement count of sytem wide in use debug registers
 *      free watchpoint control block
 */

int
dbgGlobalWPFree(PTDA *pptda,            // debuggee PTDA
                int iapgwpcb)           // global watchpoint array index
{
    register WPCB_t *pwpcbFree; // watchpoint being freed

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgGlobalWPFree  ");   }
#endif

    if ((pwpcbFree = DBGapgwpcb[iapgwpcb]) == NULL ||
        pwpcbFree->wpcb_pptda != pptda)
        return(ERROR_INVALID_PARAMETER);

    DBGapgwpcb[iapgwpcb] = NULL;

    if (pwpcbFree->wpcb_DRindex == (uchar_t) (DR_COUNT - DBGcSysDR)) {
        // watchpoint being freed has lowest global debug register assigned
        // to it, so no shuffle is required

        if (pwpcbFree->wpcb_bfState == WPCBS_ARMED) {
            _disable();

            pwpcbFree->wpcb_bfState = WPCBS_NULL;
            DBGlmskDR7system &= ~DBGDR7_mLENRWGLn(pwpcbFree->wpcb_DRindex);
            DBGlDR7global &= ~DBGDR7_mLENRWGLn(pwpcbFree->wpcb_DRindex);
            DbgValidateDR7();

            _enable();
        }
    } else {
        register WPCB_t *pwpcbMove;     // watchpoint being shuffled
        int iDR;                        // DR index of watchpoint to shuffle

        // shuffle a global watchpoint to preserve ordering

        iDR = DR_COUNT - DBGcSysDR;
        iapgwpcb = 0;
        while ((pwpcbMove = DBGapgwpcb[iapgwpcb++]) == NULL ||
               pwpcbMove->wpcb_DRindex != (uchar_t) iDR);

        if (pwpcbMove->wpcb_bfState == WPCBS_ARMED ||
            pwpcbFree->wpcb_bfState == WPCBS_ARMED) {
            _disable();

            DBGlmskDR7system &= ~DBGDR7_mLENRWGLn(iDR);
            DBGlDR7global &= ~DBGDR7_mLENRWGLn(pwpcbFree->wpcb_DRindex);
            DBGlDR7global &= ~DBGDR7_mLENRWGLn(iDR);

            pwpcbMove->wpcb_DRindex = pwpcbFree->wpcb_DRindex;
            pwpcbFree->wpcb_DRindex = (char) iDR;

            if (pwpcbMove->wpcb_bfState == WPCBS_ARMED) {
                DBGlDR7global |= DBGDR7_LENRWn(pwpcbMove->wpcb_fsLenRW,
                                               pwpcbMove->wpcb_DRindex) |
                                 DBGDR7_GLn(DR7_G0, pwpcbMove->wpcb_DRindex);
                DbgSetDRladdr(pwpcbMove->wpcb_DRindex, pwpcbMove->wpcb_laddr);
            }

            DbgValidateDR7();

            _enable();
        } else {
            pwpcbMove->wpcb_DRindex = pwpcbFree->wpcb_DRindex;
            pwpcbFree->wpcb_DRindex = (char) iDR;
        }
    }

    DbgSetDRladdr(pwpcbFree->wpcb_DRindex, NULL);            /*        A004 */
    DBGaDRowner[pwpcbFree->wpcb_DRindex] = DBGDR_FREE;
    DBGcSysDR--;

/* If we are not waiting for any Global WP turn off GE bit for 386 chip A002*/
        if ( DBGcSysDR == 0)                                     /*           A002*/
  {                                                        /*           A002*/
           _disable();                                           /*           A002*/
           DBGlDR7global &= ~DR7_GE;                             /*           A002*/
     DbgDR7LeGe();                                         /*           A002*/
           _enable();                                            /*           A002*/
  }                                                        /*           A002*/

    VMFreeKHB(VM_HKH_PUB_RESRW,pwpcbFree);
    return(NO_ERROR);
}

/***LP  dbgLocalWPFree
 *
 *      dbgLocalWPFree frees a previously allocated local watchpoint,
 *      shuffling debug register assignments as needed.
 *
 *      Entry:  iaplwpcb: local watchpoint array index
 *
 *      Exit:   Return Value == 0
 *                 No error
 *
 *              Return Value != 0
 *                 Error freeing watchpoint (not previously allocated)
 *
 *      Uses:   DR0, DR1, DR2, DR3, DR7
 *
 *      Pseudocode:
 *      if (invalid watchpoint index)
 *        return(ERROR_INVALID_PARAMETER)
 *
 *      cli
 *      turn off watchpoint associated bits in pdcb->dcb_lDR7local
 *      if (this process is the debuggee && watchpoint is armed)
 *        DbgValidateDR7
 *      sti
 *
 *      watchpoint array entry = NULL
 *      decrement count of local watchpoints
 *
 *      if (wp debug register is not highest local wp debug register)
 *        if (this process is the debuggee)
 *          cli
 *
 *        find local watchpoint with highest debug register index
 *        set linear address entry for moving watchpoint
 *        set new debug register index for moving watchpoint
 *
 *        if (watchpoint being moved is armed)
 *          turn off bits associated with moving watchpoints old position in
 *            pdcb->dcb_lDR7local
 *          turn on bits associated with moving watchpoints new position in
 *            pdcb->dcb_lDR7local
 *          if (this process is the debuggee)
 *            DbgSetDRladdr(new DR index, laddr)
 *            DbgValidateDR7()
 *
 *        sti
 *
 *      free watchpoint control block
 */

int
dbgLocalWPFree(register DCB_t *pdcb,    // pointer to debuggee DCB
               int iaplwpcb)            // local watchpoint array index
{
    WPCB_t *pwpcbFree;          // watchpoint being freed

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgLocalWPFree   ");   }
#endif

    if ((pwpcbFree = pdcb->dcb_aplwpcb[iaplwpcb]) == NULL)
        return(ERROR_INVALID_PARAMETER);

    _disable();

    pdcb->dcb_lDR7local &= ~DBGDR7_mLENRWGLn(pwpcbFree->wpcb_DRindex);

    DbgSetDRladdr(pwpcbFree->wpcb_DRindex, NULL);            /*        A004 */

    if (pdcb == DBGpdcbCur && pwpcbFree->wpcb_bfState == WPCBS_ARMED)
        DbgValidateDR7();

    _enable();

    pdcb->dcb_aplwpcb[iaplwpcb] = NULL;
    pdcb->dcb_clwpcb--;

    if (--DBGmpclwpcbcptda[pdcb->dcb_clwpcb] == 0)
        DBGaDRowner[pdcb->dcb_clwpcb] = DBGDR_FREE;

    if (pdcb->dcb_clwpcb != pwpcbFree->wpcb_DRindex)
    {
        register WPCB_t *pwpcbMove;     // watchpoint being shuffled
        int iDR;                        // DR index of watchpoint to shuffle

        // shuffle locals to preserve ordering scheme
        if (pwpcbFree->wpcb_pptda == pPTDACur)
            _disable();

        iDR = pdcb->dcb_clwpcb;

        // find watchpoint to shuffle
  iaplwpcb = 0;                                          /*            A005 */
        while ( ((pwpcbMove = pdcb->dcb_aplwpcb[iaplwpcb++]) == NULL)  ||
                 pwpcbMove->wpcb_DRindex != (uchar_t) iDR);

        pdcb->dcb_alDRladdr[pwpcbFree->wpcb_DRindex] =
            pdcb->dcb_alDRladdr[pwpcbMove->wpcb_DRindex];

  DbgSetDRladdr(pwpcbMove->wpcb_DRindex, NULL);               /*        A004 */
  {                                                           /*        A004 */
   ulong_t TempDBGDR7_mLENRWGLn;                              /*        A004 */
   TempDBGDR7_mLENRWGLn =   DBGDR7_mLENRWGLn(pwpcbMove->wpcb_DRindex);/*A004 */
   TempDBGDR7_mLENRWGLn =  ~TempDBGDR7_mLENRWGLn;             /*        A004 */
         pdcb->dcb_lDR7local &= TempDBGDR7_mLENRWGLn;               /*        A004 */
  }                                                           /*        A004 */

        pwpcbMove->wpcb_DRindex = pwpcbFree->wpcb_DRindex;

        if (pwpcbMove->wpcb_bfState == WPCBS_ARMED) {
   ulong_t len;                                               /*        A004 */
   ulong_t index;                                             /*        A004 */
   ulong_t TempDBGDR7_mLENRWGLn;                              /*        A004 */
   ulong_t TempDBGDR7_GLn;                                    /*        A004 */
   ulong_t TempDBGDR7_LENRWn;                                 /*        A004 */
   ulong_t xxx;                                               /*        A004 */

   TempDBGDR7_mLENRWGLn =   DBGDR7_mLENRWGLn(pwpcbMove->wpcb_DRindex);/* A004*/
   TempDBGDR7_mLENRWGLn =  ~TempDBGDR7_mLENRWGLn;             /*        A004 */
         pdcb->dcb_lDR7local &= TempDBGDR7_mLENRWGLn;               /*        A004 */

   len =   pwpcbMove->wpcb_fsLenRW;                           /*        A004 */
   index = pwpcbMove->wpcb_DRindex;                           /*        A004 */
   TempDBGDR7_GLn    =  DBGDR7_GLn(DR7_L0,index);             /*        A004 */
   TempDBGDR7_LENRWn =  DBGDR7_LENRWn(len, index );           /*        A004 */
   xxx = TempDBGDR7_LENRWn | TempDBGDR7_GLn;                  /*        A004 */
         pdcb->dcb_lDR7local |= xxx;                                /*        A004 */

            if (pwpcbMove->wpcb_pptda == pPTDACur) {
                DbgSetDRladdr(pwpcbMove->wpcb_DRindex, pwpcbMove->wpcb_laddr);
                DbgValidateDR7();
            }
        }
        _enable();
    }

        _disable();                                              /*           A004*/

/* If we are not waiting for any Local WP turn off LE bit for 386 chip  A002*/
        if ( pdcb->dcb_clwpcb == 0 )                             /*           A002*/
  {                                                        /*           A002*/
           pdcb->dcb_lDR7local &= ~DR7_LE;                       /*           A002*/
     DbgDR7LeGe();                                         /*           A002*/
  }                                                        /*           A002*/

         DbgValidateDR7();                                       /*           A004*/

        _enable();                                               /*           A004*/

    VMFreeKHB(VM_HKH_PUB_RESRW, pwpcbFree);
    return(NO_ERROR);
}

/***LP  DbgWPFree - Free a Watchpoint
 *
 *      DbgWPFree frees a previously allocated watchpoint.
 *
 *      Entry:  pptda   : Pointer to the debuggee PTDA
 *              lWid    : Watchpoint ID number
 *
 *      Exit:   Return Value == 0
 *                 No error
 *
 *              Return Value != 0
 *                 Error freeing watchpoint (not previously allocated)
 *
 *      Uses:   DR0, DR1, DR2, DR3, DR7
 *
 *      Pseudocode:
 *      if (invalid watchpoint ID)
 *        return(ERROR_INVALID_PARAMETER)
 *      if (watchpoint is global)
 *        return(dbgGlobalWPFree(wp ID))
 *      else
 *        return(dbgLocalWPFree(wp ID))
 */

int DbgWPFree (pptda, lWid)
PTDA *pptda;                    // debuggee PTDA
ulong_t lWid;                   // watchpoint ID
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPFree        ");   }
#endif

    if (!DBGWID_ISVALID(lWid))
        return(ERROR_INVALID_PARAMETER);

    if (DBGWID_ISGLOBAL(lWid))
        return(dbgGlobalWPFree(pptda, DBGWID_INDEX(lWid)));

    return(dbgLocalWPFree(pptda->ptda_pdcb, DBGWID_INDEX(lWid)));
}

/***LP  DbgWPSet - Set a Watchpoint to an address
 *
 *      DbgWPSet sets a watchpoint to an address, and then arms
 *      the watchpoint.
 *
 *      DbgWPSet checks watchpoint alignment, type, size, and
 *      underlying memory validity before setting the watchpoint.
 *
 *      Entry:  pwpcb   : Pointer to Watchpoint Control Block to use
 *              pchPoint: Desired Watchpoint Address (Debuggee context)
 *              cchLen  : Desired Watchpoint length (1, 2, or 4)
 *              lType   : Desired Watchpoint Type (DBG_W_Execute,
 *                         DBG_W_Write, DBG_W_IOBreadk, or DBG_W_ReadWrite)			; 152754
 *
 *      Exit:   Return Value == 0
 *                 No error
 *
 *              Return Value != 0
 *                 Error setting watchpoint
 *
 *      Uses:   DR7
 *
 *      Pseudocode:
 *      verify cchLen
 *      verify laddr is cchLen aligned
 *      verify laddr is not in system arena
 *      set wpcb_fsLenRW bits based on flType and cchLen
 *      set wpcb_laddr
 *      if (global watchpoint)
 *        cli
 *        turn on bits associated with watchpoint in DBGlDR7global
 *        turn on bits associated with watchpoint in DBGlmskDR7system
 *        DbgSetDRladdr(watchpoint debug register, watchpoint laddr)
 *        DbgValidateDR7
 *        watchpoint state = ARMED
 *        sti
 *      else // local watchpoint
 *        turn on bits associated with watchpoint in pdcb->dcb_lDR7local
 *        set dcb_alDRladdr[watchpoint DR index] = laddr
 *        watchpoint state = ARMED
 */

int DbgWPSet (pwpcb, laddr, cchLen, flType)
WPCB_t *pwpcb;                  // watchpoint control block
ulong_t laddr;                  // watchpoint linear address
ulong_t cchLen;                 // watchpoint length
ulong_t flType;                 // watchpoint type
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPSet         ");   }
#endif

    dbgAssert(pwpcb->wpcb_bfState == WPCBS_NULL);

    if (cchLen != 1 && cchLen != 2 && cchLen != 4)
        return(ERROR_INVALID_PARAMETER);

    // if (cchLen == 1), laddr must be byte aligned
    // if (cchLen == 2), laddr must be word aligned
    // if (cchLen == 4), laddr must be dword aligned
    if (laddr & (cchLen - 1))
        return(ERROR_INVALID_PARAMETER);

    if (VMSysArena(laddr))
        return(ERROR_INVALID_ADDRESS);

    switch(flType) {
        case DBG_W_Write:
            pwpcb->wpcb_fsLenRW = DR7_WRITE;
            break;
		  case DBG_W_IOBreak:                                          // 152754
            pwpcb->wpcb_fsLenRW = DR7_IO;
            break;
        case DBG_W_ReadWrite:
            pwpcb->wpcb_fsLenRW = DR7_READWRITE;
            break;
        case DBG_W_Execute:
            if (cchLen == 1) {
                pwpcb->wpcb_fsLenRW = DR7_EXECUTE;
                break;
            }
            // fall through
        default:
            return(ERROR_INVALID_PARAMETER);
    }

    pwpcb->wpcb_fsLenRW |= (cchLen-1) << 2;
    pwpcb->wpcb_laddr = laddr;

    if (DBGWID_ISGLOBAL(pwpcb->wpcb_bWid)) {
        _disable();

        DBGlDR7global |= DBGDR7_LENRWn(pwpcb->wpcb_fsLenRW,
                                     pwpcb->wpcb_DRindex) |
                         DBGDR7_GLn(DR7_G0, pwpcb->wpcb_DRindex);

  /* Enable exact data BreakPoint mataching for Global WP for 386 chip   A002*/
        DBGlDR7global |= DR7_GE;                                 /*            A002*/

        DBGlmskDR7system |= DBGDR7_mLENRWGLn(pwpcb->wpcb_DRindex);
        DbgSetDRladdr(pwpcb->wpcb_DRindex,pwpcb->wpcb_laddr);

        DbgValidateDR7();
        pwpcb->wpcb_bfState = WPCBS_ARMED;

        _enable();
    } else {
        pwpcb->wpcb_pptda->ptda_pdcb->dcb_lDR7local |=
            DBGDR7_LENRWn(pwpcb->wpcb_fsLenRW, pwpcb->wpcb_DRindex) |
            DBGDR7_GLn(DR7_L0, pwpcb->wpcb_DRindex);

  /* Enable exact data BreakPoint mataching for Local WP for 386 chip    A002*/
        pwpcb->wpcb_pptda->ptda_pdcb->dcb_lDR7local  |= DR7_LE;  /*            A002*/

        pwpcb->wpcb_pptda->ptda_pdcb->dcb_alDRladdr[pwpcb->wpcb_DRindex] =
            laddr;
        pwpcb->wpcb_bfState = WPCBS_ARMED;
    }

    return(NO_ERROR);
}

/***EP  DbgWPHit - Route a watchpoint hit to a debuggee
 *
 *      DbgWPHit is called when a debug register hit intended for DosDebug
 *      occurs.  DbgWPHit may be called at interrupt time.  It routes the
 *      watchpoint hit to the owning debuggee and decides if the debug
 *      register should be disarmed and/or if the thread should enter/exit
 *      kernel mode.
 *
 *      For now, all debug register hits owned by DosDebug are routed to a
 *      debuggee, so they are always disarmed.  If watchpoints are ever
 *      supported for V86 mode processes, then spurious hits (protect mode
 *      process hitting a V86 watchpoint or vice versa) would be ignored
 *      and the debug register would remain armed.
 *
 *      Since this routine may be called at interrupt time, it must be not
 *      be swappable.  However, it need only be resident if DosDebug
 *      watchpoints are in use.
 *
 *      Note:  This routine could be changed to work with interrupts on -
 *      just watch for critical sections when modifying DbglDR7global,
 *      DbglmskDR7system, and pdcb->dcb_lDR7local.
 *
 *      Entry:  iDR = hit debug register index
 *              Interrupts off
 *
 *      Exit:   Interrupts off
 *
 *              Low byte of return value != 0
 *               => Disarm debug register
 *
 *              Low byte of return value == 0
 *               => Leave debug register armed
 *
 *
 *              High byte of low word of return value != 0
 *               => Enter/exit kernel mode to pick up events
 *
 *              High byte of low word of return value == 0
 *               => Don't need to enter/exit kernel mode
 *
 *      Pseudocode:
 *      lDisarm = TRUE
 *      lEnterKmode = TRUE
 *
 *      if (interrupt time || v86 process)
 *        return(0)
 *
 *      if (local watchpoint)
 *        turn off bits associated with watchpoint in pdcb->dcb_lDR7local
 *      else // global watchpoint
 *        turn off bits associated with watchpoint in DBGlmskDR7system
 *        turn off bits associated with watchpoint in DBGlDR7global
 *
 *      watchpoint state = HIT
 *      set TK_FF_TRC on debuggee process
 *      set DBGP_WPHIT in debuggee process
 *
 *      if (task time)
 *        if (current process is not the watchpoint owner)
 *          lEnterKmode = 0 // don't have to enter kmode
 *
 *        wpcb_MTE = current ptda handle
 *        wpcb_Pid = current process' Pid
 *        wpcb_Tid = current process' Tid
 *        wpcb_laddr = laddr
 *      else
 *        zero wpcb_MTE, _Pid, _Tid, _laddr
 *
 *      return(lDisarm | lEnterKmode)
 */

int DbgWPHit (iDR, laddr)
int iDR;                        // hit debug register index
ulong_t laddr;                  // hit linear address
{
    register WPCB_t *pwpcb = NULL;
    ulong_t lDisarm = 0xffL;
    ulong_t lEnterKmode = 0xff00L;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPHit         ");   }
#endif

    if (InterruptLevel != -1 && pPTDACur->ptda_ProcType == PT_REALMODE)
        return(0);                              // ignore realmode hits

    //  Since local WPs are allocated contiguous debug registers starting
    //  with debug register 0, we can determine if this is a local WP hit
    //  by comparing the debug register index and the number of current
    //  local WPs.
    //
    //  DBGpdcbCur will be NULL if this a non-debugged process which has
    //  hit a debugged process' global WP.

    if ( (DBGpdcbCur != NULL) && ((ulong_t)iDR < DBGpdcbCur->dcb_clwpcb) ) {
        register int iapwpcb = 0;               // local watchpoint

        while   ( ( (pwpcb = DBGpdcbCur->dcb_aplwpcb[iapwpcb++]) == NULL) ||
                (pwpcb->wpcb_DRindex != (uchar_t) iDR) );

        pwpcb->wpcb_pptda->ptda_pdcb->dcb_lDR7local &= ~DBGDR7_mLENRWGLn(iDR);
    } else {                                    // global watchpoint
        register int iapwpcb = 0;

        while   ( ( (pwpcb = DBGapgwpcb[iapwpcb++]) == NULL) ||
                (pwpcb->wpcb_DRindex != (uchar_t) iDR) );

        DBGlmskDR7system &= ~DBGDR7_mLENRWGLn(iDR);
        DBGlDR7global &= ~DBGDR7_mLENRWGLn(iDR);
    }

    pwpcb->wpcb_bfState = WPCBS_HIT;
    pwpcb->wpcb_pptda->ptda_ForcedActions |= TK_FF_TRC;
    pwpcb->wpcb_pptda->ptda_flDbg |= DBGP_WPHIT;

    if (InterruptLevel == -1) {                 // Task time
        if (pPTDACur != pwpcb->wpcb_pptda)
            lEnterKmode = 0L;

        pwpcb->wpcb_MTE = pPTDACur->ptda_handle;
        pwpcb->wpcb_Pid = pPTDACur->ptda_Pid;
        pwpcb->wpcb_Tid = pTCBCur->TCBOrdinal;
        pwpcb->wpcb_laddr = laddr;
    } else {                                    // Interrupt time
        pwpcb->wpcb_MTE = pwpcb->wpcb_Pid = pwpcb->wpcb_Tid = 0;
        pwpcb->wpcb_laddr = 0L;
    }

    return(lDisarm | lEnterKmode);
}

#pragma code_seg()                              /* end swap code */

#ifdef PMDEBUG

/***EP  DbgDRAlloc - Request to lock a debug register
 *
 *      DbgDRAlloc is used to reserve a debug register, so that
 *      it will not interfere with the normal operation of user-level
 *      debuggers, such as CVP.
 *
 *      DbgDRAlloc does not allocate a watchpoint control block.
 *      Rather, it reserves a debug register for direct use, and
 *      the debug register manager will not modify any values
 *      associated with that register until DbgDRClear is
 *      called on that debug register.
 *
 *      DbgDRAlloc may be called at interrupt time by the kernel
 *      debugger - it must always be resident.
 *
 *      The success of DbgDRAlloc also implies that there exists
 *      an available slot for allocating a local or global watchpoint
 *      by the current process.
 *
 *      The kernel debugger should not allocate a debug register
 *      watchpoint while single stepping this procedure.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              Return Value == -1
 *                      Cannot reserve a debug register.
 *
 *              Return Value == 0, 1, 2, 3
 *                      Debug register reserved.
 *
 *      Pseudocode:
 *      if (debug register unavailable)
 *        return(-1)
 *
 *      if (there are global watchpoints)
 *        find global watchpoint with highest debug register index
 *        assign new debug register index to global watchpoint
 *        if (watchpoint is armed)
 *          turn on bits associated with new DR index in DBGlmskDR7system
 *          turn on bits associated with new DR index in DBGlmskDR7global
 *          turn off bits associated with old DR index in DBGlmskDR7global
 *          DBGaDRowner[new DR index] = DosDebug
 *      else
 *        turn on bits associated with DR index in DBGlmskDR7system
 *
 *      DBGaDRowner[debug register] = kernel debugger
 *      turn on bits associated with debug register in DBGlmskDR7reserved
 *      increment count of kernel debugger debug registers in use
 *      increment count of global debug registers in use
 *      return(debug register index)
 */

int DbgDRAlloc()
{
    int ikDR;           // kernel debugger debug register index

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgDRAlloc       ");   }
#endif

    if (DBGcSysDR != DR_COUNT &&
        DBGmpclwpcbcptda[ikDR = (DR_COUNT - 1 - DBGcSysDR)] == 0) {

        dbgAssert(DBGaDRowner[ikDR] == DBGDR_FREE);

        if (DBGcSysDR > DBGckDR) {      // TRUE iff there are global WPs
            WPCB_t *pwpcb;
            int iapgwpcb = 0,
                igDR = ikDR;

            ikDR = DR_COUNT - 1 - DBGckDR;

            while ( ( (pwpcb = DBGapgwpcb[iapgwpcb++]) == NULL )  ||
                          (pwpcb->wpcb_DRindex != (uchar_t) ikDR) );

            pwpcb->wpcb_DRindex = (char) igDR;

            if (pwpcb->wpcb_bfState == WPCBS_ARMED) {
                DBGlmskDR7system |= DBGDR7_mLENRWGLn(pwpcb->wpcb_DRindex);
                DBGlDR7global |= DBGDR7_LENRWn(pwpcb->wpcb_fsLenRW,
                                               pwpcb->wpcb_DRindex) |
                                 DBGDR7_GLn(DR7_G0, pwpcb->wpcb_DRindex);
                DBGlDR7global &= ~DBGDR7_mLENRWGLn(ikDR);
            }

            DBGaDRowner[pwpcb->wpcb_DRindex] = DBGDR_DBG;
        } else
            DBGlmskDR7system |= DBGDR7_mLENRWGLn(ikDR);

        DBGaDRowner[ikDR] = DBGDR_LOCKED;
        DBGlmskDR7reserved |= DBGDR7_mLENRWGLn(ikDR);
        DBGckDR++;
        DBGcSysDR++;
        return(ikDR);
    }

    return(-1);
}

/***EP  DbgDRFree - Unlock a locked Debug Register
 *
 *      DbgDRFree is called by to unlock a debug register
 *      previously reserved via DbgDRAlloc.
 *
 *      DbgDRFree may be called at interrupt time by the kernel
 *      debugger - it must always be resident.
 *
 *      The kernel debugger should not allocate a debug register
 *      watchpoint while single stepping this procedure.
 *
 *      ENTRY
 *              lDRid   : Debug register id (0,1,2,3)
 *
 *      EXIT
 *              No Return Value
 *
 *      Pseudocode:
 *      if (kernel debugger does not own debug register)
 *        return
 *
 *      if (kernel debugger not freeing LIFO)
 *        return ERROR_INVALID_PARAMETER
 *
 *      turn off bits associated with debug register in DBGlmskDR7reserved
 *
 *      if (there are global watchpoints)
 *        find global watchpoint with lowest debug register index
 *        watchpoint debug register = debug register being freed
 *        if (watchpoint is ARMED)
 *          turn off bits associated with old DR index in DBGlDR7global
 *          turn on bits associated with new DR index in DBGlDR7global
 *
 *        DBGaDRowner[debug register] = DosDebug
 *
 *      turn off bits associated freed DR index in DBGlDR7global
 *      turn off bits associated freed DR index in DBGlmskDR7system
 *      DBGaDRowner[freed debug register] = FREE
 *      decrement count of kernel debugger debug registers in use
 *      decrement count of global debug registers in use
 */

int DbgDRFree(ikDR)
ulong_t ikDR;                   // index of debug register to free
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgDRFree        ");   }
#endif

    if (DBGaDRowner[ikDR] != DBGDR_LOCKED)
        // must have been allocated in real mode
        return(NO_ERROR);

    if (ikDR != 0 && DBGaDRowner[ikDR-1] == DBGDR_LOCKED)
        // kernel debugger must free LIFO
        return(ERROR_INVALID_PARAMETER);

    DBGlmskDR7reserved &= ~DBGDR7_mLENRWGLn(ikDR);

    if (DBGcSysDR != DBGckDR) {
        int igDR = DR_COUNT - DBGcSysDR;
        int iapgwpcb = 0;
        WPCB_t *pwpcb;

        // shuffle a global to preserve ordering scheme
        while ( ( (pwpcb = DBGapgwpcb[iapgwpcb++]) == NULL) ||
                (pwpcb->wpcb_DRindex != (uchar_t) igDR) );

        pwpcb->wpcb_DRindex = (char) ikDR;

        if (pwpcb->wpcb_bfState == WPCBS_ARMED) {
            DBGlDR7global &= ~DBGDR7_mLENRWGLn(ikDR);
            DBGlDR7global |= DBGDR7_LENRWn(pwpcb->wpcb_fsLenRW, ikDR) |
                             DBGDR7_GLn(DR7_G0, ikDR);
            ikDR = igDR;
        }

        DBGaDRowner[pwpcb->wpcb_DRindex] = DBGDR_DBG;
    }

    DBGlDR7global &= ~DBGDR7_mLENRWGLn(ikDR);
    DBGlmskDR7system &= ~DBGDR7_mLENRWGLn(ikDR);
    DBGaDRowner[ikDR] = DBGDR_FREE;
    DBGckDR--;
    DBGcSysDR--;
    return(NO_ERROR);
}

#endif

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

/***EP  DBGWPAllocVDM - allocate a watchpoint for a VDM
 *
 *      This function allocates a local watchpoint for the current
 *      VDM process.
 *
 *      DBGWPAllocVDM (laddr, cb, type, piwp)
 *
 *      ENTRY   laddr           - linear address of watchpoint
 *              cb              - size of watchpoint (1, 2, or 4 bytes)
 *              type            - type of watchpoint
 *              piwp            - place to return watchpoint index
 *      RETURN  int             - return code (NO_ERROR if successful)
 */

int
DBGWPAllocVDM(ulong_t laddr,ulong_t cb,ulong_t type,ulong_t *piwp)
{
    ulong_t idr;                        /* Debug register index */
    ulong_t iwp;                        /* Watchpoint index */
    dbgvdm_t *pdbgv;                    /* Pointer to watchpoint info */
    int rc;                             /* Return code */

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPAllocVDM    ");   }
#endif

    --cb;                               /* cb must now be 0, 1, or 3 */

    /*
     *  Validate parameters:
     *  (1) laddr must not be in system space
     *  (2) cb must be 1, 2, or 4 bytes (represented by 0, 1, and 3)
     *  (3) laddr and size must be aligned properly (i.e., no word
     *      watchpoints at odd addresses and no dword watchpoints
     *      except at dword addresses).
     */
    if (VMSysArena(laddr) ||
      (cb != 0 && cb != 1 && cb != 3) ||
      (laddr & cb) != 0 ||
      (type != DBG_W_Execute && type != DBG_W_IOBreak && type != DBG_W_Write && type != DBG_W_ReadWrite))	// 152754
        return(ERROR_INVALID_PARAMETER);

    /*
     *  See if there is a watchpoint available.
     */
    if ((pdbgv = (dbgvdm_t *) pPTDACur->ptda_pdcb) == NULL) {
                                        /* If no control block yet */

    if ( (rc = VMAllocKHB(VM_HKH_PUB_RESRW, (ulong_t)sizeof(dbgvdm_t),
                          (VMHOB)DBGWPCBOWNER, (ulong_t)NA,
                          SSToDS(&pdbgv))
         ) != NO_ERROR)
                  return(rc);   /* Allocate a control block */

        pdbgv->dbgv_cdr = 0;            /* No registers in use */
        pdbgv->dbgv_dr6 = 0;            /* Initialize */
        pdbgv->dbgv_dr7 = 0;            /* Initialize */
        memset(pdbgv->dbgv_mpiwpidr,'\xFF',DR_COUNT);
                                        /* No mappings yet */
        pPTDACur->ptda_pdcb = (struct DCB_s *)pdbgv;/*setup watchpoint info for task
  */
    }
    if ( ((ulong_t) idr = pdbgv->dbgv_cdr) >= ((ulong_t)(DR_COUNT - DBGckDR)) )
        return(ERROR_NOT_ENOUGH_WATCHPOINTS);

    /*
     *  Find a free watchpoint number.
     */
    for (iwp = 0; iwp < DR_COUNT; ++iwp) {
                                        /* Loop to find free slot */
        if (pdbgv->dbgv_mpiwpidr[iwp] == (uchar_t)'\xFF') {
                                        /* If the slot is available */
            pdbgv->dbgv_mpiwpidr[iwp] = (uchar_t) idr;
                                        /* Tell which register it points to */
            break;                      /* Exit loop */
        }
    }
    dbgAssert(iwp < DR_COUNT);          /* We must have found one */
    DBGDRDisableVDM(pdbgv);             /* Disable debug registers */

    /*
     *  Set up the register images.
     */
    if (type == DBG_W_Execute)          /* Convert type to bit mask */
        type = 0;
    else if (type == DBG_W_Write)
        type = 1;
    else if (type == DBG_W_IOBreak)		 /* 152754 */
        type = 2;
    else type = 3;
    cb = (cb << 2) | type;              /* Combine type and length bits */
    pdbgv->dbgv_dr7 |= DBGDR7_LENRWn(cb,idr);
                                        /* Set type and length bits */
    pdbgv->dbgv_dr7 |= DBGDR7_GLn(1,idr);
                                        /* Set register's local enable bit */
    pdbgv->dbgv_dr7 |= 0x100;           /* Set exact data breakpoint bit */
    pdbgv->dbgv_adr[idr] = laddr;       /* Remember the address */

    /*
     *  Tell DosDebug and kernel debugger we're here.
     */
    ++DBGmpclwpcbcptda[idr];            /* Increment process count */
    ++pdbgv->dbgv_cdr;                  /* Increment register count */

    /*
     *  Set debug register now.
     */
    DBGDREnableVDM(pdbgv);              /* Arm the debug registers */
#ifdef DBGSTRICT
        printf( "DR7 SET  %08lx Dr0-%08lx %x Dr1-%08lx %x Dr2-%08lx %x Dr3-%08lx %x\n",
                pdbgv->dbgv_dr7, pdbgv->dbgv_adr[ 0 ], pdbgv->dbgv_mpiwpidr[0],
                pdbgv->dbgv_adr[ 1 ], pdbgv->dbgv_mpiwpidr[ 1 ],
                pdbgv->dbgv_adr[ 2 ], pdbgv->dbgv_mpiwpidr[ 2 ],
                pdbgv->dbgv_adr[ 3 ], pdbgv->dbgv_mpiwpidr[ 3 ] );
#endif

    *piwp = iwp;                        /* Return watchpoint index */
    return(NO_ERROR);                   /* Success */
}


#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

/***EP  DBGWPFreeVDM - free a watchpoint for a VDM
 *
 *      This function frees a local watchpoint for the current
 *      VDM process.
 *
 *      DBGWPFreeVDM (iwp)
 *
 *      ENTRY   iwp             - watchpoint index
 *      RETURN  int             - return code (NO_ERROR if successful)
 */

int
DBGWPFreeVDM(ulong_t iwp)
{
    ulong_t cdr;                        /* Count of debug registers in use */
    ulong_t i;                          /* Loop index */
    ulong_t idr;                        /* Debug register index */
    ulong_t lt;                         /* Length and type bit mask */
    dbgvdm_t *pdbgv;                    /* Pointer to watchpoint info */

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgWPFreeVDM     ");   }
#endif

    pdbgv = (dbgvdm_t *) pPTDACur->ptda_pdcb;
                                        /* Get pointer to control block */
    if (pdbgv == NULL || iwp >= DR_COUNT ||
      (idr = pdbgv->dbgv_mpiwpidr[iwp]) == (uchar_t)'\xFF')
        return(ERROR_INVALID_PARAMETER);/* Return error if index invalid */
    cdr = pdbgv->dbgv_cdr;              /* Get count of registers in use */
    dbgAssert(cdr > 0);                 /* Must have some allocated */
    DBGDRDisableVDM(pdbgv);             /* Disable all watchpoints */
    pdbgv->dbgv_mpiwpidr[iwp] = '\xFF'; /* Mark entry as free */
    if (idr < cdr - 1) {                /* If other registers above */
        for (i = idr; i < cdr - 1; ++i)
            pdbgv->dbgv_adr[i] = pdbgv->dbgv_adr[i + 1];
                                        /* Move address down */
        /* This is a defect never been found. Fixed in 149007  GK    */
        for( i = 0; i< cdr; i++ )                          /* 149007 */
        {                                                  /* 149007 */
           if( pdbgv->dbgv_mpiwpidr[i] > idr &&            /* 149007 */
               pdbgv->dbgv_mpiwpidr[i] != (uchar_t)'\xFF') /* 149007 */
             pdbgv->dbgv_mpiwpidr[i] --;                   /* 149007 */
        }                                                  /* 149007 */
        lt = ~0 << (((idr + 1) << 2) + 16);
                                        /* Get bit mask to save settings */
        lt = (lt & pdbgv->dbgv_dr7) >> 4;
                                        /* Get bits to save and shift down */
        pdbgv->dbgv_dr7 &= ~(~0 << ((idr << 2) + 16));
                                        /* Save remaining bits */
        pdbgv->dbgv_dr7 |= lt;          /* Put in shifted bits */
        lt = (~0 << (idr + 1)) & 0x0F;  /* Get bit mask to save settings */
        lt = (lt & pdbgv->dbgv_dr6) >> 1;
                                        /* Get bits to save and shift down */
        pdbgv->dbgv_dr6 &= ~(~0 << idr) | 0xFFFFFFF0;
                                        /* Save remaining bits */
        pdbgv->dbgv_dr6 |= lt;          /* Put in shifted bits */
    }
    pdbgv->dbgv_dr7 &= 0xFFFF0000L;     /* Clear all enable bits */
    if (--cdr != 0) {           /* If there are still watchpoints set */
        for (idr = cdr; idr-- > 0; )
            pdbgv->dbgv_dr7 |= DBGDR7_GLn(1,idr);
                                        /* Set local enable bits */
        pdbgv->dbgv_dr7 |= 0x100;       /* Set exact match bit */
        pdbgv->dbgv_cdr = cdr;          /* Set new count */
        DBGDREnableVDM(pdbgv);          /* Enable remaining watchpoints */

#ifdef DBGSTRICT
        printf( "DR7 FREE %08lx Dr0-%08lx %x Dr1-%08lx %x Dr2-%08lx %x Dr3-%08lx %x\n",
                pdbgv->dbgv_dr7, pdbgv->dbgv_adr[ 0 ], pdbgv->dbgv_mpiwpidr[0],
                pdbgv->dbgv_adr[ 1 ], pdbgv->dbgv_mpiwpidr[ 1 ],
                pdbgv->dbgv_adr[ 2 ], pdbgv->dbgv_mpiwpidr[ 2 ],
                pdbgv->dbgv_adr[ 3 ], pdbgv->dbgv_mpiwpidr[ 3 ] );
#endif

    }
    else {                              /* Else no more watchpoints set */
        /*
         *  Release the VDM watchpoint control block.  Note
         *  that if the process plays around with exactly
         *  one watchpoint, we may spend a lot of time allocating
         *  and freeing the heap block over and over.  If this
         *  turns out to be a problem, then a new field could
         *  be added to the PTDA to save the control block until
         *  it is needed again.  We do NOT want to leave ptda_pdcb
         *  pointing to the block when no watchpoints are set, as
         *  this would cause unnecessary overhead during context
         *  switches.
         */
        VMFreeKHB(VM_HKH_PUB_RESRW, pdbgv);
        pPTDACur->ptda_pdcb = NULL;
    }
    --DBGmpclwpcbcptda[cdr];            /* Decrement process count */
    return(NO_ERROR);                   /* Success */
}


#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

/***EP  DBGDR6QueryVDM - query/update DR6 for a VDM process
 *
 *      This function is used to query bits B0-B3 in DR6
 *      for the current VDM process.  It is also used to reset
 *      any of those bits.  Note that the logical mapping of
 *      the bits is affected rather than the physical mapping.
 *
 *      DBGDR6QueryVDM (flquery, flreset, pfl)
 *
 *      ENTRY   flquery         - bit mask of bits to query
 *              flreset         - bit mask of bits to reset
 *              pfl             - place to return bits
 *      RETURN  int             - return code (NO_ERROR if successful)
 */

int
DBGDR6QueryVDM(ulong_t flquery,ulong_t flreset,ulong_t *pfl)
{
    ulong_t flpquery;                   /* Physical query bit mask */
    ulong_t flpreset;                   /* Physical reset bit mask */
    ulong_t idr;                        /* Debug register index */
    ulong_t iwp;                        /* Watchpoint index */
    dbgvdm_t *pdbgv;                    /* Pointer to watchpoint info */

#ifdef DBGSTRICT
    if (DBGBugBug & DBGWPc)
    {  printf("\nDbgwp.c *** DdgDR6QueryVDM   ");   }
#endif

    if (((flquery | flreset) & 0xFFFFFFF0) ||
      (pdbgv = (dbgvdm_t *) pPTDACur->ptda_pdcb) == NULL)
        return(ERROR_INVALID_PARAMETER);/* Error if bits bad or none in use */
    flpquery = flpreset = 0;            /* Initialize */
    for (iwp = 0; iwp < DR_COUNT; ++iwp) {
                                        /* Convert logical to physical */
        if ((idr = (ULONG)pdbgv->dbgv_mpiwpidr[iwp]) != 0xff) {      /* C006 */
                                        /* If watchpoint in use */
            if (flquery & (1 << iwp))
                flpquery |= 1 << idr;   /* Set bit if query requested */
            if (flreset & (1 << iwp))                                /* C006 */
                flpreset |= 1 << idr;   /* Set bit if reset requested */
        }
    }
    flpquery &= pdbgv->dbgv_dr6;        /* Query the requested bits */
    pdbgv->dbgv_dr6 &= ~flpreset;       /* Reset the requested bits */
    flquery = 0;                        /* Initialize */
    for (iwp = 0; iwp < DR_COUNT; ++iwp) {
                                        /* Convert physical to logical */
        if ((idr = (ULONG)pdbgv->dbgv_mpiwpidr[iwp]) != 0xff) {      /* C006 */
                                        /* If watchpoint in use */
            if (flpquery & (1 << idr))
                flquery |= 1 << iwp;    /* Set if physical bit was set */
        }
    }
    *pfl = flquery;                     /* Return queried bits */
    return(NO_ERROR);                   /* Success */
}

#endif /* DEBUG */
