;       SCCSID = @(#)kmodea.asm 6.8 92/02/29
PAGE    ,132
TITLE   KMODEA.ASM - Kernel enter/exit support
NAME    KMODEA

SCCSID  equ     "@(#)kmodea.asm 6.8 92/02/29"

;***    KMODEA.ASM
;
;
;       Title:
;
;       Kernel Enter/Exit support
;
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       Description:
;
;       The assembly language support for entering and exiting
;       kernel mode.
;
;       Design/Implementation Notes:
;
;       Public routines:
;
;       KMEnterKmodeEvents
;       KMExitKmodeEvents
;       _kmCTXHDaemon
;       KMDispatchHook
;
;       Modifications:
;       --------------
;       07/19/94  93159  Change Team - If task switching from a VDM with VME
;                                      enabled, clear the VIF and VIP eflags
;                                      with an IRETD instruction.  Note that
;                                      POPFD does not modify these flags.
;                                      Otherwise, we will gpfault when the next
;                                      ring 3 code is executed.
;
;       10/11/95  134780  BZ           Moved the logic (from defect 93159) to
;                                      clear the VIF and VIP eflags to the
;                                      pgSwitchContext routine.
;       04/11/96  150281  TPL          Check for hybernation when exiting
;                                      the Kernel


INCL_NOREFERS   equ     1               ; for kmode.inc

.xlist
        include basemaca.inc
        include osmaca.inc
        include fnotask.inc
        include dosseg.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include osconst.inc
        include infoseg.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include curdir.inc
        include dirent.inc
        include screen.inc
        include sem.inc
        include find.inc
        include tkptda.inc
        include seldesc.inc
        include sched.inc
        include kmode.inc
        include sysentry.inc
        include mi.inc
        include bsexcpt.inc
        include error.inc                                               ;150281
.list

        CpuMode 386
        ModName kmodea, FNO_KMODEA_ASM

        ReferFlat       _hookheadGlobal,byte


        ReferFlat       _pPTDACur,dword
        ReferFlat       _pTCBCur,dword
        ReferFlat       _criSEF,dword
        ReferFlat       _criPASCALSEF,dword
        ReferFlat       _Int13TCBNumber,word                             ;@xxxx
        ReferFlat       _pHybernateTCB,dword                            ;150281
        ReferFlat       _hybernateSem,dword                             ;150281

        EXTRN16         CMAttendSwapper

        EXTRN16         PostPending
        EXTRN16         ProcessFreeze
        EXTRN16         RlseBuffr
        EXTRN16         SigActive
        EXTRN16         ExitCritSecUnHoldSig

ifdef INCDEBUG
        EXTRN32         _DBGEntryEvent
        EXTRN32         _DbgEvent
        EXTRN32         _DbgValidateTF
endif ;DEBUG

        EXTRN32         NPX_Error_Handler
        EXTRN32         Sig_Proc
        EXTRN32         _XCPTBuildR3DispatcherStack
        EXTRN32         _XCPTCheckExceptionHandler
        EXTRN32         _tkEvapAll
        EXTRN32         _TKForceCritSec
        EXTRN32         _tkSetPIB
        EXTRN32         _tkSetTIB
;       EXTRN32         EM86STITest
        EXTRN32         EM86DispatchSTIHooks
        EXTRN32         EM86DispatchVDMBPHooks
        EXTRN32         _TKExit
        EXTRN32         _TKSleep
        EXTRN32         _tkSchedNext
        EXTRN32         _tkUpdateTIBPriority
        EXTRN32         _SMFreeCompDFs                       ; d# 98488
        EXTRN32         _KSEMWaitEvent                                  ;150281

CONSTANTS segment
        ASSUME  DS:DOSGROUP

                ReferFlat       _flGlobalActions,dword
                ReferFlat       _Resched,byte
                ReferFlat       Resched,dword
                ReferFlat       _cInDos,byte
                ReferFlat       InDos,byte


CONSTANTS ends


          FARCODE HIGH32
HIGH32CODE SEGMENT

;***EP  KMEnterKmodeThunkSEF - enter kernel mode for a SEF stack frame
;
;       The thunk return address is copied as part of the user argments.
;
;       ENTRY   ESP = call gate stack frame
;
;       EXIT    (DS, ES) = flat context
;               (EBX) = flat relative stack frame base
;               Direction flag cleared
;               Interrupts enabled
;
;       USES    EBX, ESI, DS, ES, Flags
;
;       The stack looks like this on entry:
;       +----------------+
;       |  User SS       |
;       +----------------+
;       |  User ESP      |
;       +----------------+
;       |                |
;       |  User Args     |
;       |                |
;       +----------------+
;       |  Thunk Addr    |
;       +----------------+
;       |  User CS       |
;       +----------------+
;       |  User EIP      |
;       +----------------+
;       |  User EFLAGS   |
;       +----------------+
;       |  cbargs        |
;       +----------------+
;       |  retaddr       |
;       +----------------+<---------- Kernel SS:ESP
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMEnterKmodeThunkSEF,near
        movzx   esp,sp
        PUSHD   ds                      ; save SEF stack frame
        PUSHD   es
        PUSHD   fs
        PUSHD   gs
        pushad
        FlatContext <ds,es>,eax
        mov     ebx,esp                 ; (EBX) = SS relative base of SEF
        SSToDS  ebx                     ; (EBX) = flat relative base of SEF

;       Move the thunk's return address to the client's EIP

        mov     eax,[ebx].thunksef_ulret; (EAX) = thunk return address
        mov     [ebx].sef_eip,eax       ; plant address on stack
        .errnz  thunksef_sef

;       Adjust user's ESP to remove thunk return address

        mov     eax,[ebx].sef_cbargs    ; (EAX) = cbargs
        add     dword ptr [ebx+eax+(size sef_s)],4

        NullContext <fs,gs>,esi
        kmEnterKmode <offset FLAT:_criSEF>,ebx     ; P729681
        jmp     [ebx].sef_retaddr       ; return without removing retaddr
EndProc   KMEnterKmodeThunkSEF

;***EP  KMEnterKmodeSEF - enter kernel mode for a SEF stack frame
;
;       ENTRY   ESP = call gate stack frame
;
;       EXIT    (DS, ES) = flat context
;               (EBX) = flat relative stack frame base
;               Direction flag cleared
;               Interrupts enabled
;
;       USES    EBX, ESI, DS, ES, Flags
;
;       The stack looks like this on entry:
;       +----------------+
;       |  User SS       |
;       +----------------+
;       |  User ESP      |
;       +----------------+
;       |                |
;       |  User Args     |
;       |                |
;       +----------------+
;       |  User CS       |
;       +----------------+
;       |  User EIP      |
;       +----------------+
;       |  User EFLAGS   |
;       +----------------+
;       |  cbargs        |
;       +----------------+
;       |  retaddr       |
;       +----------------+<---------- Kernel SS:ESP
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMEnterKmodeSEF,near
        movzx   esp,sp
        PUSHD   ds                      ; save SEF stack frame
        PUSHD   es
        PUSHD   fs
        PUSHD   gs
        pushad
        NullContext <fs,gs>,esi
        kmEnterKmode <offset FLAT:_criSEF>
        jmp     [ebx].sef_retaddr       ; return without removing retaddr
EndProc   KMEnterKmodeSEF


;***EP  KMEnterKmodePASCALSEF - enter kernel mode for a pascal SEF stack frame
;
;       ENTRY   ESP = call gate stack frame
;
;       EXIT    (DS, ES) = flat context
;               (EBX) = flat relative stack frame base
;               Direction flag cleared
;               Interrupts enabled
;
;       USES    EBX, ESI, DS, ES, Flags
;
;       The stack looks like this on entry:
;       +----------------+
;       |  User SS       |
;       +----------------+
;       |  User ESP      |
;       +----------------+
;       |                |
;       |  User Args     |
;       |                |
;       +----------------+
;       |  User CS       |
;       +----------------+
;       |  User EIP      |
;       +----------------+
;       |  User EFLAGS   |
;       +----------------+
;       |  cbargs        |
;       +----------------+
;       |  retaddr       |
;       +----------------+<---------- Kernel SS:ESP
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMEnterKmodePASCALSEF,near
        movzx   esp,sp
        PUSHD   ds                      ; save SEF stack frame
        PUSHD   es
        PUSHD   fs
        PUSHD   gs
        pushad
        NullContext <fs,gs>,esi
        kmEnterKmode <offset FLAT:_criPASCALSEF>
        jmp     [ebx].sef_retaddr       ; return without removing retaddr
EndProc   KMEnterKmodePASCALSEF

;***EP  KMExitKmodeSEF - exit the kernel for SEF stack frames
;
;       ENTRY   (DS,ES) = flat context
;               (EAX)   = client's EAX
;               (ESP)   = SEF
;
;       EXIT    Back to user code
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMExitKmodeSEF,near
        pop     [esp].sef_retaddr       ; pop off return address
        mov     [esp].sef_eax,eax       ; save return code
        kmCheckEvents
        jnz     short eks30             ; jump if exit kmode events to service

eks10:  kmExitKmode

;       If there are arguments, and this is not a pascal call, then adjust
;       the user ESP for C32.  This adjustment emulates a RETF 0 to ring 3,
;       while still performing a RETF N to pop the kernel stack's arguments.
;       kmRebuildStackFrame will add cbargs back if we have a problem.
;       So, the net effect is to return to the user without changing ESP.

        mov     eax,[esp].sef_cbargs
        sub     [esp+eax+(size sef_s)],eax

        kmChkStack1 eks40               ; make stack present, if Ring 2
eks20:  popad                           ; restore user registers
        POPD    gs
        POPD    fs
        POPD    es
        POPD    ds
        ret     (size sef_cbargs)       ; pop cbargs from stack

eks30:  call    KMExitKmodeEvents
        jmp     short eks10

eks40:  kmChkStack2 eks20
EndProc   KMExitKmodeSEF

;***EP  KMExitKmodePASCALSEF - exit the kernel for SEF stack frames
;
;       ENTRY   (DS,ES) = flat context
;               (EAX)   = client's EAX
;               (ESP)   = SEF
;
;       EXIT    Back to user code
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMExitKmodePASCALSEF,near
        pop     [esp].sef_retaddr       ; pop off return address
        mov     [esp].sef_eax,eax       ; save return code
        kmCheckEvents
        jnz     short eps30             ; jump if exit kmode events to service

eps10:  kmExitKmode
        kmChkStack1 eps40               ; make stack present, if Ring 2
eps20:  popad                           ; restore user registers
        POPD    gs
        POPD    fs
        POPD    es
        POPD    ds
        ret     (size sef_cbargs)       ; pop cbargs from stack

eps30:  call    KMExitKmodeEvents
        jmp     short eps10

eps40:  kmChkStack2 eps20
EndProc   KMExitKmodePASCALSEF

;***EP  KMExitKmodeSEFx - exit the kernel for SEF stack frames
;
;       This is the common return code for the SEF stack frame
;       (32-bit call gate api's using SysEntry32, etc).  This
;       code is used by the kmode stack rebuild function to exit
;       the kernel once the stack is rebuild on traps at ring 0
;       with InDos == 0.
;
;       ENTRY   (DS,ES) = flat context
;               (EAX)   = client's EAX
;               (ESP)   = SEF
;
;       EXIT    Back to user code
;

IRP     x,<0,4,8,12,16,20,24,28,32,36>
align 4
Procedure KMExitKmodeSEF&x,near
        call    KMExitKmodeSEF
        popfd                           ; popfd and return must be together
        RETFD   <x>                     ;  for single steping code
EndProc   KMExitKmodeSEF&x

ENDM

;***EP  KMExitKmodePASCALSEFx - exit the kernel for PASCAL SEF stack frames
;
;       ENTRY   (DS,ES) = flat context
;               (EAX)   = client's EAX
;               (ESP)   = SEF
;
;       EXIT    Back to user code
;

IRP     x,<0,4,8,12,16,20,24,28,32,36>
align 4
Procedure KMExitKmodePASCALSEF&x,near
        call    KMExitKmodePASCALSEF
        popfd                           ; popfd and return must be together
        RETFD   <x>                     ;  for single steping code
EndProc   KMExitKmodePASCALSEF&x

ENDM

;***ED  _afpfnReturnSEF - array of kernel exit return functions
;
;       This array is used by the kmode rebuild stack frame
;       functions to setup the kernel exit routine address.

align 4
        public  _afpfnReturnSEF
_afpfnReturnSEF label   fword

IRP     x,<0,4,8,12,16,20,24,28,32,36>

        dd      offset FLAT:KMExitKmodeSEF&x
        dw      seg FLAT:KMExitKmodeSEF&x
        dw      0

ENDM
.errnz  (MAXRETN - ((($ - _afpfnReturnSEF - 8)/8) * 4))

;***ED  _afpfnReturnPASCALSEF - array of PASCAL kernel exit return fn's
;
;       This array is used by the kmode rebuild stack frame
;       functions to setup the kernel exit routine address, for the
;       PASCALSEF frame.

align 4
        public  _afpfnReturnPASCALSEF
_afpfnReturnPASCALSEF   label   fword

IRP     x,<0,4,8,12,16,20,24,28,32,36>

        dd      offset FLAT:KMExitKmodePASCALSEF&x
        dw      seg FLAT:KMExitKmodePASCALSEF&x
        dw      0

ENDM
.errnz  (MAXRETN - ((($ - _afpfnReturnPASCALSEF - 8)/8) * 4))


;***EP  KMEnterKmodeEvents - perform EnterKmode events
;
;       We are called during EnterKmode processing when an entry
;       action flag is set in the current TCB.  This code is
;       optimized for the case where only one bit is set in
;       the flags.
;
;       ENTRY:  (ESI) = Current TCB pointer
;               (DS) = (ES) = FLAT
;               Interrupts disabled
;
;       EXIT:   Interrupts enabled
;
;       USES:   Flags (MUST PRESERVE OTHERS)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

keDispTab       label   dword           ; Entry flag dispatch table
        .errnz  (($ - keDispTab)/4 - TK_IEF_PFCLI)
        dd      offset FLAT:kePFCLI
        .errnz  (($ - keDispTab)/4 - TK_IEF_TRC)
        dd      offset FLAT:keTRC
ifdef MISCSTRICT
        dd      (8 - ($ - keDispTab)/4) dup(0)  ; Dummies for invalid bits
endif

Procedure KMEnterKmodeEvents,<near,far32>

        push    eax
        push    ebx
        movzx   ebx,[esi].TCBEntryActions ; (EBX) = entry flags
ke0:    bsf     eax,ebx                 ; (EAX) = bit index of set bit
ifdef MISCSTRICT
        jz      short ke2               ;  panic if no bits set
endif
        btr     ebx,eax                 ; Clear the bit we found
        jmp     cs:keDispTab[eax*4]     ; Jump to handler


;       DosDebug entry event

keTRC:  sti
ifdef INCDEBUG
        push    ecx
        push    edx
        CCALL32 _DBGEntryEvent
        pop     edx
        pop     ecx
endif ;DEBUG
        jmp     short ke1


;       End of Page Fault with interrupts disabled

kePFCLI:
        CALL16  ExitCritSecUnHoldSig

;       Check for additional events

ke1:    or      ebx,ebx                 ; Any more events?
        jnz     short ke0               ;  yes, go handle them
        mov     [esi].TCBEntryActions,bl; Clear the flags
        pop     ebx
        pop     eax
        RETC

ifdef MISCSTRICT
;       Unexpected error

ke2:    Panic   "KMEnterKmodeEvents: no events"
        jmp     short ke1               ; Allow orderly shutdown
endif

EndProc KMEnterKmodeEvents


;***EP  KMExitKmodeEvents - Service exit kernel mode events
;
;       First, if there is a forced action pending for the task, we
;       perform the appropriate action.
;
;       Next, if _Resched is set we call the scheduler who may
;       select some other task.  Note that when the scheduler starts
;       a new task it will always comes back here (eventually),
;       although with a different PTDA selected.
;
;       ENTRY   NONE
;
;       EXIT    Interrupts disabled
;
;       USES    EAX, EBX, ECX, EDX, EBP, ESI, EDI, DS, ES, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

pfnGlobalActs   label   dword           ; Global force flag dispatch table
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_GCTXH)
        dd      offset FLAT:exk40
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_ATTENDSWAP)
        dd      offset FLAT:exk50
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_SIGPEND)
        dd      offset FLAT:exk60
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_EVAPALL)
        dd      offset FLAT:exk70
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_FREECOMPDFS)  ; d# 98488
        dd      offset FLAT:exk75                                         ; d# 98488
        .errnz  ((100h SHL (($ - pfnGlobalActs)/4)) - KM_FF_HYBERNATE)  ;150821
        dd      offset FLAT:exk10A      ; this flag is cleared in       ;150821
                                        ; hyberFreezeSys (hybernat.c)   ;150821
