;       SCCSID = @(#)harderr.asm        6.6 91/10/01
SCCSID  equ     "@(#)harderr.asm        6.6 91/10/01"
title   HARDERR - hard error handler

;***    Hard Error management for OS/2
;
;       Hard errors are situations under which the task-level kernel instance
;       for a process decides that it cannot handle a particular error situation
;       and must resort to programmed directives or user directives.  Typically,
;       this results in a prompt being displayed for the user detailing the
;       circumstances and soliciting one of a variety of outcomes.
;
;       Global          Local
;
;       HardError
;                       GenErrCode
;                       CopyTemplate
;       ChkHEDaemon
;                       FillHEPacket
;                       GetHEResponse
;                       ProcessHE
;       HEAction
;                       SetLogicalDrive
;                       VDMHardError
;
;       VDMInt24Handler
;
;   Modification History:
;      08/28/92  APJ         CP20 24690   Disable hard error pop ups during
;                                         C-A-D shutdown.
;      02/15/94  Skip Nizinski    69005   Handle swap full errors better.
;                   Change hard error processing to serialize on _HardErrSem
;                   (not a ksem)
;                   and _HardErrTCB dword instead of _HdErrSem ksem.
;                   This defect addresses the following problems:
;                   1.trap in ProcessHE since tcb address is zero (thread
;                     terminated before user answered swapper full pop up).
;                   2.if thread which caused swapper full pop up to be
;                     displayed terminates, and another thread for another
;                     process is created which reuses this slot, then
;                     the wrong process would be terminated if the user
;                     replies 'abort' to pop up message.
;                   3.if thread which caused swapper full terminates before
;                     pop up is answered, the _HdErrSem is still owned by
;                     the slot which terminated. If another hard error comes
;                     in, tkdeclareinversion will reference a terminated
;                     tcb (or the wrong one if the slot has been reused).
;                   4.panic in tkassert(tcbcboostlock!=0). Code decremented
;                     boostlock count for tcb which detected swapper full.
;                     If another harderr comes while swap full thread owns
;                     _HdErrSem, boost count will be zero when
;                     tkdeclareinversion is called. This is fixed by
;                     having swap full threads use serialization other than
;                     the ksem. Note that this panic only occurs when
;                     running with the debug kernel.
;
;      05/11/94  Change Team  83566   If the swapper is trying to display
;                   a swap full harderr message, ignore the JFN_FAIL flag.
;                   Defect 87563 was used to merge 83566 from r206 to r207.
;
;      11/17/94  Change Team  84994   Fix regression from 83566 where DS
;                   has changed and causes a gpfault.
;
;
;       09/22/94  John Nalbone    98064  In VDMHardError, turned OFF the
;                     VHE_FLAG_POPUP flags for NET errors.  Now when we get
;                     a net error we will call the INT 24 routine first, which
;                     will call harderror as part of its processing.  This
;                     will allow applications that have hooked INT 24 a chance
;                     to get in before the popup.  This is consistent with DOS.
;
;       11/27/95 - MLM defect 72417
;                 moved from PTDA to TCB: WFP_Start
.xlist
.xcref
include fnofs.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include error.inc
include dpb.inc
include devsym.inc
include exec.inc
include harderr.inc
include mi.inc
include protmode.inc
include screen.inc
include sft.inc
include sigdef.inc
include syscalls.inc
include sysserv.inc
include tkexport.inc
include vector.inc
include vpb.inc
include proc.inc
include kmode.inc
include osconst.inc
include dossymc.inc
include infoseg.inc
include except.inc
include pgexport.inc
include vmexport.inc
include sem.inc
include curdir.inc
include dirent.inc
include tkptda.inc
include smexport.inc
.cref
.list

        CpuMode 386
        ModName , FNO_HARDERR_ASM

        ReferGlobal     HardErrPkt,BYTE
        ReferGlobal     Err_Table_24,BYTE
        ReferGlobal     TaskData,WORD
        ReferGlobal     NULdev,BYTE
        ReferGlobal     pLAZCur,DWORD
        ReferGDT        GDT_VPB,WORD            ; VPB BMP selector

        ReferFlat       _pTCBCur,DWORD

        EXTRNFAR        HexToAscii
        EXTRNFAR        $TJMP
        EXTRNFAR        StrCpy
        EXTRNFAR        FuWord
        EXTRNFAR        SysServChkDaemon
        EXTRNFAR        SysServWake
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun
        EXTRNFAR        AllocReqPacketWait
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        DevIOCall2
        EXTRNFAR        POST_SIGNAL

        EXTRN           ErrMap24:BYTE
        EXTRN           ErrMap24End:BYTE
        EXTRN           MsgMap24:BYTE
        EXTRN           MsgMap24End:BYTE
        EXTRN           demMapSeg:NEAR
        EXTRN           VDHPOPINT:NEAR

        EXTRN32         _TKWakeup
        EXTRN32         _TKSleep


;***    Constants
;
;       Internal responses.  These are the codes returned by the
;       hard error daemon.  The HardError call does *NOT* return
;       these errors.  It returns the HDERR_* codes (defined in
;       harderr.inc).  The int 24 handlers also return the HDERR_* codes.
;       Do NOT export the HED_* defines.

HED_FAIL                equ     0
HED_ABORT               equ     1
HED_RETRY               equ     2
HED_IGNORE              equ     3
HED_ACKNOWLEDGE         equ     4

HE_POPUP_HAPPENED       equ     0001h           ; a popup was displayed
HE_INT24_POPUP          equ     8000h           ; default INT 24 hdlr popup
HE_HEACTION             equ     4000h           ; called from HEAction

CONSTANTS       SEGMENT

HdErrRet        db      ?       ; return value
HdErrPt         dd      ?       ; pointer to packet for hard error

CONSTANTS       ENDS

_DATA   SEGMENT
                public  CADHandlerActive     ;fix 24690
CADHandlerActive dd    0                     ;    24690

; _HardErrSem is used to serialize the hard error processing.           69005
; If _HardErrSem is owned,
; then it is safe to update _HardErrTCB and _SwapFullPTDA.              69005
; Possible states of _HardErrSem are:                                   69005
;  =  0   Semaphore not owned.                                          69005
;  = -1   Semaphore is owned by TCB set in _HardErrTCB.                 69005
                public  _HardErrSem           ; Hard error semaphore.   69005
_HardErrSem      db    0                      ;                         69005

; _HardErrTCB is the pointer to the TCB which caused the current        69005
; hard error.                                                           69005
; Possible states of _HardErrTCB are:                                   69005
;  = pTCB Pointer to TCB which "owns" the active hard error.            69005
;  =  0   No hard error is active.                                      69005
;  = -1   _tkTermThread has either terminated the thread which owns     69005
;         the current hard error, or thread 1 of the process which      69005
;         owns the current hard error.                                  69005
                public  _HardErrTCB           ;                         69005
_HardErrTCB      dd    0                      ; TCB which owns harderror69005

; _SwapFullPTDA is the pointer to the PTDA which owns the current       69005
; hard error.                                                           69005
; Possible states of _SwapFullPTDA are:                                 69005
;  = pPTDA Pointer to PTDA which "owns" the active swap full hard error.69005
;  =  0   No swap full hard error is active.                            69005
;  = -1   _tkTermThread has terminated thread 1 of the process which    69005
;         owns the current hard error.                                  69005
                public  _SwapFullPTDA         ;                         69005
_SwapFullPTDA    dd    0                      ; Process with swap full. 69005

_DATA   ENDS

MVDMINSTDATA    SEGMENT

                public  _pvheVDM
_pvheVDM        dd      0       ; pointer in the V86 kernel of the VHE struc

MVDMINSTDATA    ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT


;**     HardError - signal a hard error to the daemon
;
;   HardError is used to construct a packet to hand to the hard error daemon at
;   task level.  We wait until the internal hard-error region is free and then
;   copy in the appropriate data, consisting of a fixed portion:
;
;       Process ID
;       Allowed bits
;       Extended error code
;
;   and a variable portion depending on type of error (see below).  We then
;   start up the hard error daemon with a pointer to this block on his stack.
;   He is expected to process this error and return to us with some type of
;   action:
;
;       ax = HED_FAIL   -- fail system call
;       ax = HED_ABORT  -- abort process
;       ax = HED_RETRY  -- retry error
;       ax = HED_IGNORE -- ignore error
;       ax = HED_ACKNOWLEDGE -- acknowledged error (FSDs only)
;
;   We examine this return and make sure that it agrees with what we are allowed
;   to do.  In the case that we cannot perform the action, the least-restrictive
;   choice is made and the process repeats:
;
;       can't retry => ignore
;       can't ignore => fail
;       can't fail => abort
;
;   In the case of ABORT, we set a global flag to indicate that this process
;   should be killed at the next opportunity; we then change the abort into
;   fail and let the remainder of the system call clean up.  The "next"
;   opportunity is at console I/O blocking and at system call exit.
;
;   Different errors pass in different data:
;
;       Packet error:
;           (cx)    drive number (-1 for char devices)
;           (ds:si) long pointer to device driver header
;           (es:bx) long pointer to device driver packet
;
;       Swap disk request:
;           (ds:si) long pointer to DPB
;           (es:di) long pointer to VPB
;           (bx)    handle of VPB
;
;       Share error:    VDM only
;           (ds:si) long pointer to SFT of offending file
;
;       FCB problem:    VDM only
;           nothing
;
;       Lock problem:   VDM only
;           (ds:si) sft of file with lock violation
;
;       File system bad:
;           (ds:si) long pointer to DPB
;
;       Network error:
;           (cx)    drive number (-1 for char devices)
;           (ds:si) long pointer to device name
;
;       Drive locked:
;           (bl)    drive number (0=A)
;
;       Send message to hard error daemon:
;           (cx)     number of message argument strings
;           (dx)     message number
;           (ds:esi) pointer to message arguments (ignored if cx == 0)
;
;           if (dx) = HEMessage_Template
;              MsgTemp -> message template
;              MsgLen == template length
;
;           In this case, the message arguments are substituted into the
;           message template, not into a message in the message file.
;
;           This hard error type doesn't terminate the process if
;           there is an abort response. It is left up to the caller
;           to terminate the process. The AutoFail is not checked
;           for this call.  The GP fault hard error mechanism checks
;           its own bit in AutoFail before it calls this routine.
;
;       Send message to hard error daemon if no AutoFail:
;           (cx)    number of message argument strings
;           (dx)    message number
;           (ds:si) pointer to message arguments (ignored if cx == 0)
;
;           if (dx) = HEMessage_Template
;              MsgTemp -> message template
;              MsgLen == template length
;
;           In this case, the message arguments are substituted into the
;           message template, not into a message in the message file.
;
;           This hard error type terminates the process if there is
;           an abort response. The AutoFail is checked for this call.
;
;          HEFSH_MESSAGE type hard errors are treated as Prot mode hard
;          errors always even if from 3xbox. (Other 'real' hard errors
;          in real mode are put in abeyance till we reach exit_kmoderm,
;          and then we suddenly realise that we have to have a hard error
;          pop up and issue an int 24; this is done so that all things can
;          be cleaned up even if int 24 is hooked by somebody. AND BECAUSE
;          of this, HEFSH_MESSAGE can be treated as protmode all the time)
;
;   Since the hard error daemon may be distributed separately from MSDOS, we
;   must presume that it cannot handle super-new errors.  Therefore, it is
;   assumed that the error handler will call getextendederror to get the info
;   that he understands.  We need to set up this info based on ExtErr.
;
;   Default responses is ALWAYS "RETRY" unless "RETRY" isn't one of the
;   allowed options, then it uses "ABORT".
;
;   Inputs:     AX is hard error type
;               TCBAllowed set by caller to indicate allowed actions
;               [AutoFail] set if we should always fail hard error
;               TCBJfnFlag set if we should always fail hard error
;               [ExtErr] set to extended error (except for HEPacket and
;               HEMessage and HEFSHMessage).
;               Various other data depending on hard error type (documented
;               above).
;
;   Outputs:    Carry set if abort or fail action
;               otherwise:
;                       ax = 0 to ignore
;                       ax = 1 to retry
;
;   Registers modified: AX, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure HardError,far16

ArgVar    hrderr_Message,DWORD          ; message template
ArgVar    hrderr_MsgLen,WORD            ; message template length

LocalVar  he_response,WORD              ; user response
LocalVar  he_flags,WORD                 ; general flags
LocalVar  he_hetype,WORD                ; hard error type (AX)
LocalVar  he_pTCB,DWORD                 ;
LocalVar  he_PktUnit,WORD               ; these two make sense only for
LocalVar  he_PktStatus,WORD             ; packet type errors
LocalVar  he_errcode,WORD               ; real error code
LocalVar  he_msgnum,WORD                ; message number for hard error
LocalVar  he_packeterr,WORD             ; error code from packet
LocalVar  lazflag,BYTE                  ; lazy writer

Entry   HardError,faronly

        cmp     al,HEPacket             ; is this a packet error?
        jne     short he0               ; no need to setup pktunit and errorcode
        SaveReg <dx,di>
        mov     dl,es:[bx].PktUnit      ;
        sub     dh,dh                   ; dx = pktunit
        mov     di,es:[bx].PktStatus    ; di = pktstatus
        call    far ptr h_S2HardError
        RestoreReg <di,dx>
        RETP

Entry   S2HardError,faronly             ; harderror for Strat2 error codes
;
; This is always a packet type error code
; and on entry in addition, ax (HEPacket), cx = drive etc..,
; dx = pktunit and di = packet error
;
he0:

EnterProc <HED_FAIL,0,ax,<dword,0>,dx,di>       ; initialize stack frame

        pushad                          ; save all regs (just in case)
        push    ds
        push    es

        mov     [he_flags],ax           ; save bit away
        and     [he_flags],HE_INT24_POPUP

        and     ax,not HE_INT24_POPUP   ; (AX) = hard error type
        mov     [he_hetype],ax

        push    ds                      ; check for lazywriter tcb
        push    es
        CpuMode 386

        SaveReg <eax,ebx,ecx,edx>       ; save regs
        FlatContext <ds>,ax             ; removed es for 69005
        mov     ebx,[_pTCBCur]          ;
        mov     [he_pTCB],ebx           ;

; If we are currently handling a "swapper full" hard error and          69005
; another "swapper full" error comes in (from either the same process   69005
; or another process), we will just return to the caller. We don't      69005
; want to queue other "swapper full" hard errors behind the current     69005
; one because there is a possibility of deadlock with the harderr       69005
; daemon which must be able to display the pop up message.              69005

        mov     al,[ebx].TCBAllowed     ;                               69005
        cmp     al,SM_ALLOWED_FLAGS     ; is this harderr "swap full"?  69005
        jnz     short he02              ; no. let thread continue.      69005

        cmp     _SwapFullPTDA,0         ; already have a "swap full"?   69005
        jz      short he02              ; no. go process it.            69005

        cmp     _SwapFullPTDA,-1        ; swap full process terminated? 69005
        jz      short he02              ; yes. handle this swap full    69005
                                        ; since old swap full process   69005
                                        ; doesn't hold resources.       69005
; Already processing swap full and another one came in. Return.         69005
        mov     [he_response],HDERR_ACK ; since harderr is already processing
        stc                             ;  the swap full error for this process
        jmp     short he03              ;  just return
he02:   clc                             ;
he03:   RestoreReg <edx,ecx,ebx,eax>    ; restore regs
        jnc     short he04              ; jmp if harderror not busy
        pop     es                      ;
        pop     ds                      ;
        jmp     he9                     ;

he04:   DosContext <es>
        cmp     CADHandlerActive, 1     ; fix 24690  test if doing reboot
        jne     short notcad            ;
        push    bp                      ;
        mov     bp,ss:[TKOPTDA.CurrTCB] ;
        or      [bp].TCBMiscFlags,TMF_IGNORE_HE ;disable HE pop ups
        pop     bp                      ;
notcad:                                 ; end 24690
        push    ebx
        xor     bl,bl
        mov     lazflag,bl
        mov     ebx,[_pTCBCur]          ; Is this the lazywriter process?
        cmp     ebx,es:[pLAZCur]        ;
        pop     ebx
        CpuMode reset
        pop     es
        pop     ds
        jne     short chkdohard
        inc     lazflag                 ; lazywriter tcb
        assume  ds:nothing,es:nothing
        jmp     short generror

chkdohard:
        assume  ds:nothing,es:nothing
        push    bp
        mov     bp,ss:[TKOPTDA.CurrTCB] ; check if we are ignoring hard errors
        test    [bp].TCBMiscFlags,TMF_IGNORE_HE
        pop     bp
        jne     he8                     ; jump if we are ignoring hard errors.

generror:
        call    GenErrCode              ; examine error type and gen real error

        cmp     ax,HEMessage            ; don't autofail this hard error
        je      short he1

        cmp     [he_errcode],ERROR_INTERRUPT
        je      he8                     ; interrupted operations always failed

        cmp     lazflag,0               ; lazywriter tcb
        jne     short dontcare

; DOSERR_HARDERR_FLAG is set by the DosError API as a means for an
; application to suppress hard error popups.
        test    ss:[TKOPTDA.AutoFail],DOSERR_HARDERR_FLAG ; do we automatically fail?
        jnz     he8                     ; jump if we automatically fail

dontcare:
; JFN_Fail can be set during DosOpen (bit 13 of ulOpenMode flags), and
; by various file system routines that wish to suppress popups.
        push    bp
        mov     bp,ss:[TKOPTDA.CurrTCB]
        test    [bp].TCBJfnFlag,JFN_FAIL
        pop     bp
        jz      he01                    ; 83566 - do not suppress harderr msg
        push    ebx                     ; 83566
        push    eax                     ; 83566
        push    ds                      ; 84994 - save DS
        FlatContext <ds>,ax             ; 84994 - ensure DS is flat
        mov     ebx,[he_pTCB]           ; 83566 - is harderr for "swap full"?
        mov     al,[ebx].TCBAllowed     ; 83566   if so, forget the JFN_FAIL
        cmp     al,SM_ALLOWED_FLAGS     ; 83566   and do harderr anyway because
        pop     ds                      ; 84994 - restore DS
        pop     eax                     ; 83566   JFN_FAIL is left after FS
        pop     ebx                     ; 83566   operation is complete
        jnz     he8                     ; 83566 - not swap full, so skip msg

he01:   cmp     ax,HEFSHMessage         ; message from FSHelper?
        je      short he1               ;  yes, treat it as a prot mode always

        call    VDMHardError            ; special case this for a VDM?
        jc      he8                     ;  yes, just fail hard error

he1:    call    ChkHEDaemon             ; check for daemon
        jc      he8                     ; no daemon is listening, go fail

; At this point, we need to serialize on _HardErrSem. We will use       69005
; the XCHG instruction to make this atomic so it works for SMP too.     69005

        SaveReg <eax,ebx,ecx,edx,ds,es>
        FlatContext <ds,es>,bx          ;                               69005
serialize:                              ;                               69005
        mov     al,-1                   ; -1 means _HardErrSem owned.   69005
        xchg    _HardErrSem,al          ; Atomic swap.                  69005
        cmp     al,0                    ; Was _HardErrSem owned?        69005
        jz      short gotsem            ; No, but we own it now.        69005

; Can't get the semaphore. Take a nap and retry to get serialization    69005
; again when we wake up.                                                69005

hezz:   CCALL32 _TKSleep <offset FLAT:_HardErrSem, -1, 0, 0>          ; 69005
        cmp     eax,0                   ; error from wakeup ?           69005
        jnz     short hezz              ;  yes. go back to sleep.       69005
        jmp     short serialize         ; go get serialization          69005
gotsem:                                 ; We own the _HardErrSem.       69005
        mov     ebx,[_pTCBCur]          ; current tcb pointer           69005
        mov     _HardErrTCB,ebx         ; save owner for tktermthread   69005
        mov     al,[ebx].TCBAllowed     ;                               69005
        cmp     al,SM_ALLOWED_FLAGS     ; swap full?                    69005
        jnz     short he1a              ; not swap full.                69005
        mov     eax,[ebx].TCBpPTDA      ; get ptda address              69005
        mov     _SwapFullPTDA,eax       ; remember we had swap full err 69005
he1a:                                   ;                               69005
        RestoreReg <es,ds,edx,ecx,ebx,eax>
        assume  ds:nothing,es:nothing   ;                               69005
        mov     bx,offset DOSGROUP:HardErrPkt+ssrp_pkt
        push    ax                      ;
        call    FillHEPacket            ; fill in the details
        pop     ax                      ;

        cmp     ax,HEFSHMessage         ; message from FSHelper?
        je      short he2               ; Yes. Process.
        cmp     ax,HEMessage            ; message?
        jne     short he3               ; no, continue process
he2:    cmp     dx,HEMessage_Template   ; message file or template?
        jne     short he3               ; message file, don't copy template
        lds     si,[hrderr_Message]     ; (DS:SI) -> message template
        mov     cx,[hrderr_MsgLen]      ; (CX) = template length
        call    CopyTemplate            ; (ES:DI) -> hard error packet

he3:    DosContext ds                   ; DS:BX = DOS response packet

        mov     bx,offset DOSGROUP:HardErrPkt
        push    bx
        CALLFAR SysServWake             ; start up daemon
        pop     bx
        add     bx,ssrp_pkt
        mov     [he_response],HDERR_ACK
        test    [bx].HE_Allowed,80h     ; swap full error ?
        jnz     he9                     ;  jmp if yes

        call    GetHEResponse           ; wait for response from HE daem
        mov     [he_response],ax        ; save it away

 ; If the swap full error occurs, the process causing it will call into
 ; harderror with the 80h bit set in it's TCBAllowed flags. Since
 ; we cannot block this thread, because he is usually holding resources
 ; we need to complete the hard error popup, we allow the process
 ; to maintain ownership of the _HardErrSem and let the process         69005
 ; continue running. The harderror daemon will do a systemservice, which calls
 ; HEAction, when it has finished retrieving the users response. We
 ; finish processing the swap full error under the harderror daemons
 ; context.
 ;
 ; NOTE: Other swap full errors caused by the same process as the one we are
 ;       already processing are ignored. Other non-swap full errors are
 ;       delayed until we finish processing the original request.       69005

Entry   S3HardError,faronly

; Indicate that a popup occurred. We can also check this later to       69005
; determine whether or not we were invoked from error paths             69005
; such as direct jumps to label he8, or if we have a user response      69005
; to a pop up message.                                                  69005

        or      [he_flags],HE_POPUP_HAPPENED

he8:    FlatContext <ds,es>,ax          ; d59805   ; added es for 69005
        mov     ebx,_pTCBCur            ;
        mov     cl,[ebx].TCBAllowed     ; save allowed flags
        SaveReg <ds,es,ecx>             ;
        test    [he_flags],HE_HEACTION  ; called from HEAction ?
        jz      short he8a              ;  jmp if no

; tkTermThread sets _SwapFullPTDA and _HardErrTCB to -1 if thread 1 of  69005
; the process has terminated. If only the specific thread has been      69005
; terminated, the _HardErrTCB will be set to -1 and the _SwapFullPTDA   69005
; is still set to the ptda of the process which got the swap full error.69005
; In this case, pass ProcessHE the thread 1 tcb pointer so that it      69005
; will then terminate the process.                                      69005

        cmp     _HardErrTCB,-1          ; is the thread still around?   69005
        jz      short threadgone        ; no. check ptda.               69005
        mov     [ebx].TCBAllowed,SM_ALLOWED_FLAGS
        jmp     short he8a              ; go call ProcessHE             69005
threadgone:                             ;                               69005
        cmp     _SwapFullPTDA,-1        ; is the process still around?  69005
        jnz     processok               ; yes. pass thread 1 tcb.       69005

; Process must have terminated before user replied to pop up.           69005
; Bypass call to ProcessHE since that code references tcb and ptda      69005
; which are no longer valid.                                            69005
processbad:                             ;                               69005
        mov     ax,HDERR_ABORT          ; so carry flag get set on exit 69005
        jmp     he8e                    ; jump around ProcessHE         69005

; Thread is gone but process is still here.                             69005
processok:                              ;                               69005
        mov     eax,_SwapFullPTDA       ;                               69005
        mov     eax,[eax].ptda_pTCBHead ; thread 1 tcb address          69005
        or      eax,eax                 ; still around?                 69005
        jz      short processbad        ; no. don't call ProcessHE      69005
        mov     [eax].TCBAllowed,SM_ALLOWED_FLAGS                     ; 69005
        mov     [he_pTCB],eax           ; pass it to ProcessHE and      69005
        mov     [he_response],HED_ABORT ;  indicate to abort            69005
he8a:                                   ;                               69005
        mov     ax,[he_response]        ;
        call    ProcessHE               ; convert the returned action into
he8e:                                   ;                               69005
        mov     [he_response],ax        ; one that's allowed
        RestoreReg <ecx,es,ds>          ; added es for 69005

; If a pop up happened, then we own the serialization and we also know  69005
; that we may have blocked others. Wake up the sleepers, if any,        69005
; then release serialization.                                           69005
        test    [he_flags],HE_POPUP_HAPPENED  ; has there been a popup? 69005
        jz      short he9               ; jump if not                   69005

; Unconditionally wake up any sleepers. I had considered keeping a      69005
; count of the number of sleepers waiting on _HardErrSem, but found     69005
; this is easier and more straigtforward to implement. If a count were  69005
; kept, it is possible that one of the sleeping threads could be        69005
; terminated before the wake up occurred, so the count would not be     69005
; correct. Also by not keeping a count, don't have to worry about SMP   69005
; implications by making the counter atomic.                            69005

; Release serialization. Do this prior to doing the wake up so that     69005
; any other processors (SMP) will be able to get the semaphore.         69005
        mov     _SwapFullPTDA,0         ;                               69005
        mov     _HardErrTCB,0           ;                               69005
        mov     _HardErrSem,0           ; Release semaphore.            69005

        push    cx                      ; save TCBAllowed flags         69005
        CCALL32 _TKWakeup <offset FLAT:_HardErrSem, 0, 0>             ; 69005
        pop     cx                      ; restore TCBAllowed flags      69005

        test    [he_flags],HE_HEACTION  ; called from HEAction?         69005
        jz      short he9               ; no. must pop regs.            69005
        mov     ebx,_pTCBCur            ; get current tcb pointer       69005
        mov     [ebx].TCBAllowed,cl     ; restore allowed flags
        jmp     short he10              ;                               69005

he9:    pop     es                      ; restore caller's registers
        pop     ds
    assume ds:nothing,es:nothing
        popad

he10:   cmp     [he_response],HDERR_RETRY ; test for retry return code
        jne     short he15
        cmp     [he_errcode],ERROR_DISK_CHANGE
        jne     short he15

;       (ES:BX) = request packet address
;       (DS:SI) = device driver header

        mov     al,byte ptr [he_PktUnit] ; (AL) = unit number to map
        call    SetLogicalDrive

he15:   push    bx                      ; Save BX
        mov     bx,1                    ; BX = 1, so leavproc is done
        test    [he_flags],HE_HEACTION  ; called from HEAction
        jz      short he17              ;  jmp if no
        mov     bx,2                    ; BX = 2, so leaveproc is not done
he17:   mov     ax,[he_response]        ;
        cmp     ax,HDERR_ACK            ;
        jz      short he18              ; no CY for ACK
        cmp     ax,HDERR_ABORT          ; set CY if fail or abort
        cmc                             ;
he18:   dec     bx                      ; Does not modify CF
        pop     bx                      ; Restore BX
        jnz     short he20              ; jmp if leaveproc is not to be done

        LeaveProc
he20:   RETP
EndProc   HardError


;**     GenErrCode - generate a dos-standard error from an error type.
;
;       Given an error type and some additional information, we generate
;       a dos-standard error.  This error is among the set returnable to the
;       application.
;
;       ENTRY   (ax) has hard error type.
;               (bp) = HardError LocalVar stack frame pointer
;
;               if (ax) == HEPacket
;                  (es:bx) point to a DD request packet.
;                  Pull error code from the packet.
;               else if (ax) == HEMessage
;                  (dx) = error code/message number
;               else if (ax) == HEFSHMessage
;                  (dx) is meaningless
;
;               else // (ax) != HEPacket, HEMessage, HEFSHMessage
;                  [ExtErr] has real error code.
;
;       EXIT    [he_packeterr] has error from packet or ERROR_I24_GEN_FAILURE
;               [he_errcode] has DOS error code or ERROR_GEN_FAILURE,
;               [TCBFailErr] has DOS error code for prot system call,
;               [ExtErr] has DOS error code for a VDM GetExtendError call,
;               [he_msgnum] has message number to display in hard error.
;
;       USES    FLAGS
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure GenErrCode,near,local
        assume  ds:nothing,es:nothing
        SaveReg <ax,bx,cx>

        mov     [he_packeterr],ERROR_I24_GEN_FAILURE
        mov     [he_errcode],ERROR_GEN_FAILURE

        cmp     ax,HEPacket             ; is this a packet error?
        je      short gec20             ;  yes, go translate packet error

        cmp     ax,HEMessage            ; (DX) = message number on this type
        je      short gec10

        cmp     ax,HEFSHMessage         ; (DX) = message number on this type
        je      short gec10

        mov     dx,ss:[TKOPTDA.ExtErr]  ; retrieve already-set error

;       (DX) = error code
;       (AX) = error type

gec10:  mov     [he_msgnum],dx          ; assume he_msgnum = he_errcode
        cmp     ax,HEFSHMessage
        mov     ax,dx
        je      short gec60
        jmp     short gec50

;       The error is in the device driver packet at (es:bx)

gec20:  mov     ax,[he_PktStatus]       ;
        and     ax,STECODE              ; (ax) = packet error code

;       Convert DD packet error number to error number via ErrMap24

gec30:  SaveReg <ds>
        DosContext ds                   ; address mapping table
        mov     bx,ax                   ; (bx) = packet error code
        mov     cx,ax                   ; (cx) = packet error code
        lea     bx,ErrMap24[bx]         ; (bx) -> error number
        mov     [he_msgnum],ax          ; assume he_msgnum = he_errcode ;### B725763
        cmp     bx,offset DOSGROUP:ErrMap24End  ; beyond end of table?
        jae     short gec40             ; yes, go use (ax) as correct error
        mov     [he_packeterr],ax       ; save the packet error for a VDM int24
        mov     al,[bx]                 ; (ax) = error number from table
        xor     ah,ah
        mov     [he_msgnum],ax          ; assume he_msgnum = he_errcode

;       Convert DD packet error number to message number via MsgMap24

        mov     bx,cx                   ; (bx) = packet error code
        lea     bx,MsgMap24[bx]         ; (bx) -> message number
        cmp     bx,offset DOSGROUP:MsgMap24End  ; beyond end of table?
        jae     short gec40             ; yes, go use (ax) as correct message
        cmp     byte ptr [bx],0         ; is there a message?
        je      short gec40             ; no. use (ax) as correct message
        mov     bl,[bx]                 ; (bx) = message number from table
        xor     bh,bh
        mov     [he_msgnum],bx          ; save he_msgnum from table
gec40:  RestoreReg <ds>
    ASSUME  DS:NOTHING
        mov     ss:[TKOPTDA.ExtErr],ax

gec50:  mov     [he_errcode],ax         ; store error for caller
        mov     bx,ss:[TKOPTDA.CurrTCB]
        mov     ss:[bx].TCBFailErr,ax   ; set extended error for possible fail

gec60:  RestoreReg <cx,bx,ax>
        ret
EndProc   GenErrCode

;***    CopyTemplate - copy message template into hard error packet
;
;       ENTRY   (DS:SI) -> message template
;               (ES:DI) -> hard error packet
;               (CX) == template length
;
;       EXIT    none
;
;       USES    CX, DI, SI, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure CopyTemplate,near,local
        cmp     cx,HEMaxMsgLen      ; template longer than max?
        jbe     short ct1           ; no, go fill in packet
        mov     cx,HEMaxMsgLen      ; set proper length
ct1:    mov     es:[di].HE_MsgLen,cx    ; set template length
        lea     di,[di].HE_Msg      ; (ES:DI) -> template buffer
        rep     movsb               ; copy template into hard error packet
        ret
EndProc   CopyTemplate


;**     ChkHEDaemon - check for existence of HE Daemon
;
;       Return an indication of whether or not there is anyone listening.
;
;       ENTRY   none
;
;       EXIT    carry clear =>
;                   there is a hard error daemon waiting for input
;               carry set =>
;                   no daemon is available
;
;       USES    flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure ChkHEDaemon,hybrid
        SaveReg <bx,es>
        mov     bx,SysServ_HardErr      ; identify which daemon to check for
        CALLFAR SysServChkDaemon        ; get status
        RestoreReg  <es,bx>
        ret
EndProc   ChkHEDaemon


;**     FillHEPacket - examine registers and error type to decide what
;       information is to be placed into the hard error packet.  The fixed
;       portion consists of:
;
;           WORD    screen group
;           WORD    pid
;           WORD    extended error
;           WORD    allowed action bitmask
;           WORD    default action response
;
;       The variable portion depends on the error type:
;
;       (ax) == HEPacket
;           Generate a device name from the device driver header in DS:SI
;               (cx) has drive number if block dev
;
;       (ax) == HESwapdisk
;           Generate a device name from the drive number in
;               (ds:si)->dpb_drive
;           Copy in the text of the identifier from VPB at (es:di)
;
;       (ax) == HEShare VDM only
;           Copy file name from TASKAREA->wfp_start
;
;       (ax) == HEFCB VDM only
;           No further information
;
;       (ax) == HELock VDM only
;           Copy file name from TASKAREA->wfp_start
;
;       (ax) == HEBadFS
;           Generate a device name from the drive number in
;               (ds:si)->dpb_drive
;
;       (ax) == HEDrvLocked
;           Generate a device name from the drive number in bl
;
;       (ax) == HEMessage or (ax) == HEFSHMessage
;            Send message to hard error daemon
;               (cx) = number of argument strings
;               (dx) = message number (already put into he_msgnum)
;               (ds:si) = message arguments (ignored if cx == 0)
;
;       ENTRY   (ax) = error type
;               (bx) = DOSGROUP offset of packet to fill in
;               (bp) = HardError LocalVar stack frame pointer
;               (cx) (dx) (ds:si) and (es:di) contain information as above
;
;       EXIT    none
;
;       USES    All but BP
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure FillHEPacket,near,local
        push    es
        push    di
        DosContext es
        mov     di,bx                   ; ES:DI = hard error packet

        CALLFAR $TJMP                   ; dispatch to fill in variable portion
        db      (fhpend-fhpstart-2)/2   ; allowed set of errors
        dw      DOSHIGH3CODE:fhpie      ; internal error
        .errnz  HEPacket-0
fhpstart dw     DOSHIGH3CODE:fhp10      ; packet
        .errnz  HESwapdisk-1
        dw      DOSHIGH3CODE:fhp30      ; disk change
        .errnz  HEShare-2
        dw      DOSHIGH3CODE:fhp45      ; VDM sharing
        .errnz  HEFCB-3
        dw      DOSHIGH3CODE:fhp50      ; VDM FCB problem
        .errnz  HELock-4
        dw      DOSHIGH3CODE:fhp55      ; VDM lock problem
        .errnz  HEBadFS-5
        dw      DOSHIGH3CODE:fhp60      ; can't understand disk format
        .errnz  HENet-6
        dw      DOSHIGH3CODE:fhp65      ; reserved for net error
        .errnz  HEDrvLocked-7
        dw      DOSHIGH3CODE:fhp70      ; drive is locked
        .errnz  HEMessage-8
        dw      DOSHIGH3CODE:fhp80      ; send message to hard error daemon
        .errnz  HEFSHMessage-9
        dw      DOSHIGH3CODE:fhp75      ; send message to hard error daemon if no autofail
fhpend  label   word

;       An unrecognized error was passed in

fhpie:  InternalError <FillHEPacket: unknown hard error>

;       Generate a device name from the device driver header at ds:si
;       and from the drive number in cx

fhp10:  SaveReg <di>
        lea     di,[di].HE_buf          ; (es:di) -> buffer
        test    [si].SDevAtt,DEV_CHAR_DEV ; is it a character device?
        jnz     short fhp15             ; yes, go convert the name

;       Generate the block device name from the drive in cl

        mov     ax,':A'                 ; drive:
        add     al,cl                   ; plus drive number
        stosw                           ; save in buffer
        jmp     short fhp25             ; go null terminate

fhp15:  lea     si,[si].SDevName        ; ds:si points to name
        mov     cx,4
        rep     movsw                   ; copy entire name

fhp20:  cmp     byte ptr es:[di-1]," "  ; was last char a space?
        jnz     short fhp25             ; no, go nul-terminate
        dec     di                      ; back up over space
        jmp     short fhp20             ; go try again

fhp25:  mov     byte ptr es:[di],0      ; nul terminate the string
        RestoreReg <di>                 ; restore pointer to block
        mov     es:[di].HE_cAsciz,1     ; single string
        jmp     fhp100                  ; go fill in remainder of packet

;       The error is a change-disk request. Generate a drive name from dpb
;       at (ds:si).  Generate a name from vpb at (cx:dx)

fhp30:  mov     al,[si].dpb_drive       ; get drive number
        add     al,'A'                  ; convert to asciz
        xor     ah,ah
        mov     word ptr es:[di].HE_buf,ax  ; drive in drive and NUL
        mov     bx,di
        lea     di,[di].HE_buf+2        ; point after drive
        pop     si
        pop     ds
        push    ds
        push    si
        chkvselhd ds,si                 ; Check pointer to VPB
        lea     si,[si].vpb_fsi.vpi_text; (ds:si) point to NUL-terminated string
        CALLFAR StrCpy                  ; copy in string
        dec     di
fhp35:  cmp     byte ptr es:[di-1]," "  ; was last char a space?
        jnz     short fhp40             ; no, go nul-terminate
        dec     di                      ; back up over space
        jmp     short fhp35             ; go try again

fhp40:  mov     byte ptr es:[di],0      ; nul terminate the string
        inc     di                      ; put the vpb signature also
        pop     si
        push    si                      ; restore ds:si -> VPB
        mov     ax,[si].vpb_fsi.vpi_ID.hiword   ;
        CALLFAR HexToAscii              ; convert the volume serial no and
        mov     byte ptr es:[di],':'    ; separate them as 'DIR' shows; and
        inc     di                      ;
        mov     ax,[si].vpb_fsi.vpi_ID.loword  ;
        CALLFAR HexToAscii              ; store it as another argument
        mov     byte ptr es:[di],0      ; null terminate VSN
        mov     di,bx
        mov     es:[di].HE_cAsciz,3     ; two strings for output
        jmp     fhp100                  ; go fill in remainder of packet

;       The error is a sharing violation from a VDM.  Copy in a string
;       from TASKAREA->wfp_start

fhp45:  TaskContext ds
                                        ;72417 moved from PTDA to TCB
        mov     si,ss:[TKOPTDA.CurrTCB]
        mov     si,ss:[si].TCBWFP_Start ; (ds:si) point to string
                                        ;72417 end
        push    di
        lea     di,[di].HE_buf          ; (es:di) point to destination buffer
        CALLFAR StrCpy                  ; copy string to output buffer
        pop     di
        mov     es:[di].HE_cAsciz,1     ; single output string
        jmp     fhp100                  ; go fill in remainder of packet
    ASSUME  DS:NOTHING

;       The error is a FCB problem from a VDM

fhp50:  mov     es:[di].HE_cAsciz,0
        jmp     fhp100                  ; go form remainder of packet to HEDaem

;       The error is a lock problem from a VDM. Copy in a string
;       from TASKAREA->wfp_start

fhp55:  TaskContext ds
                                        ;72417 moved from PTDA to TCB
        mov     si,ss:[TKOPTDA.CurrTCB]
        mov     si,ss:[si].TCBWFP_Start ; (ds:si) point to string
                                        ;72417 end
        push    di
        lea     di,[di].HE_buf          ; point at destination
        CALLFAR StrCpy                  ; copy name
        pop     di
        mov     es:[di].HE_cAsciz,1     ; single string
        jmp     fhp100                  ; go form remainder of packet to HEDaem
    ASSUME  DS:NOTHING

;       The error is an unrecognizable disk in a drive.  Generate a name from
;       the DPB at (ds:si)

fhp60:  mov     al,[si].dpb_drive       ; get drive number
        add     al,'A'                  ; convert to asciz
        xor     ah,ah
        mov     word ptr es:[di].HE_buf,ax  ; drive in drive and NUL
        mov     es:[di].HE_cAsciz,1     ; single string
        jmp     short fhp100            ; go form remainder of packet to HEDaem

;       The error is from the network.  Device name is at (ds:si)

fhp65:  mov     bx,di
        lea     di,[di].HE_buf          ; point after drive
        CALLFAR StrCpy                  ; copy in string
        mov     di,bx
        mov     es:[di].HE_cAsciz,1     ; one string for output
        jmp     short fhp100            ; go fill in remainder of packet

;       The error is due to a locked drive.  Generate a name from the drive
;       number in bl

fhp70:  add     bl,'A'                  ; convert to ascii
        xor     bh,bh
        mov     word ptr es:[di].HE_buf,bx  ; drive in drive and NUL
        mov     es:[di].HE_cAsciz,1     ; single string
        jmp     short fhp100

;       Send message to hard error daemon
;
;       (CX) = number of argument strings.
;       (DX) = is the message number to display (already in "he_msgnum").
;       (DS:ESI) = the argument string(s).

fhp75:  movzx   esi,si                  ; zero-extend high word of offset
fhp80:  cmp     cx,HEMaxSubs            ; too many substitution strings?
        jle     short fhp85             ; no, continue processing
        mov     cx,HEMaxSubs            ; (CX) = max substitutions
fhp85:  mov     es:[di].HE_cAsciz,cx    ; set the type of parameters
        jcxz    fhp100

        push    di
        lea     di,[di].HE_buf          ; (ES:DI) = argument string(s) buffer
        lea     bx,[di+HEMaxSubsLen-1]  ; (ES:BX) = end of buffer

fhp90:  lods    byte ptr ds:[esi]
        stosb                           ; copy each argument string
        cmp     di,bx                   ; end of buffer?
        jae     short fhp95             ; yes, go null terminate last sub
        or      al,al
        jnz     short fhp90
        loop    fhp90                   ; next argument string

fhp95:  mov     byte ptr es:[di],0      ; null terminate string
        pop     di

;       All the above errors join back together here.  Form the remainder of
;       the packet.

fhp100: mov     al,ss:[TKOPTDA.Cons_Loc.Scrn_Num]
        xor     ah,ah                   ; (ax) is screen group of offending proc
        mov     es:[di].HE_SG,ax        ; set screen group of packet

        mov     ax,[he_msgnum]          ; local version of message number
        mov     es:[di].HE_Error,ax     ; set error code for daemon

        push    ds                      ; check for lazywriter tcb
        CpuMode 386
        FlatContext <ds>
        mov     ebx,[_pTCBCur]          ; Is this the lazywriter process?
        cmp     ebx,es:[pLAZCur]        ;
        jne     notlazywrt
        mov     ax,ss:[TKOPTDA.ptda_Pid]
        mov     es:[di].HE_ProcID,ax    ; set process id of lazywriter
        mov     ah,4                    ; set lazywriter as title
        mov     al,[ebx].TCBAllowed
        mov     es:[di].HE_Allowed,ax   ; set allowed actions
        mov     es:[di].HE_Def,HED_RETRY; default to retry
        test    al,ALLOWED_RETRY
        jnz     short fhp101
        mov     es:[di].HE_Def,HED_ABORT; default to about if retry not allowed
        CpuMode reset
fhp101: pop     ds
        assume  ds:nothing
        jmp     fhp110

notlazywrt:
        assume  ds:nothing
        CpuMode reset
        pop     ds
        mov     ax,ss:[TKOPTDA.ptda_Pid]
        mov     es:[di].HE_ProcID,ax    ; set process id of culprit

        push    bp
        mov     bp,ss:[TKOPTDA.CurrTCB]
        test    [bp].TCBAllowed,ALLOWED_ACKNOWLEDGE
        jnz     short fhp102
        or      [bp].TCBAllowed,ALLOWED_ABORT ; abort is ALLOWED in all cases
fhp102: mov     al,[bp].TCBAllowed      ; EXCEPT ACKNOWLEDGE
        pop     bp

        xor     ah,ah
        cmp     ss:[TKOPTDA.ptda_ProcType],PT_DETACHED
        jne     short fhp105

        or      ax,ALLOWED_DETACHED     ; set process detached bit
fhp105: mov     es:[di].HE_Allowed,ax   ; set allowable actions

        mov     es:[di].HE_Def,HED_RETRY; default to retry
        test    al,ALLOWED_RETRY
        jnz     short fhp110
        mov     es:[di].HE_Def,HED_ABORT; default to about if retry not allowed

fhp110: pop     ax
        pop     ax
        ret
EndProc   FillHEPacket


;**     GetHEResponse set a simple semaphore and await the response
;       from the hard error daemon
;
;       The semaphore is merely the return value from the hard error daemon.
;
;       ENTRY   none
;
;       EXIT    ax has response
;
;       USES    bx, cx, dx, ds, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure GetHEResponse,near,local
        DosContext  ds                  ; address the common data
        mov     HdErrRet,-1             ; signal no response
ghr1:   mov     ax,ds
        mov     bx,offset DOSGROUP:HdErrRet ; wait on semaphore
        mov     cx,-1
        mov     di,-1                   ; wait forever
        mov     dh,1                    ; uninterruptibly
        CALLFAR ProcBlock
        cmp     HdErrRet,-1             ; was there a response?
        jz      ghr1                    ; no, go back and wait
        mov     al,HdErrRet
        xor     ah,ah
        ret
EndProc   GetHEResponse


;**     ProcessHE uses the allowed bits and the user-provided response to
;       determine a course of action.
;
;       ENTRY   (ax)    contains user response.  One of:
;                       HED_FAIL  HED_ABORT  HED_RETRY  HED_IGNORE
;                       HED_ACKNOWLEDGE
;               (bp)    HardError LocalVar stack frame pointer
;
;       EXIT    (ax)    contains modified user response.  One of:
;                       HDERR_FAIL HDERR_ABORT HDERR_RETRY HDERR_IGNORE
;                       HDERR_ACKNOWLEDGE
;       USES    all
;
;       For hard error type HEMessage this routine doesn't
;       terminate the process. It is left up to the call
;       to terminate the process if abort is the response.
;
;       If the user response is HDERR_FAIL or HDERR_ABORT,
;       TMF_IGNORE_HE bit in TCBMiscFlags is set to ignore
;       any more popups during this system call.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure ProcessHE,near,local
        mov     si,ss:[TKOPTDA.CurrTCB] ; (SS:SI) = current TCB
        mov     cl,ss:[si].TCBAllowed   ; (CL) = allowed bits
        FlatContext ds
        mov     esi,[he_pTCB]           ; (ESI) = pTCB
        mov     edi,[esi].TCBpPTDA      ; (EDI) = pPTDA
        CALLFAR $TJMP                   ;
phetable:       db      HED_ACKNOWLEDGE ; max possible return code
        dw      DOSHIGH3CODE:phe10      ; invalid return code ?
        .errnz  HED_FAIL-0              ; convert it to fail
        dw      DOSHIGH3CODE:phe10      ; fail response
        .errnz  HED_ABORT-1
        dw      DOSHIGH3CODE:phe20      ; abort response
        .errnz  HED_RETRY-2
        dw      DOSHIGH3CODE:phe50      ; retry response
        .errnz  HED_IGNORE-3
        dw      DOSHIGH3CODE:phe40      ; ignore response
        .errnz  HED_ACKNOWLEDGE-4
        dw      DOSHIGH3CODE:phe90      ; acknowledge response

;       Code for invalid return code; convert the return code to fail
;       Fail option chosen by user

phe10:  mov     ax,HED_FAIL
        test    cl,ALLOWED_FAIL         ; are we allowed to fail?
        jnz     short phe30             ; yes, check if popup has occurred

                                        ; FAIL WASN'T ALLOWED SO WE MUST ABORT
                                        ; CODE RUNS THROUGH HERE ; INTENTIONAL

;       Abort option chosen by user; or user chose fail but fail was not allowed

phe20:  mov     ax,HED_ABORT
        cmp     [he_hetype],HEMessage   ; do the termination ourselves
        je      short phe30             ;

;       If this is a VDM process and the response is abort, jump to exit.

;       BUGBUG - using undocumented undefined bit 0x80, this bit is
;        used only by the swapper (smsfta.asm) when the hardfile is running
;        out disk space for the swapfile and the user wants to kill the
;        app that caused the swapfile to grow too large

        test    cl,80h                  ;*GLFulk*
        jnz     phe25                   ;*GLFulk*
        cmp     [edi].ptda_ProcType,PT_REALMODE
        je      short phe30             ; jump if a VDM
phe25:                                  ;*GLFulk*

;       Send a terminate signal to the process with the special
;       hard error signal argument.

        SaveReg <ax,cx>
        mov     al,SIGTERM              ; Send SIGTERM signal
        mov     bl,1                    ; Send to only this process
        mov     cx,HE_SIGTERM_ARG       ; Special augument denotes hard error
        mov     dx,[edi].ptda_Pid       ; Send to process that caused hard err
        CALLFAR POST_SIGNAL             ; Send a terminate signal

;       WARNING:  This is a nonstandard use of ptda_ulExitCode,
;       ptda_ulExitType, and ptda_ulExitTID.  These values should
;       normally only get set in _TKDieSoonPTDA.  They are set here
;       purely for compatability.  (We override PS_EXITALL here.)

        mov     [edi].ptda_ulExitCode,0 ; now mark reason for exit
        mov     [edi].ptda_ulExitType,TC_HARDERR
        movzx   eax,[esi].TCBOrdinal
        mov     [edi].ptda_ulExitTID,eax
        RestoreReg <cx,ax>

;       This is the common exit for FAIL and ABORT responses.  Mark
;       the process so that no more popups can happen if this hard
;       error has already caused a popup while in this system call.

phe30:  test    [he_flags],HE_POPUP_HAPPENED    ; has there been a popup?
        jz      short phe70             ; jump if not
        or      [esi].TCBMiscFlags,TMF_IGNORE_HE
        jmp     short phe70             ; don't allow any more popups

;       User chose ignore

phe40:  mov     ax,HED_IGNORE
        test    cl,ALLOWED_IGNORE       ; are we allowed to ignore?
        jnz     short phe60             ; yes, go return final response
        jmp     short phe10             ; convert this to fail
                                        ; not allowed to ignore turns into fail
;       Retry

phe50:  mov     ax,HED_RETRY
        test    cl,ALLOWED_RETRY        ; are we allowed to retry?
        jz      phe10                   ; not allowed to retry turns into fail

;       Ignore or Retry or Acknowledge is the response.

phe60:  mov     [esi].TCBFailErr,0      ; no failing occurs, clear error code
phe70:  cmp     ax,HED_ACKNOWLEDGE      ; if ACK no translation needed
        je      short phe80             ;
        sub     ax,HED_IGNORE           ;
        neg     ax                      ; invert responses

;b721284
;cmp    ProcType,PT_REALMODE
;jne    short phe80             ; jump if not a VDM
;       FlatContext ds
;       mov     esi,[_pvheVDM]          ; (ESI) = ptr to v86 hard error packet
;       or      esi,esi                 ; is the vhe address initialized yet?
;       jz      short phe80             ;  no, just exit
;       cmp     [esi].vhe_bResp,HDERR_NORESP
;       jne     short phe80             ; jump if response already saved
;       mov     [esi].vhe_bResp,al
phe80:  ret

phe90:  mov     ax,HED_ACKNOWLEDGE
        test    cl,ALLOWED_ACKNOWLEDGE  ; are we allowed to acknowledge?
        jz      phe10                   ; not allowed to retry turns into fail
        jmp     short phe60             ; go acknowledge

EndProc ProcessHE


;**     HEAction - handle system service actions for hard errors
;
;       HEAction is called when the hard error daemon responds to a hard error.
;       We place the response into the harderror response area and then wake up
;       the waiting hard error process.
;
;       ENTRY   (BX:SI) = pointer to response packet
;       EXIT    none
;       USES    AX, BX, CX, DX, SI, DS, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure HEAction,FARONLY

LocalVar  he_response,WORD              ; user response
LocalVar  he_flags,WORD                 ; general flags
LocalVar  he_hetype,WORD                ; hard error type
LocalVar  he_pTCB,DWORD                 ;

        EnterProc
        CALLFAR FuWord                  ; retrieve action word
        jnc     short hea1              ; successfully got it, go respond
        mov     ax,HED_FAIL             ; presume we failed

hea1:   DosContext ds
        mov     HdErrRet,al             ; set up hard error return

        mov     bx,offset DOSGROUP:HardErrPkt+ssrp_pkt
        test    [bx].HE_Allowed,80h     ; swap full error?
        jz      short hea2              ; jump if not swap full.

        pushad
        SaveReg <es>
        xor     ah,ah
        mov     [he_response],ax        ;
        mov     [he_flags],HE_HEACTION  ;
        mov     [he_hetype],SM_HE_TYPE  ;
        FlatContext ds,ax               ;
        mov     eax,_HardErrTCB         ; get saved tcb pointer         69005
        mov     [he_pTCB],eax           ;
        call    far ptr h_S3HardError   ;
        RestoreReg <es>                 ;
        popad                           ;
        jmp     short heax              ;

hea2:   mov     ax,ds
        mov     bx,offset DOSGROUP:HdErrRet
        CALLFAR ProcRun                 ; wake up respondent

heax:   LeaveProc
        ret
EndProc   HEAction


;**     SetLogicalDrive - Issue IOCTL to set logical drive mapping
;
;       Sets the logical drive to unit passed.
;
;       ENTRY   (AL) = logical drive number
;               (DS:SI) = device driver header
;
;       EXIT    NONE
;
;       USES    AX, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure SetLogicalDrive,near,local
        SaveReg <bx,es>
        CALLFAR AllocReqPacketWait              ; (ES:BX) = tiled request packet
        mov     es:[bx].PktUnit,al              ; set drive
        mov     es:[bx].PktCmd,CMDSetLogMap     ; set command code
        mov     es:[bx].PktLen,PktHeadSize+LenGetSetLog     ; set packet length
        mov     es:[bx].PktStatus,0             ; initialize status

;       DS:SI = device driver header
;       ES:BX = request packet

        CALLFAR DevIOCall2                      ; call device driver

;       ES:BX = request packet to free

        CALLFAR FreeReqPacket
        RestoreReg <es,bx>
        ret
EndProc  SetLogicalDrive


;**     VDMHardError - check if special case hard error for a VDM
;
;       If not a VDM just return.  If a VDM, test to see if we special
;       case the error.  If so fill up VHE buffer in the V86 kernel with
;       the necessary information.
;
;       ENTRY:  Same as HardError
;               (BP) = HardError LocalVar stack frame pointer
;
;       EXIT:   Carry clear - do protected mode popup
;               Carry set - don't do protected mode popup
;
;       USES:   Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure VDMHardError,near,local
        test    [he_flags],HE_INT24_POPUP
        jnz     short vhd05
        cmp     ss:[TKOPTDA.ptda_ProcType],PT_REALMODE
        je      short vhd10             ; jump if real mode process
vhd05:  clc
        ret                             ; EXIT (carry clear)

vhd10:  pushad                          ; can't trash any registers
        SaveReg <ds,es>
        mov     dx,bx                   ; $TJMP trashes BX
        CALLFAR $TJMP
        db      (vhd25-vhd20)/2         ; allowed set of errors
        dw      DOSHIGH3CODE:vhd27
vhd20   dw      DOSHIGH3CODE:vhd30      ; packet
        .errnz  HEPacket-0
        dw      DOSHIGH3CODE:vhd40      ; disk change
        .errnz  HESwapdisk-1
        dw      DOSHIGH3CODE:vhd65      ; VDM sharing
        .errnz  HEShare-2
        dw      DOSHIGH3CODE:vhd60      ; VDM FCB problem
        .errnz  HEFCB-3
        dw      DOSHIGH3CODE:vhd70      ; VDM lock problem
        .errnz  HELock-4
        dw      DOSHIGH3CODE:vhd80      ; can't understand disk format
        .errnz  HEBadFS-5
        dw      DOSHIGH3CODE:vhd45      ; net errors
        .errnz  HENet-6
        dw      DOSHIGH3CODE:vhd55      ; drive is locked
        .errnz  HEDrvLocked-7
vhd25   label   word

;       An unrecognized error was passed in

vhd27:  InternalError <VDMHardError: unknown hard error>

;*      Packet error:
;               (DS:SI) is device header address
;               (ES:DX) is request packet address
;               (CX) is drive number

vhd30:  mov     ah,HE_LOC_DATA          ; (AH) = int 24 flag byte, data
        mov     bx,dx                   ; (BX) = packet address offset
        mov     al,es:[bx].PktCmd       ; get command
        cmp     al,CMDOUTPUT            ; write command
        je      short vhd32
        cmp     al,CMDOUTPUTV           ; write with verify command
        je      short vhd32
        cmp     al,CMDIOCTLW            ; ioctl write
        jne     short vhd33
vhd32:  or      ah,HE_WRITE_ERR         ; set read/write bit to mean write
vhd33:  mov     al,cl                   ; (AL) = drive number
        mov     bx,ds:[si].SDevRealDS
        mov     cx,si                   ; (BX:CX) = real mode dd header addr

        xor     dl,dl                   ; (DL) = flags
        cmp     [he_packeterr],ERROR_I24_GEN_FAILURE
        jbe     short vhd36             ; jump if int 24 can handle it
        or      dl,VHE_FLAG_POPUP       ; (DL) = flags
        mov     [he_packeterr],ERROR_I24_GEN_FAILURE

vhd36:  test    [si].SDevAtt,DEV_CHAR_DEV ; is it a character device?
        jz      short vhd90             ; no, don't set the bit
        or      ah,HE_CHARDEV
        jmp     short vhd90             ; go form remainder of packet

;*      Volume manager swap disk request
;               (DS:SI) = dpb address
;               (ES:DI) = vpb address

vhd40:  mov     ah,HE_LOC_DOS           ; (AH) = int 24 flag byte, DOS area
        mov     dl,VHE_FLAG_POPUP       ; (DL) = flags, do popup
        jmp     short vhd85

;*      Net Error:
;               (CL) = drive number (0=A, -1=char. dev.)

vhd45:  mov     al,cl                   ; (AL) = drive number
        mov     ah,HE_LOC_DATA          ; (AH) = int 24 flag byte, data area
;98064  mov     dl,VHE_FLAG_POPUP       ; (DL) = flags, do popup
        and     dl,NOT VHE_FLAG_POPUP   ; (DL) = flags, don't do popup  ;98064
        cmp     al,-1                   ; is it a character device?
        jne     short vhd62             ; no, don't set the bit
        or      ah,HE_CHARDEV
        jmp     short vhd62             ; go form remainder of packet

;*      Drive locked:
;               (DL) = drive number (0=A)

vhd55:  mov     al,dl                   ; (AL) = drive number
        mov     ah,HE_LOC_DOS           ; (AH) = int 24 flag byte, DOS area
        mov     dl,VHE_FLAG_POPUP       ; (DL) = flags, do popup
        jmp     short vhd62             ; go form remainder of packet

;*      The error is a FCB problem from a VDM:
;               nothing

vhd60:  mov     ah,HE_LOC_DATA          ; (AH) = int 24 flag byte, data
        xor     dl,dl                   ; (DL) = flags, no popup
vhd62:  DosContext ds                   ; (DS:SI) = device driver address
        mov     si,offset DOSGROUP:NULdev
        jmp     short vhd90             ; go form remainder of packet

;*      The error is a sharing violation.
;               (DS:SI) = sft address of file or device

vhd65:  mov     ah,HE_LOC_DIR           ; (AH) = int 24 flag byte, directory
        xor     dl,dl                   ; (DL) = flags, no popup
        test    [si].sf_flags,DEVID_DEVICE
        jz      short vhd75             ; not a character device
        lds     si,[si].sf_devptr       ; (DS:SI) = device header
    ASSUME   DS:NOTHING
        or      ah,HE_CHARDEV           ; (AH) = set char device flags
        jmp     short vhd90             ; go form remainder of packet

;*      The error is a lock problem from a VDM:
;               (DS:SI) = sft address of file

vhd70:  mov     ah,HE_LOC_DATA          ; (AH) = int 24 flag byte, data
        xor     dl,dl                   ; (DL) = flags, no popup
vhd75:  mov     si,[si].sf_fsi.sfi_hVPB ; (SI) = VPB handle
        VPBContext ds,si                ; (DS:SI) = VPB address
        test    [si].vpb_fMisc,VPB_REMOTE_DRIVE ; remote drive?
        jz      short vhd77             ;  yes, don't try to get DPB.
        xor     al,al                   ; drive number is set to zero
        xor     si,si
        mov     ds,si                   ; ds:si = 0
        jmp     short vhd90             ; continue

vhd77:  lds     si,[si].vpb_fsi.vpi_pDPB; (DS:SI) = DPB address
     ASSUME  DS:NOTHING
        jmp     short vhd85

;*      File system bad:
;               (DS:SI) = pointer to dpb

vhd80:  mov     ah,HE_LOC_DATA          ; (AH) = int 24 flag byte, data
        xor     dl,dl                   ; (DL) = flags, no popup
vhd85:  mov     al,[si].dpb_drive       ; (AL) = drive number
        lds     si,[si].dpb_driver_addr ; (DS:SI) = device driver address
     ASSUME  DS:NOTHING

;*      All the above errors join back together here.  Form the
;       remainder of the packet.
;
;       (AL) = drive number
;       (AH) = int 24 flag byte
;       (DS:SI) = device driver address
;       (DL) = misc flags

vhd90:  FlatContext es
        mov     ebx,[_pvheVDM]          ; (ES:EBX) = pointer to VHE structure
        or      ebx,ebx                 ; is the vhe address initialized yet?
        jz      short vhd95             ;  no, just exit (CARRY CLEAR)

;       Copy device driver header to v86 kernel hard error packet

        mov     cx,ds
        or      cx,si                   ; Is ds:si = 0?
        jz      short vhd93             ; yes, no DPB, do not attempt to copy

        movzx   esi,si                  ; (DS:ESI) = device header
        lea     edi,[ebx].vhe_abDeviceHeader ; (ES:EDI) = place to put dev hdr
        mov     ecx,VHE_DEVICE_HEADER_SIZE
        rep movs byte ptr es:[edi], byte ptr ds:[esi]

;       Fill the rest of the V86 kernel HE packet

vhd93:  FlatContext ds
        or      dl,VHE_FLAG_ACTIVE
        mov     [ebx].vhe_fbMisc,dl
        mov     [ebx].vhe_fbInt24,ah
        mov     [ebx].vhe_bDriveNum,al
        mov     ax,[he_packeterr]       ; (AX) = packet error
        mov     [ebx].vhe_usPacketErr,ax
        mov     ax,[he_errcode]         ; (AX) = error code
        mov     [ebx].vhe_usFailErr,ax
        mov     [ebx].vhe_bResp,HDERR_NORESP

;       Ignore hard errors for the rest of the system (SVC) call

        mov     di,ss:[TKOPTDA.CurrTCB] ; (SS:DI) = current TCB pointer
        or      ss:[di].TCBMiscFlags,TMF_IGNORE_HE

;       Set up the allow bits in HE packet

        mov     al,ss:[di].TCBAllowed   ; (AL) = allowable actions
        mov     [ebx].vhe_fbAllowed,al

;       The bits in AL are 0000IRAF.  We want 00IRF000.  That is,
;       squeeze out the A flag then shift left 3 bits.  The following
;       code does this:

        and     al,NOT ALLOWED_ABORT    ; turn off abort bit        0000IR0F
        rcr     al,1                    ; put fail (F) bit in carry 00000IR0
        adc     al,0                    ; put back in fail bit      00000IRF
        shl     al,3                    ; put in proper place       00IRF000
        .errnz  ALLOWED_FAIL - 1h
        .errnz  ALLOWED_RETRY - 4h
        .errnz  ALLOWED_IGNORE - 8h
        .errnz  HE_ALLOW_FAIL - 8h
        .errnz  HE_ALLOW_RETRY - 10h
        .errnz  HE_ALLOW_IGNORE - 20h
        or      es:[ebx].vhe_fbInt24,al

        test    dl,VHE_FLAG_POPUP
        jnz     short vhd95             ; jump if hard popup ok (carry clear)
        stc

vhd95:  RestoreReg <es,ds>
        popad
        ret                             ; EXIT
EndProc   VDMHardError

HIGH3CODE ENDS

           FARCODE HIGH32
HIGH32CODE SEGMENT

;**     VDMInt24Handler - default int 24 handler
;
;       ENTRY   (EBX) = PVSF
;               VSF(AL) = drive number
;               VSF(AH) = int 24 flags
;               VSF(BP:SI) = device driver header address
;               VSF(DI) = packet error code
;
;       EXIT    VSF(AL) = hard error response
;               Carry clear, don't chain to next VDD
;
;       USES    EAX, ECX, EDX, FS, GS
;
        ASSUME  DS:FLAT,ES:FLAT,SS:TASKAREA,FS:NOTHING,GS:NOTHING

Procedure VDMINT24HANDLER,near

LocalVar  vih_packet_dpb, <size Packet> ; union of req packet and dpb
LocalVar  vih_save_allowed,BYTE                                         ;72761;
        .errnz  dpb_drive               ; only need first byte of dpb

        EnterProc
        CCALL32 VDHPOPINT               ; eat interrupt
        mov     ecx,[_pvheVDM]          ; (ECX) = ptr V86 hard error packet
        mov     al,[ecx].vhe_bResp      ; (AL) = response from prot popup
        test    [ecx].vhe_fbMisc,VHE_FLAG_POPUP
        jnz     vih90                   ; jump if protected mode popup

        SaveReg <ebx,esi,edi>
        mov     cx,ss:[TKOPTDA.ExtErr]

;------ Is it Sharing violations?
        mov     ax,(HEShare or HE_INT24_POPUP)
        cmp     cx,ERROR_SHARING_VIOLATION
        je      short vih80

;------ Is it FCB problem?
        mov     ax,(HEFCB or HE_INT24_POPUP)
        cmp     cx,ERROR_FCB_UNAVAILABLE
        je      short vih80

;------ Is it Lock problem?
        mov     ax,(HELock or HE_INT24_POPUP)
        cmp     cx,ERROR_LOCK_VIOLATION
        je      short vih80

;------ Is it Bad file system?
        cmp     cx,ERROR_NOT_DOS_DISK
        jne     short vih03
        mov     al,[ebx].vsf_eax.lobyte
        mov     [vih_packet_dpb.dpb_drive],al
        mov     ax,ss
        mov     ds,ax
        ASSUME  DS:NOTHING
        lea     si,vih_packet_dpb
        mov     ax,(HEBadFS or HE_INT24_POPUP)
        jmp     short vih80
vih03:  ASSUME  DS:FLAT

;------ Is it an invalid DTA?
        cmp     cx,ERROR_INVALID_DTA    ;=96
        jne     short vih05
        mov     dx,cx           ;(DX) = message number
        xor     cx,cx           ;(CX) = # of replacement strings
                                ;(DS:ESI) = ptr to replacement strings
        mov     ax,(HEMessage or HE_INT24_POPUP)
        jmp     short vih80
vih05:

;------ default, Packet error
        movsx   cx,[ebx].vsf_eax.lobyte ; (CX) = drive number (sign extended)
        mov     eax,[ebx].vsf_edi       ; (AX) = packet error
        mov     [vih_packet_dpb.PktStatus],ax

        mov     ax,[ebx].vsf_ebp.loword
        mov     esi,[ebx].vsf_esi       ; (AX:SI) = device driver header
        CCALL32 demMapSeg
        mov     ds,ax                   ; (DS:SI) = prot ptr to DD header
        ASSUME  DS:NOTHING

        mov     ax,ss
        mov     es,ax
        ASSUME  ES:NOTHING
        lea     bx,vih_packet_dpb

;       (CX)    = drive number (-1 for char devices)
;       (DS:SI) = pointer to device driver header
;       (ES:BX) = request packet

        mov     ax,(HEPacket or HE_INT24_POPUP)
;;;;;;; jmp     short vih80



vih80:
        SaveReg <ebx,ds>                                                ;53825;
        FlatContext <ds>                                                ;53825;
        mov     ebx,[_pTCBCur]          ; Save current settings         ;53825;
;72761; mov     dh,[ebx].TCBAllowed                                     ;53825;
        mov     vih_save_allowed,dh                                     ;72761;
        mov     dh,[ebx].TCBAllowed                                     ;72761;
        mov     [ebx].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY           ;53825;
        xchg    dh,vih_save_allowed                                     ;72761;
        RestoreReg <ds,ebx>                                             ;53825;

        CALL16  HardError

;72761; FlatContext <ds>                                                ;53825;
        FlatContext <ds,es>,cx                                          ;72761;
        mov     ebx,[_pTCBCur]          ; Restore settings              ;53825;
        mov     dh,vih_save_allowed                                     ;72761;
        mov     [ebx].TCBAllowed,dh                                     ;53825;
        RestoreReg <edi,esi,ebx>
;72761; FlatContext <ds,es>,cx

vih90:
        mov     [ebx].vsf_eax.lobyte,al
        mov     ecx,[_pvheVDM]          ; (ECX) = ptr V86 hard error packet
        mov     [ecx].vhe_fbMisc,0
        clc
        LeaveProc
        RETC
EndProc   VDMINT24HANDLER

HIGH32CODE ENDS

        END
