;       SCCSID = @(#)dbgsub.asm 6.3 91/11/13
;***    DBGSUB.ASM - Miscelaneous MASM subroutines for DosDebug
;
;       SCCSID = @(#)dbgsub.asm 6.3 91/11/13
;
;       THIS IS AN OS/2 VERSION 2.x SPECIFIC FILE
;
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;       This file implements support procedures for DosDebug which
;       cannot be written in C.   This includes :
;
;       Debug Register Access routines
;
;*****************************************************************************
;*  Change                                                               Chg *
;*  Activity      Date    BY   Reason                                    Num *
;*  --------      ----    ---  -------                                   --- *
;*  PTR B727809 11/06/91  ARJ  Add DBGDR7LeGe to turn off LE and GE bits 001 *
;*  PTR   72417 18:Nov-95 MLM  moved find.inc after tkexport.inc             *
;*****************************************************************************

.xlist
include basemaca.inc
include osmaca.inc
include dossymc.inc
include ostypes.inc
include dosseg.inc
include error.inc
include error2.inc
include mi.inc
include protmode.inc
include seldesc.inc
include proc.inc
include kmode.inc
include sysentry.inc
include ordinals.inc
include struc.inc
include osconst.inc
include sigdef.inc
include except.inc
include infoseg.inc
include pgexport.inc
include vmowner.inc
include vmexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include screen.inc
include sem.inc
include tkexport.inc
include find.inc
include tkptda.inc
include dbgin.inc
include dbgex.inc
.list

        ModName dbgsub

ifdef INCDEBUG

        EXTRN32         _DbgWPHit

        ReferFlat       _DBGaDRowner,DWORD
        ReferFlat       _DBGapgwpcb,DWORD
        ReferFlat       _DBGckDR,DWORD
        ReferFlat       _DBGcSysDR,DWORD
        ReferFlat       _DBGlDR7global,DWORD
        ReferFlat       _DBGlmskDR7reserved,DWORD
        ReferFlat       _DBGlmskDR7system,dword
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       DR6NotBn,DWORD

        CpuMode 386

IFDEF   PMDEBUG
DbgWPScan_TEXT  equ     <_TEXT>
ELSE
DbgWPScan_TEXT  equ     <DEBUGLOCK_TEXT>
ENDIF

        FARCODE HIGH32
DbgWPScan_TEXT  SEGMENT

;***EP  DbgWPScan - Scan watchpoints
;
;       DbgWPScan scans the CPU debug registers to see if a watchpoint was hit.
;
;       DbgWPScan may be called at interrupt time.
;
;       If DbgWPScan detects a debug register hit that is intended for
;       DosDebug, the Bn bit in the DR6 image and the Gn and Ln bits
;       in DR7 will be cleared, and DbgWPHit will be called to route
;       the hit to the debuggee.
;
;       Debug registers which are hit but are not intended for DosDebug will
;       remain hit.
;
;       Debug registers which are hit but not armed are ignored - the Bn bit
;       is cleared but no further action is taken.
;
;       Entry:  Interrupts off, one or more debug registers hit.
;               (EBX) = DR6 image
;               (ESI) = DR7 image
;               if (faulting address is ring 0)
;                  (EDX) = 0
;               else
;                  (EDX) = faulting linear address
;               DS,ES FLAT
;
;       Exit:   if (no more DR6 events)
;                  Carry Set
;               else
;                  Carry Clear
;               (EBX) = updated DR6 image
;               DR7 Gn and Ln bits clear for each DosDebug register hit.
;               (If all DR7 Gn and Ln bits clear, GE and LE also clear).
;
;       Uses:   EAX, EBX, ECX, EDX, EDI, ESI, DR7
;
;       The code is straight line for the most common case:  one armed
;       debug register hit owned by DosDebug.
;
;       Pseudocode: (High Level)
;
;          EnterKmode = FALSE
;
;          for (each hit debug register) {
;
;             if (not armed) {
;                clear Bn in DR6 image
;                continue
;             }
;
;             if (owner != DosDebug)
;                continue
;
;             rc = DbgWPHit(debug register index, faulting address)
;
;             if (rc.EnterKmode)
;                EnterKmode = TRUE
;
;             if (rc.Disarm)
;                clear Gn, Ln in DR7
;          }
;
;          if (all Gn and Ln clear in DR7)
;             clear GE and LE in DR7
;
;          if (EnterKmode || more events in DR6 image)
;             clear carry
;
;          else
;             set carry
;
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

; Masks to clear DR7 DRn arming control bits

DBGDR7NotGnLn   label   dword
        dd      NOT (DR7_L0+DR7_G0)
        dd      NOT (DR7_L1+DR7_G1)
        dd      NOT (DR7_L2+DR7_G2)
        dd      NOT (DR7_L3+DR7_G3)

; DR6 image masks to check for more debug register hits
;
; if ((DR6 image) & (DBGDR6MoreBn(i)) != 0
;    then there is some hit debug register j such that j > i.

DBGDR6MoreBn    label   byte
        db      DR6_B1+DR6_B2+DR6_B3
        db      DR6_B2+DR6_B3
        db      DR6_B3
        db      0

Procedure DbgWPScan,near

        xor     edi,edi                         ; (EDI) = flDR7Changed = FALSE
        push    edx                             ; Save linear address for
                                                ;  calls to DbgWPHit
        .errnz  DR6_B0 - 1h
        .errnz  DR6_B1 - 2h
        .errnz  DR6_B2 - 4h
        .errnz  DR6_B3 - 8h
        bsf     edx,ebx                         ; (EDX) = hit debug register
diw10:  mov     ecx,_DBGaDRowner[edx*type _DBGaDRowner]
                                                ; (ECX) = _DBGaDRowner[DR index]
        .errnz  DBGDR_DBG - 1h
        dec     ecx                             ; DosDebug debug register?
        jnz     short diw30                     ;  No, continue

        and     ebx,DR6NotBn[edx*type DR6NotBn] ; clear Bn in DR6 image
        mov     eax,[esp]                       ; (EAX) = faulting address
        push    edx                             ; Preserve DR index
        CCALL32 _DbgWPHit,<edx,eax>             ; DbgWPHit(DR index,fault laddr)
        pop     edx                             ; Restore DR index
        or      ah,ah                           ; Enter kmode?
        jz      short diw20                     ;  No, don't set flag

        or      ebx,DBGDR6IMAGE_ENTERKMODE      ; Make sure we enter kmode
diw20:  or      al,al                           ; Disarm?
        jz      short diw30                     ;  No, skip Gn,Ln clearing

        and     esi,DBGDR7NotGnLn[edx*type DBGDR7NotGnLn]
                                                ; Disarm Debug register (EDX)
        inc     edi                             ; flDR7Changed = TRUE (Nonzero)
diw30:  test    DBGDR6MoreBn[edx*type DBGDR6MoreBn],bl
                                                ; More Debug registers hit?
        jnz     short diw90                     ;  Yes, find the next one

diw40:  or      edi,edi                         ; DR7 changed?
        jz      short diw60                     ;  No, don't update DR7

        test    ebx,(DR7_L0+DR7_G0+DR7_L1+DR7_G1+DR7_L2+DR7_G2+DR7_L3+DR7_G3)
                                                ; Any DRs set?
        jz      short diw50                     ;  Yes, skip over GE,LE clearing

        and     esi,NOT (DR7_LE+DR7_GE)         ; Clear GE and LE bits in DR7
diw50:  mov     dr7,esi                         ; Write new DR7 image
diw60:  test    ebx,DR6_VALID+DBGDR6IMAGE_ENTERKMODE-DR6_BD
        jnz     short diw70                     ; Jump if more events

        stc
diw70:  pop     edx
        RETC

        ; If there are more debug register hits to handle, find the next
        ; one.

diw90:  inc     edx                             ; (EDX) = next DR index
        bt      ebx,edx                         ; Debug register (EDX) hit?
        jc      short diw10                     ;  Yes, handle it

        jmp     short diw90                     ; Keep looking

EndProc DbgWPScan

DbgWPScan_TEXT  ends

DEBUGLOCK_TEXT  SEGMENT

;***LP  DbgValidateDR7 - validate DR7
;
;       Updates DR7 with the current global and local watchpoint settings
;
;       Entry:  Interrupts off
;               DS, ES FLAT
;
;       Exit:   Interrupts off
;
;       Uses:   EAX, ECX, DR7
;
;       Pseudocode:
;
;          Clear nonreserved bits in DR7
;
;          DR7 |= Global watchpoint bits
;
;          if (current process has local watchpoints) {
;
;             DR7 |= Local watchpoint bits
;
;          }
;
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure _DbgValidateDR7,near

        mov     eax,dr7
        and     eax,[_DBGlmskDR7reserved]
        or      eax,[_DBGlDR7global]            ; Update global watchpoint bits
        mov     ecx,[_pPTDACur]                 ; (ECX) = pPTDACur
        mov     ecx,[ecx].ptda_pdcb             ; (ECX) = pPTDACur->ptda_pdcb
        jecxz   dv71                            ; Jump if not a debugge

        or      eax,[ecx].dcb_lDR7local         ;  Update local watchpoint bits
dv71:   mov     dr7,eax                         ; Update DR7
        RETC

EndProc _DbgValidateDR7

;***LP  DbgDR7LeGe - Turn off LE or GE bits for the 386 chip              A001
;                                                                         A001
;       Turns off the LE or GE bits for global and local watchpoint       A001
;       settings, the 386 processor needs these bits on they are          A001
;       ignored by the 486 processor. I turn them off to speed up         A001
;       performance.                                                      A001
;                                                                         A001
;       PTR B727809 ADDED THIS ROUTINE, which need the bits turned on.    A001
;                                                                         A001
;       Entry:  Interrupts off                                            A001
;               DS, ES FLAT                                               A001
;                                                                         A001
;       Exit:   Interrupts off                                            A001
;                                                                         A001
;       Uses:   EAX, ECX, DR7                                             A001
;                                                                         A001
;       Pseudocode:                                                       A001
;                                                                         A001
;          Clear nonreserved bits in DR7                                  A001
;                                                                         A001
;          DR7 |= Global watchpoint bits                                  A001
;                                                                         A001
;          if (current process has local watchpoints) {                   A001
;                                                                         A001
;             DR7 |= Local watchpoint bits                                A001
;                                                                         A001
;          }                                                              A001
;                                                                         A001
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure _DbgDR7LeGe,near

        mov     eax,dr7                       ; Debug Register 7          A001
        and     eax,[_DBGlmskDR7reserved]     ;                           A001
        and     eax,[_DBGlDR7global]          ; Update Global WP bits     A001
        mov     ecx,[_pPTDACur]               ; (ECX)=pPTDACur            A001
        mov     ecx,[ecx].ptda_pdcb           ; (ECX)=pPTDACur->ptda_pdcb A001
        jecxz   dv73                          ; Jump if not a debugge     A001

        and     eax,[ecx].dcb_lDR7local       ;  Update Local WP bits     A001
dv73:   mov     dr7,eax                       ; Update DR7                A001
        RETC

EndProc _DbgDR7LeGe

DEBUGLOCK_TEXT  ends


        FARCODE HIGH32
_TEXT   segment


;***LP  DbgSetDRladdr - set a linear address for a debug register
;
;       Sets the specified debug register
;
;       Entry:  dsd_index = debug register index (0-3)
;               dsd_laddr = linear address to watch
;               DS FLAT
;
;       Exit:   None
;
;       Uses:   EAX, ECX
;               DR0, DR1, DR2, or DR3 depending on dsd_index
;
;       Pseudocode:
;
;          DR(dsd_index) = dsd_laddr
;
        ASSUME  DS:FLAT, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

DSDjmptbl label dword
        dd      offset FLAT:dsd0                ; use DR0
        dd      offset FLAT:dsd1                ; use DR1
        dd      offset FLAT:dsd2                ; use DR2
        dd      offset FLAT:dsd3                ; use DR3

Procedure _DbgSetDRladdr,near,,,esp

ArgVar  dsd_index,DWORD
ArgVar  dsd_laddr,DWORD

        EnterProc
        mov     eax,[dsd_laddr]                 ; (EAX) = laddr
        mov     ecx,[dsd_index]                 ; (ECX) = DR index
        LeaveProc
        jmp     DSDjmptbl[ecx * type DSDjmptbl]

dsd0:   mov     dr0,eax
        RETC
dsd1:   mov     dr1,eax
        RETC
dsd2:   mov     dr2,eax
        RETC
dsd3:   mov     dr3,eax
        RETC

EndProc _DbgSetDRladdr


;***EP  _DBGDRDisableVDM - disable VDM watchpoints
;
;       This procedure is called to disable a set of VDM watchpoints.
;       This procedure is resident because it is called in the middle
;       of context switches.  In addition to disabling the current
;       VDM watchpoints, it also reenables any DosDebug global
;       watchpoints.
;
;       ENTRY   ((SS:ESP)+4) = VDM watchpoint control block
;       EXIT    Interrupts enabled
;       USES    DR0-DR7, EAX, ECX, EDX, Flags

Procedure _DBGDRDisableVDM,near,,,esp
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        ArgVar  pdbgv,DWORD             ; Pointer to VDM watchpoint block

        EnterProc
        mov     ecx,pdbgv               ; (ECX) = control block pointer
        mov     edx,_DBGlmskDR7reserved ; Get reserved bit mask
        cli
        mov     eax,dr7                 ; (EAX) = DR7
        mov     [ecx].dbgv_dr7,eax      ; Save it in control block
        and     eax,edx                 ; Turn off local watchpoints
        mov     dr7,eax                 ; Turn them off
        sti
        not     edx                     ; Invert bit mask
        and     [ecx].dbgv_dr7,edx      ; Turn off kernel debugger bits
        mov     eax,dr6
        mov     [ecx].dbgv_dr6,eax      ; Save (DR6) in control block
        mov     ecx,_DBGcSysDR          ; (ECX) = global & kernel watchpoints
        sub     ecx,_DBGckDR            ; (ECX) = global watchpoint count
        jz      short drdvx             ;  done if none

;       Reenable DosDebug global watchpoints

        mov     ecx,DR_COUNT            ; (ECX) = total number of watchpoints
drdv0:  mov     eax,_DBGapgwpcb[ecx*4-4]; (EAX) = pwcb
        or      eax,eax                 ; Any watchpoint here?
        jz      short drdv1             ;  no, keep looking
        PushP   [eax].wpcb_laddr
        movzx   eax,[eax].wpcb_DRindex
        PushP   eax
        CCALL32 _DbgSetDRladdr,,,PushP  ; Set the watchpoint
drdv1:  loop    drdv0                   ; Try next watchpoint control block
        xor     eax,eax                 ; Don't leave our trash in DR6
        mov     dr6,eax
        cli
        mov     eax,dr7                 ; (EAX) = (DR7)
        or      eax,_DBGlDR7global      ; Or in global bits
        mov     dr7,eax                 ; Reenable DosDebug globals
        sti

drdvx:  LeaveProc
        RETC

EndProc _DBGDRDisableVDM


;***EP  _DBGDREnableVDM - enable VDM watchpoints
;
;       This procedure is called to enable a set of VDM watchpoints.
;       This procedure is resident because it is called in the middle
;       of context switches.  In addition to enabling VDM watchpoints,
;       it disables any global DosDebug watchpoints.
;
;       ENTRY   ((SS:ESP)+4) = VDM watchpoint control block
;       EXIT    Interrupts enabled
;       USES    DR0-DR7, EAX, ECX, Flags

Procedure _DBGDREnableVDM,near,,,esp
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        ArgVar  pdbgv,DWORD             ; Pointer to VDM watchpoint block

        EnterProc
        mov     ecx,pdbgv               ; (ECX) = control block pointer
        mov     eax,[ecx].dbgv_adr+(0*4); (EAX) = DR0 image
        mov     dr0,eax
        mov     eax,[ecx].dbgv_adr+(1*4); (EAX) = DR1 image
        mov     dr1,eax
        mov     eax,[ecx].dbgv_adr+(2*4); (EAX) = DR2 image
        mov     dr2,eax
        mov     eax,[ecx].dbgv_adr+(3*4); (EAX) = DR3 image
        mov     dr3,eax
        mov     eax,[ecx].dbgv_dr6      ; (EAX) = DR6 image
        mov     dr6,eax
        cli
        mov     eax,dr7                 ; (EAX) = (DR7)
        and     eax,_DBGlmskDR7reserved ; Turn off all but kdb watchpoints
        or      eax,[ecx].dbgv_dr7      ; Set bits for VDM watchpoints
        mov     dr7,eax                 ; Enable watchpoints
        sti
        LeaveProc
        RETC

EndProc _DBGDREnableVDM

_TEXT   ends

endif ;DEBUG

        end