Procedure KMExitKmodeEvents,<near,far32>
        TaskTime
exk10:  cli                             ; disable interrupts until exit
        FlatContext <ds,es>,eax         ; (DS, ES) = flat context
        xor     eax,eax                 ; make sure there is nothing in fs,gs
        mov     fs,eax
        mov     gs,eax
                                                                         ;@xxxx
        ; If Int 13 thread, only handle process related events           ;@xxxx
                                                                         ;@xxxx
        mov     edi,[_pTCBCur]          ; (EDI) = pointer to Cur TCB     ;@xxxx
        mov     bx, _Int13TCBNumber     ; (BX) = VDM Int13 Slot #        ;@xxxx
        cmp     bx, [edi].TCBNumber     ; are we running on Int 13 Thrd? ;@xxxx
        jz      short exk10A            ; yes - skip global actions      ;@xxxx

;       Check for hybernation event

        test    _flGlobalActions, KM_FF_HYBERNATE                       ;150281
        jnz     exk90                   ; don't allow thread to exit    ;150281

;       Check for global events, excluding rescheduling, first.
exk10_:
        bsf     ebx,dword ptr _flGlobalActions+1
                                        ; (EBX) = bit index of set bit
        jnz     short exk30             ;  jump if some bits set

;       Check for per-process and per-thread events.

