;       SCCSID = @(#)nwd.asm    6.4 92/01/17
PAGE    ,132
NAME    NWD
TITLE   NMI Watchdog Timer Handling

;**     NWD.ASM
;
;       SCCSID = @(#)nwd.asm    6.4 92/01/17
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       DESCRIPTION
;
;       These routines are entered when an nmi occurs.  They distiguish
;       between failsafe NMIs and normal NMIs, and pass control back to
;       the nmi handling in trap.asm.
;
;       NOTES
;
;       The NMI Watchdog Timer is enabled by SysInit, and is never disabled,
;       except on debugger entry.
;
;       Change History
;       =======================================================================
;       122474  10/5/95 ChgTeam Moved TKCallKrnlExit code from TRAP.ASM,
;                               allowing user NMI servicing without requiring
;                               TSS editing.
;       137252  10/16/95 CHGTm  Move the saved nmi error code in TSS.
;
.xlist
        include dosseg.inc
        include basemaca.inc
        include doshlp.inc
        include error.inc
        include except.inc
        include kmode.inc
        include mi.inc
        include protmode.inc
        include osmaca.inc
        include seldesc.inc
        include selxport.inc
        include tkexport.inc
.list

        CpuMode 386
        ModName nwd


        ReferFlat       _SELGDTLaddr,DWORD
        ReferGDT        GDT_NWDSTACK,QWORD

        ReferGlobal     ABIOS_present,BYTE
        ReferGlobal     DHF,DWORD

        EXTRN32         TRAPCommonFaultEntry:NEAR
ifdef   PMDEBUG
        EXTRN32         _printf:NEAR
endif
IF      RAS
        include krnlexit.inc                                           ; 122474
        ReferFlat       _pKExitListHead,DWORD                          ; 122474
        EXTRN16         Call16bitKrnlExitDD                            ; 122474
ENDIF
DATA segment

;       references moved to sdata.asm for System Anchor Block - f122427

;               public  NWDSTKLEN, NWDStackBase, cMaxNMI, cNMI, fNWDEnabled
;               public  fNWD, cMaxKNMI, isa_error
                public  NWDSTKLEN, NWDStackBase
                public  isa_error

                extrn   cMaxNMI:DWORD
                extrn   cMaxKNMI:DWORD
                extrn   cNMI:DWORD
                extrn   fNWD:BYTE
                extrn   fNWDEnabled:BYTE

NWDSTKLEN       equ     400H;                   ; increased from 200H 122474


NWDStackBase    label   byte
                db      NWDSTKLEN dup (?)       ; NMI stack
NWDStack        label   byte

                public  NWDTss
NWDTss          label   byte                    ; TSS for handling NMIs
                tss_s   <0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
                0, NWDSTKLEN, 0, 0, 0, seg FLAT:DGROUP, 0, \
                seg FLAT:DOSHIGH32CODE, 0, GSEL GDT_NWDSTACK, 0, seg FLAT:DGROUP, 0, \
                0, 0, 0, 0, 0, 0, 0, -1>


ulMaxCli        dd      NWD_MAX_INTERVAL

ifdef   PMDEBUG

szWarningVDM    db      'NWD WARNING: a failsafe timeout was detected in a '
                db      'VDM at cs:eip = %x:%lx'
                db      0DH, 0AH, 0
szWarning       db      'NWD WARNING: a failsafe timeout was detected at '
                db      'cs:eip = %x:%lx'
                db      0DH, 0AH, 0
endif
isa_error       db      0               ; 137252 - Saved nmi error code

DATA ends

           FARCODE HIGH32
HIGH32CODE segment


;***    NWDHandler - handle watchdog NMI
;
;       Entry
;               Task switched
;               SS:ESP = NWDStackBase
;               DS = FLAT
;               ES = DOSGROUP
;
;       Exit
;               KM_FF_NMI set if it was a watchdog nmi & too many watchdog
;               exits to trap02 if not watchdog nmi
;
;       USES
;               None
;
;       CONTEXT
;               ALL
;
;       PSEUDO-CODE
;               if it is a Watchdog NMI
;                       increment cNMI
;                       if cNMI > cMaxNMI
;                               set force flag
;                               if not ring 0
;                                       edit backlink tss to return to
;                                           trap02
;                               endif
;                       endif
;                       reset hardware nmi counter
;
;               else
; 122474                call TKCallKrnlExit
; 122474                if exit handled the NMI
; 122474                   return to interrupted task
; 122474                else
;                       edit backlink tss to return to CommonFaultEntry in trap.asm
;               endif
;

        ASSUME  DS:FLAT, ES:DOSGROUP, SS:NOTHING, GS:NOTHING, FS:NOTHING
Procedure NWDHandler, near

ifdef   PMDEBUG

        ; turn off nt flag, so return from debugger will work.
        ; this is required for printf to work correctly

        pushfd
        and     dword ptr [esp], not F_NT
        popfd

        ; set up stack base for SStoDS stuff

        push    _TKSSBase                               ; make sure TKSSbase
        mov     [_TKSSBase], offset FLAT:NWDStackBase   ; is set up for debug

        ; ***
        ; setting breakpoints before this point may have unpredicable
        ; results
        ; ***

endif

        push    eax                                                  ;122474
        call    DHF.DosHlpResetWatchdogNMI ; check for watchdog & reset counter
        or      eax, eax                                             ;95323
        pop     eax                                                  ;122474
        jz      NWDH50          ; not a watchdog timer NMI           ;95323


ifdef   PMDEBUG

        ; print a warning message if someone turns interrups off too long

        mov     ax, NWDTss.tss_backlink
        call    NWDAccessTSS
        movzx   eax, word ptr [esi].tss_cs
        test    [esi].tss_eflags, F_VM          ; in a vdm?
        jz      short NWDH5                     ; no

        CCall   _printf, <FLAToffset szWarningVDM, eax, [esi].tss_eip>, cdecl

        jmp     short NWDH15

NWDH5:  CCall   _printf, <FLAToffset szWarning, eax, [esi].tss_eip>, cdecl

; if you wish to get more info, set a breakpoint on the following instruction
NWDH15:
endif

        mov     ebx, cNMI
        inc     ebx                     ; count another NMI
        cmp     ebx, cMaxNMI
        ja      short NWDH30            ; time to terminate process

NWDH10: mov     cNMI, ebx

NWDH20:

ifdef  PMDEBUG

        ; ***
        ; setting breakpoints after this in the code may have unpredictable
        ; results
        ; ***

        pop     _TKSSBase

        ; set nt flag, so IRET will return to the trapped out of task

        pushfd
        or      dword ptr [esp], F_NT
        popfd

endif

        iretd                   ; exit

        jmp     NWDHandler      ; Tss is left pointing here.


NWDH30: mov     ax, NWDTss.tss_backlink ; get access to previous TSS
        call    NWDAccessTSS
        test    [esi].tss_eflags, F_VM
        jnz     short NWDH40            ; in a vdm, so we are at ring 3 ;95323

        mov     ax, [esi].tss_cs
        and     ax, SEL_RPLMASK
        jz      short NWDH45            ; ring 0, so wait for exitKMODE

NWDH40: xor     ebx, ebx                ; reset count of nmi
        mov     fNWD, TRUE              ; set NWD timeout flag
        call    NWDEditTss              ; fix for return to TRAPCommonFaultEntry
        jmp     short NWDH10

NWDH45: cmp     ebx, cMaxKNMI           ; has kernel had ints off too long?
        jna     short NWDH10            ; no

        jmp     short NWDH40            ; yes, so internal error

NWDH50: xor     ebx, ebx                ; other NMI
        cmp     ABIOS_present,1         ; Is ABIOS present
        jz      short NWDH60            ; yes - skip acknowledge
        call    DHF.DosHlpAckNMI        ; Acknowledge NMI
        mov     isa_error,bl            ; Save nmi error 137252
NWDH60:
;
; if a Kernel Exit is registered for NMI, allow it to process       122474
; the NMI first and return to the point of interruption.            122474
;                                                                   122474
; If the the NMI is not handled by the registered Kernel Exit,      122474
; edit the tss to cause control to pass to CommonFaultEntry         122474
;                                                                   122474
;                                                                   122474
; Allow Kernel Exit registered with DEVHLP a chance to handle NMI   122474
;                                                                   122474
IF      RAS
        push    eax                     ; 78486                     122474
        push    ebx                     ; 78486                     122474
        mov     ebx,-1                  ; 78486 no info available   122474
        TKCallKrnlExit NMI_exit         ; 78486 call Kernel Exit    122474
        pop     ebx                     ; 78486                     122474
        cmp     eax,ke_NMI_U_HANDLED    ; 78486                     122474
        pop     eax                     ; 78486                     122474
        jz      NWDH20                  ; 78486 Exit Handled        122474
ENDIF
        call    NWDEditTss              ; Edit nmi tss              122474
        jmp     NWDH20                  ;                           122474

EndProc NWDHandler


;***    NWDEditTss - edit context to return to trap handling routine
;
;       Entry
;               DS = FLAT
;               ES = DOSGROUP
;       Exit
;               destination tss modified
;
;       Uses
;               flags, gs, ebx, esi
;
;       Pseudo-Code
;
;
;               if coming from V86 mode
;                       put gs, fs, ds, es on ring 0 stack
;                       zero gs, fs, ds, es in BackLinkTss
;               put ss, esp, eflags, cs, eip on ring 0 stack
;               if coming from V86 mode
;                       reset V86 flag in BackLinkTss
;               change BackLinkTss SS:ESP to point to ring 0 stack
;               change BackLinkTss CS:EIP to point to trap02
;

        ASSUME DS:FLAT, ES:DOSGROUP, SS:NOTHING, GS:NOTHING, FS:NOTHING
Procedure NWDEditTss, near

        mov     ax, NWDTss.tss_backlink         ; get acces to tss
        call    NWDAccessTSS

        mov     gs, [esi].tss_ss0               ; get pointer to ring0 stack
        mov     edi, [esi].tss_esp0             ;

        test    [esi].tss_eflags, F_VM          ; coming from v86 mode?
        jnz     short net10                     ; yes

        test    [esi].tss_cs,RPL_MASK           ; ring0 code?         JM_78486
        jnz     short net30                     ; no                  JM_78486

        mov     gs, [esi].tss_ss                ; use client stack    JM_78486
        mov     edi, [esi].tss_esp              ;                     JM_78486
        jmp     short net30

        ; put gs, fs, ds, es on ring 0 stack

net10:  sub     edi, 4                          ; "push" gs
        mov     bx, [esi].tss_gs
        mov     gs:[edi], bx
        sub     edi, 4                          ; "push" fs
        mov     bx, [esi].tss_fs
        mov     gs:[edi], bx
        sub     edi, 4                          ; "push" ds
        mov     bx, [esi].tss_ds
        mov     gs:[edi], bx
        sub     edi, 4                          ; "push" es
        mov     bx, [esi].tss_es
        mov     gs:[edi], bx

        ; zero them in the TSS (trap out of V86 zeros all seg regs)

        xor     bx, bx
        mov     [esi].tss_gs, bx
        mov     [esi].tss_fs, bx
        mov     [esi].tss_ds, bx
        mov     [esi].tss_es, bx

        ; put ss, esp, eflags, cs, eip on ring 0 stack

net30:                                          ; JM_78486
; JM_78486 net30:  sub     edi, 2        JM_78486        ; "push" TKSSBase (low word)
ifdef   PMDEBUG

        ; If this is the debug kernel, we have saved _TKSSBase on the
        ; stack, and changed it's value. The stack looks like:
        ;
        ;   [ return address  ]   < - ESP
        ;   [ saved _TKSSBase ]
        ;

        mov    ebx,[esp+4]
else
        ; Otherwise, we can get _TKSSBase directly.

        mov     ebx,[_TKSSBase]
endif
        mov     [esi].tss_esp1, ebx             ; JM_78486

        test    [esi].tss_eflags, F_VM          ; coming from v86 mode? ;95323
        jnz     short net31                     ; yes                   ;95323

        test    [esi].tss_cs,RPL_MASK           ; JM_78486
        jz      short net40                     ; JM_78486
net31:  sub     edi, 2                                                  ;105262
        mov     gs:[edi], bx
        sub     edi, 2                          ; "push" ss
        mov     bx, [esi].tss_ss
        mov     gs:[edi], bx
        sub     edi, 4                          ; "push" esp
        mov     ebx, [esi].tss_esp
        mov     gs:[edi], ebx
net40:  sub     edi, 4                          ; "push" eflags
        mov     ebx, [esi].tss_eflags
        or      ebx, F_INTERRUPT                ; reenable interrupts
        mov     gs:[edi], ebx
        sub     edi, 2                          ; "push" TKSSBase (high word)
ifdef   PMDEBUG

        ; If this is the debug kernel, we have saved _TKSSBase on the
        ; stack, and changed it's value. The stack looks like:
        ;
        ;   [ return address  ]   < - ESP
        ;   [ saved _TKSSBase ]
        ;

; JM_78486  mov    ebx,[esp+4]
else
        ; Otherwise, we can get _TKSSBase directly.

; JM_78486  mov    ebx,[_TKSSBase]
endif
; JM_78486  shr     ebx,16
; JM_78486  mov     gs:[edi], bx
        sub     edi, 2                          ; "push" cs
        mov     bx, [esi].tss_cs
        mov     gs:[edi], bx
        sub     edi, 4                          ; "push" eip
        mov     ebx, [esi].tss_eip
        mov     gs:[edi], ebx

        ; set up error code and trap code

        sub     edi, 4
;;;;    mov     dword ptr gs:[edi], NO_ERROR    ; 137252
        xor     ebx,ebx                         ; 137252
        mov     bl,isa_error                    ; 137252   move nmi error code
        mov     dword ptr gs:[edi], ebx         ; 137252 - to TSS
        sub     edi, 4
        mov     dword ptr gs:[edi], EXCNMI

        ; point ss:eip at trap frame built on ring0 stack

        mov     [esi].tss_esp, edi
        mov     [esi].tss_ss, gs

        ; turn off V86 flag

        and     [esi].tss_eflags, NOT F_VM

        ; set destination cs:eip

net60:  mov     [esi].tss_cs, seg FLAT:TRAPCommonFaultEntry
        mov     [esi].tss_eip, offset FLAT:TRAPCommonFaultEntry

        ; TKSSBase may not be set up correctly for the ring 0 stack
        ; (e.g. if we take an NMI inside an ISR _TKSSBase points to
        ; the interrupt managers stack base, instead of the ring 0
        ; stack base.)

        mov     ebx,[_pTCBCur]
        mov     ebx,[ebx].TCBBiasTSD

ifdef   PMDEBUG

        ; If this is the debug kernel, we have saved _TKSSBase on the
        ; stack, and changed it's value. The stack looks like:
        ;
        ;   [ return address  ]   < - ESP
        ;   [ saved _TKSSBase ]
        ;

        mov    [esp+4],ebx
else
        ; Otherwise, we can set _TKSSBase directly.

        mov    [_TKSSBase],ebx
endif
        ret

EndProc NWDEditTss


;***    NWDAccessTSS - provied access to specified TSS
;
;       Entry
;               DS = FLAT
;               AX = TSS selector
;
;       Exit
;               ds:esi -> tss data
;
;       Uses
;               flags, esi
;
;       Pseudo-Code
;               search for TSS in table
;               if found
;                       put coresponding linear offset in esi
;               else
;                       panic
;
        ASSUME DS:FLAT, ES:DOSGROUP, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure NWDAccessTSS, near

        EnterProc
        push    eax
        push    edx

        movzx   eax, ax
        mov     esi, [_SELGDTLaddr]
        and     eax, not (SEL_RPLMASK or SEL_LDTBIT) ; make gdt index

        mov     edx,[esi+eax+1]         ; (high 3 bytes of EDX) = low 3 bytes
        mov     dl,[esi+eax+7]          ; (low byte of EDX) = high base byte
        ror     edx,8                   ; (EDX) = Base Linear Address of TSS

        mov     esi, edx

        pop     edx
        pop     eax                     ; corrected pop order 122474
        LeaveProc
        ret


EndProc NWDAccessTSS


HIGH32CODE      ends
        FARCODE LOW
CODE    segment

;***    NWDEnableWatchdogNMI
;
;       Entry
;                none
;
;       Exit
;               Watchdog NMI enabled, and variables initialized
;
;       Uses
;               flags
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
Procedure NWDEnableWatchdogNMI, faronly
        push    eax
        push    ds
        DosContext ds

        mov     eax, ulMaxCli                   ; get time in milliseconds
        call    DHF.DosHlpEnableWatchdogNMI
        mov     cMaxNMI, eax                    ; save # of nmi in interval
        mov     edx, 10
        mul     edx
        jc      short new10                     ; more than 32 bits?

        mov     cMaxKNMI, eax
        jmp     short new20

new10:  mov     cMaxKNMI, 0FFFFFFFFh            ; yes, use max value

new20:  mov     fNWDEnabled, TRUE

        pop     ds
        pop     eax
        ret

EndProc NWDEnableWatchdogNMI


;***    NWDDisableWatchdogNMI
;
;       Entry
;               none
;
;       Exit
;               Watchdog nmi disabled
;
;       Uses
;               flags
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
Procedure NWDDisableWatchdogNMI, <faronly, far16>
        push    ds
        DosContext ds

        cmp     fNWDEnabled, TRUE
        jne     short ndw10                                          ;95323

        call    DHF.DosHlpDisableWatchdogNMI

ndw10:  pop     ds
        ret
EndProc NWDDisableWatchdogNMI


;***    NWDRestoreWatchdogNMI -- enable WatchdogNMI if it used to be
;
;       Entry
;               none
;
;       Exit
;               Watchdog nmi is enabled if it used to be enabled
;
;       Uses
;               flags
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
Procedure NWDRestoreWatchdogNMI, faronly
        push    eax
        push    ds
        DosContext ds

        cmp     fNWDEnabled, TRUE               ; has system enable NWD yet?
        jne     nwr10

        mov     eax, ulMaxCli
        call    DHF.DosHlpEnableWatchdogNMI
        mov     cMaxNMI, eax

nwr10:  pop     ds
        pop     eax
        ret
EndProc NWDRestoreWatchdogNMI

CODE ends
                end