exk10A: mov     esi,[_pPTDACur]         ; (ESI) = pointer to current PTDA
        mov     edi,[_pTCBCur]          ; (EDI) = pointer to current TCB
        mov     eax,[esi].ptda_ForcedActions ; check process force flags
        or      eax,[edi].TCBForcedActions ; check thread force flags
        jz      short exk20             ; jump if we have no forced action

;       CALL32  EM86STITest             ; Check for VDM STI HACK
;       or      eax,eax                 ; STI Check not ready ignore
;       jz      short exk20             ;

        sti                             ; enable interrupts
        CCALL32 kmCheckForceFlags       ; process forced actions, and repeat
   ASSUME       DS:NOTHING, ES:NOTHING
        jc      short exk10             ; if check force flags again
        cli                             ; disable interrupts until exit
        FlatContext <ds,es>,eax         ; (DS, ES) = flat context

;       Check for rescheduling.

exk20:  cmp     [_Resched],0            ; any rescheduling need?
        jne     short exk80             ;  yes, jump if rescheduling to do
        sti                             ; B733554
        RETC                            ; EXIT, interrupts enabled

;       Process global events.

exk30:  jmp     cs:pfnGlobalActs[ebx*4] ; Jump to handler

exk40:  CCALL32 kmDispatchGlobalContextHooks
        jmp     exk10

exk50:  sti                             ; enable interrupts
        CALL16  CMAttendSwapper         ; grow swap file cluster map
        jmp     exk10                   ; check force flags again

exk60:  sti                             ; enable interrupts
        CALL16  PostPending             ; post any pending signals
        jmp     exk10

exk70:  sti                             ; enable interrupts
        CCALL32 _tkEvapAll              ; free PTDAs and TCBs
        jmp     exk10

exk75:  sti                             ; enable interrupts          ; d# 98488
        CCALL32 _SMFreeCompDFs       ; Shrink Swap File              ; d# 98488
        jmp     exk10                                                ; d# 98488

;       Do Rescheduling.

exk80:  sti                             ; enable interrupts
        CCALL32 _tkSchedNext            ; try to reschedule
        jmp     exk10

exk90:  cmp     edi, _pHybernateTCB     ; hybernation thread leaving?   ;150281
        jz      exk10_                  ; skip this thread              ;150281
        sti                             ;                               ;150281
exk100:                                 ; DON'T clear KM_FF_HYBERNATE   ;150281
        lea     eax, _hybernateSem      ;   here!!!                     ;150281
        CCALL32 _KSEMWaitEvent,<eax,-1> ;                               ;150281
        cmp     eax, ERROR_INTERRUPT    ; the request was interrupted?  ;150281
        je      short exk100            ; try again                     ;150281
        cli                             ;                               ;150281
        jmp     exk10_                  ;                               ;150281

EndProc KMExitKmodeEvents


;***LP  kmCheckForceFlags - check force flags
;
;       We call here when we see that a force flag has been set.
;       This is not "inline" above in order to save execution time.
;
;       ENTRY:  (ESI) = Current PTDA pointer
;               (EDI) = Current TCB pointer
;               (EAX) = Force flags to check
;
;       EXIT:   Carry set
;                  KMExitKmodeEvents processing must be repeated.  This
;                  is because the original processing is invalid for
;                  the current thread due to a reschedule occurring
;                  because of force flag processing and/or because
;                  additional force flags have been set.
;
;               Carry clear
;                  No need to repeat KMExitKmodeEvents processing.  This
;                  may happen if we're in thread 1 of a process and signals
;                  were active on entry to this routine. If TK_FF_CRITSEC or
;                  TK_FF_ICE were set, they are held pending completion of
;                  signal activity.
;
;       USES:   EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

cffDispTab      label   dword           ; Force flag dispatch table
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_BUF)
        dd      offset FLAT:cffBUF
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_EXIT)
        dd      offset FLAT:cffEXIT
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_CRITSEC)
        dd      offset FLAT:cffCRITSEC
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_ICE)
        dd      offset FLAT:cffICE
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_NPX)
        dd      offset FLAT:cffNPX
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_TIB)
        dd      offset FLAT:cffTIB
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_TRC)
        dd      offset FLAT:cffTRC
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_SIG)
        dd      offset FLAT:cffSIG
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_CTXH)
        dd      offset FLAT:cffCTXH
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_STIH)
        dd      offset FLAT:cffSTIH
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_VDMBP)
        dd      offset FLAT:cffVDMBP
        dd      0
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_PIB)
        dd      offset FLAT:cffPIB
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_SCH)
        dd      offset FLAT:cffSCH
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_TFBIT)
        dd      offset FLAT:cffTFBIT
        .errnz  ((1 SHL (($ - cffDispTab)/4)) - TK_FF_TIBPRI)
        dd      offset FLAT:cffTIBPRI

Procedure kmCheckForceFlags,near,local

;       (EDI) = current TCB pointer

cff10:  bsf     ebx,eax                 ; (EBX) = bit index of set bit
        jmp     cs:cffDispTab[ebx*4]    ; Jump to handler

cffBUF: CALL16  RlseBuffr               ; release buffers
        stc     ;jmp     cff50
        RETC
cffEXIT:                                ; TK_FF_EXIT (old FF_DES)
        and     byte ptr [edi].TCBForcedActions,not TK_FF_EXIT
        CCALL32 _TKExit,<0,[esi].ptda_ulExitCode>
                                        ; Exit just this thread
        stc     ;jmp     cff50          ; Dispatch 32-bit PTERM Exception,
        RETC                            ; or to ExitList, checking other flags.

;       If  this thread is thread 1 and there are signals to be handled,
;       then  we  do not want to block this thread either for a critical
;       section  (TK_FF_CRITSEC)  or  because of a pending freeze (TK_FF_ICE).
;       However,  we  want  to  leave the force flags set so that if the
;       conditions  are  still pending when thread 1 has no more signals
;       to process, we can take the specified forced action.

cffCRITSEC:
        CALL16  SigActive               ; signals active?
        jnc     short cff20             ;  no, enter critical section
        and     eax,NOT TK_FF_CRITSEC   ; Turn off critsec bit
        jnz     short cff10             ; Try again if other bits set
        RETC    ;jmp     cff60          ; Done for now (CARRY CLEAR)

cff20:  CCALL32 _TKForceCritSec         ; enter critical section
        stc     ;jmp     cff50          ; do it all again
        RETC
cffICE: CALL16  SigActive               ; signals active?
        jnc     short cff30             ;  no, freeze thread
        and     eax,NOT TK_FF_ICE       ; Turn off ice bit (clears carry)
        jnz     short cff10             ; Try again if other bits set
        RETC    ;jmp     cff60          ; Done for now (CARRY CLEAR)

cff30:  CALL16  ProcessFreeze           ; do the freeze
        stc     ;jmp     cff50          ; may have been inside for a long while
        RETC
cffNPX: CCALL32 NPX_Error_Handler       ; let user know about error
        stc     ;jmp     short cff50    ; recheck all force flags again
        RETC
cffTIB: CCALL32 _tkSetTIB               ; go update the TIB
        stc     ;jmp     short cff50    ; check for other forced actions
        RETC

ifdef INCDEBUG
cffTRC: CCALL32 _DbgEvent               ; handle debug event
        stc     ;jmp     short cff50    ; recheck all force flags again
        RETC
else ;DEBUG

cffTRC: stc     ;jmp     short cff50    ; recheck all force flags again
        RETC
endif ;DEBUG


cffCTXH:
        CCALL32 kmDispatchLocalContextHooks
        stc     ;jmp     short cff50
        RETC
cffSTIH:
        CCALL32 EM86DispatchSTIHooks    ; execute sti hooks
        stc     ;jmp     short cff50
        RETC
cffVDMBP:
        CCALL32 EM86DispatchVDMBPHooks  ; execute return hooks
        stc     ;jmp     short cff50
        RETC
cffPIB: CCALL32 _tkSetPIB               ; go update the PIB
        stc     ;jmp     short cff50    ; check for other forced actions
        RETC
cffSCH: and     [edi].TCBForcedActions,NOT TK_FF_SCH    ; "Thread left kernel"
        stc     ;jmp     short cff50    ; check for other forced actions
        RETC
cffTFBIT:

ifdef INCDEBUG
        CCALL32 _DbgValidateTF          ; validate TF bit
endif ;DEBUG

        clc                             ; removed by 59463 reinserted by 60142
        RETC    ;jmp     short cff60    ; changed by 59463 changed back  60142

cffTIBPRI: CCALL32 _tkUpdateTIBPriority ; go update priority in the TIB 59463
        clc                             ;                               59463
        RETC    ;jmp     short cff60    ; done (CARRY CLEAR)            59463


;       We can't dispatch to user's signal handler if we
;       were executing in ring 2 code (on a ring 2 stack).

cffSIG: mov     edi,[edi].TCBpTSD       ; (EDI) = TSD pointer
        test    [edi].TSDUserSS,1       ; Ring 2 stack?
        .errnz  ((RPL_RING2 and 1) - 0)
        .errnz  ((RPL_RING3 and 1) - 1)
        jnz     short cff40             ;  no, handle signal

        test    eax,TK_FF_TFBIT         ; need to validate TF bit?
        jnz     short cffTFBIT          ;  yes, validate TF bit
        RETC    ;jmp     short cff60    ;  no, done (CARRY CLEAR)

;       Exit:  We setup to dispatch to a signal handler if we have a
;       pending signal.  We then tell KMExitKmodeEvents either to return to
;       the user, or to repeat from the beginning. Note also that since only
;       protected mode tasks can get signals, we're always in protected
;       mode if we call Sig_Proc.  There are two exit cases:
;
;       (1)  We need to repeat KMExitKmodeEvents processing as we've done
;       force flag processing that could have caused a reschedule and/or
;       new force flags to get set. (due to Debug or SuBuff in Sig_Proc)

cff40:  CCALL32 kmCheckSigHandler       ; select signal and setup handler
cff50:  stc                             ; request to repeat Exit_Kmode
cff60:  RETC                            ; from the beginning.
EndProc kmCheckForceFlags

;**     kmCheckSigHander - Setup signal/exception handler for signal.
;
;       kmCheckSigHandler will set up either an signal handler or an
;       exception handler (if one is available and untried) for any
;       pending signals.
;
;       ENTRY   ESI = Current PTDA
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
        ASSUME DS:FLAT,ES:FLAT,SS:FLAT,FS:NOTHING,GS:NOTHING

Procedure kmCheckSigHandler

LocalVar ulExceptionInfo, DWORD         ; Only 1 param needs to be used

        EnterProc

        mov     edi,[_pTCBCur]
        and     [edi].TCBForcedActions,NOT TK_FF_SIG

        movzx   edx,[edi].TCBSig_pend   ; (EDX) = pending signals
        movzx   eax,[edi].TCBSig_cur    ; bit clear all current ones
        btr     eax,SIGGCHARMED         ; Code size optimization. Instruction
                                        ; smaller than DWORD constant
        not     eax
        and     edx,eax                 ; (EDX) = signal bit mask
        jz      cshie1                  ; jump if no signals set
csh1:   bsf     eax,edx                 ; (EAX) = (signal number - 1)
        btr     edx,eax                 ; Clear EDX so we don't see it
                                        ;  next time around the loop
        bts     [edi].TCBSig_cur,ax     ; set active signal
        btr     [edi].TCBSig_pend,ax    ; reset signal pending

        mov     ecx,eax
        inc     eax                     ; (AX) = signal number
        cmp     eax,SIGPFA              ; Is it a flag?
        jb      short csh2              ; Nope. Continue.
        cmp     eax,SIGPFC
        jle     short csh10             ; Yup. 16-bit processing only.

csh2:   btr     [esi].Sig_attempted,cx
        jc      short csh10             ; already tried this, go to 16 bit

        SaveReg <eax>
        SaveReg <ecx,edx>

        ; is there a 32 bit handler?
        CCALL32 _XCPTCheckExceptionHandler, <edi>

        RestoreReg <edx,ecx>
        or      eax,eax
        RestoreReg <eax>                ; EAX = Signal number
        jz      short csh10             ; no handler exists, look for 16 bit

;       we found a handler, set up to call it on return to user space

        mov     ecx,XCPT_ASYNC_PROCESS_TERMINATE        ; Assume APTERM

;;;;    Begin  75797
        movzx   ebx, [esi].Sig_termtid    ; Assume global terminator
        or      ebx, ebx                  ; Is there global terminator?
        jnz     short  csh2a              ; if Yes
        movzx   ebx, [edi].TCBSig_termtid ; No.. assume local terminator
csh2a:  cmp     al, SIGAPTERM             ; is this async Pterm?
;;;;    End 75797

        je      short csh3              ; Yes.
        mov     ecx,XCPT_SIGNAL
        mov     ebx, eax                ; EBX = ExceptionInfo
csh3:   mov     [ulExceptionInfo],ebx   ; p1 = Signal type
        SSToDS  eax,[ulExceptionInfo]   ; EAX = flat address of param
        SaveReg <edx>
        CCALL32 _XCPTBuildR3DispatcherStack, <EXCSW, ecx, 1, eax, 0, 0>
        RestoreReg <edx>
        jmp     short csh20

cshie1: Panic   "No signal set"

;       See if there is a 16 bit handler for this signal.  If so,
;       do it the old-fashioned way (Sig_Proc).

;       (AX) = signal number

csh10:  cmp     ax,SIGAPTERM            ; 32 bit Signal?
        je      short csh20             ;  Yes. Don't dispatch to 16-bit
                                        ;  handler.
        cmp     [esi].[Sig_vec-4][eax*4],0 ; Handled signal?
        jz      short csh20             ;  no, leave

        SaveReg <edx>                   ; Save pending signals
        CCALL32 Sig_Proc                ; select signal and setup handler
        RestoreReg <edx>                ; Restore Pending Signals
;       jmp     short csh20             ; Exit.

csh20:  or      edx,edx                 ; More signals?
        jnz     csh1                    ;  Yes. Do the next one.

cshx:   LeaveProc
        RETC

EndProc kmCheckSigHandler

;***LP  kmDispatchLocalContextHooks - dispatch local context hooks
;
;       This routine is called on the way out of the kernel if
;       there are local exit hooks to process.
;
;       ENTRY   (EDI) = Current TCB pointer
;
;       EXIT    Carry set so KMExitKmodeEvents will recheck the
;               force action flags again.
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
;       for each local context hook (TCB_pctxhLocalHead)
;           call handler passing handler specific data
;           unarmed the hook
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure kmDispatchLocalContextHooks,near,local
        cli                             ; disable interrupts
        and     [edi].TCBForcedActions,not TK_FF_CTXH
        mov     ebx,[edi].TCB_pFrameBase        ; (EBX) = frame base pointer
        lea     edi,[edi].TCB_hookheadLocal     ; (EDI) = head of linked list
        jmp     short KMDispatchHook
EndProc   kmDispatchLocalContextHooks

;***LP  _kmCTXHDaemon - process global context hooks
;
;       This the code run when there are no running threads to
;       service the global context hooks.  This thread is created
;       at init and woken up in the idle loop at time there are
;       pending global context hooks.
;
;       ENTRY   (DS) = (ES) = FLAT
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
;       forever
;           sleep until idle loop wakes us up
;           process global context hooks
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure _kmCTXHDaemon,near
cxd10:  CCALL32 _TKSleep, <<offset FLAT:_kmCTXHDaemon>, -1, 1, 0>
        CCALL32 kmDispatchGlobalContextHooks
        jmp     short cxd10
EndProc   _kmCTXHDaemon

;***LP  kmDispatchGlobalContextHooks - dispatch global context hooks
;
;       This routine is called when at task time in the idle
;       loop or on the way out of the kernel in the exit kernel
;       mode function.
;
;       ENTRY   (DS) = (ES) = FLAT
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
;       for each global context hook (_hookheadGlobal)
;           call handler passing reference data pointer
;           unarmed the hook
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure kmDispatchGlobalContextHooks,near,local
        cli                             ; disable interrupts
        and     [_flGlobalActions],NOT KM_FF_GCTXH ; clear context hook flag
        mov     edi,OFFSET FLAT:_hookheadGlobal ; (EDI) = head of linked list
        mov     ebx,-1                  ; (EBX) = second fnarm parameter

;;;     FallInto KMDispatchHook
EndProc   kmDispatchGlobalContextHooks

;***EP  KMDispatchHook - dispatch hook routines on list
;
;       Dispatches to all the hook routine on the hook list specified.
;
;       ENTRY   EDI = hookhead pointer
;               EBX = frame base pointer (second fnarm parameter)
;               interrupts may be disabled
;
;       EXIT    interrupts enabled
;
;       USES    EAX, ECX, EBX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
;       for each hook
;           call handler passing reference data pointer
;           unarmed the hook
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMDispatchHook,near
;;;     FallFrom kmDispatchGlobalContextHooks

        KMNextHookLink esi,edi                  ; (ESI) = first link
        cmp     esi,edi                         ; is next = head ?
        je      kdhx                            ;  jmp if yes
        KMDeleteHook edi,ecx,edx                ; delete head
        KMInitHookHead edi                      ; remove entries from list
        sub     esp,size dll_s                  ; allocate new head
        mov     edi,esp                         ;
        SSToDS  edi                             ; EDI = pointer to new head
        mov     [edi].dll_pdllNext,esi          ; link head into chain
        mov     ecx,[esi].dll_pdllPrev          ;
        mov     [edi].dll_pdllPrev,ecx          ;
        mov     [esi].dll_pdllPrev,edi          ;
        mov     [ecx].dll_pdllNext,edi          ;
kdh10:  cli                                     ; disable ints
        KMNextHookLink esi,edi                  ; (ESI) = next link
        cmp     esi,edi                         ; is next = head ?
        jz      short kdh15                           ;  jmp if yes
        kmAssertP HOOK,esi                      ; check ESI points to a hook
        KMDeleteHook esi,ecx,edx                ; delete hook block
        KMQueryHookData edx,esi                 ; (EDX) = reference data pointer
        mov     ecx,[esi].hook_pfn              ; (ECX) = pfn
        .errnz  hook_dll                        ;
        sti                                     ; enable ints
        PCALL32 ecx, <edx, ebx>                 ; call hook function
        jmp     short kdh10                     ; loop
kdh15:  add     esp,size dll_s                  ; delete head

kdhx:   sti                                     ; make sure ints are enabled
        RETC
EndProc   KMDispatchHook


;***EP  KMArmHook - arm a hook/add arm hook block to arm list
;
;       Adds a hook control block to the arm hook service list.
;
;       ENTRY   (phook)     = hook block pointer
;               (phookhead) = pointer to hook head
;
;       EXIT    returns TRUE, if already armed
;               returns FALSE, if it wasn't armed
;
;       USES    EAX, ECX, EDX (FLAGS are preserved)
;
;       CONTEXT Task
;               Interrupt
;
;       PSEUDO-CODE
;
;       save and disable interrupt state
;       if already armed
;          restore interrupt state
;          return TRUE
;       else
;          add the new hook block to the end of the arm list
;          restore interrupt state
;          return FALSE
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KMARMHOOK,near,,,esp          ; no traditional frame
        ArgVar  phookhead,DWORD         ; have to invert arguments
        ArgVar  phook,DWORD

        EnterProc
        pushfd                          ; pushfd requires ArgVars to be +4
        cli

        mov     eax,[phook+4]           ; (eax) -> new hook
        kmAssertP HOOK,eax

        KMIsHookLinked eax
        jne     short kah90             ; return TRUE

        mov     edx,[phookhead+4]       ; (edx) -> hookhead hook

;       KMAddHookBefore(phook, (PHOOK)phookhead);

        mov     [eax].dll_pdllNext,edx  ; new hook points fwd to hookhead

        mov     ecx,eax
        xchg    ecx,[edx].dll_pdllPrev  ; hookhead points back to new hook

        mov     [eax].dll_pdllPrev,ecx  ; new hook points back to hookhead's
                                        ; old predecessor

        mov     [ecx].dll_pdllNext,eax  ; old predecessor points fwd to new hook

        sub     eax,eax                 ; return FALSE

kah90:  popfd
        ExitProc
EndProc   KMARMHOOK


HIGH32CODE ENDS

        END
