;       SCCSID = @(#)sci.asm    6.12 92/05/21
SCCSID  equ     "@(#)sci.asm    6.12 92/05/21"
TITLE   SCI  --  system call interpreter code
PAGE    ,132

;***    sci.asm  --  system call interpreter code
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Sci.asm contains code for the system call interpreter.
;
;
;       PUBLIC  sci     - system call interpreter
;
;               thops   - thread operators, the "instructions" for the
;                         interpreters, their addresses are stored in the
;                         call gates to form the "program" executed by sci.
;
;
;       SEE     sysentry.inc
;
;       Modification History
;
;       20-Jul-88    JSF   Clear user selector from ES before yielding
;                          (1.2 PTM 1439)
;       24:Jul-89    SB    Updated to use new Enter/Exit Kmode interfaces
;       18:Nov-95    MLM   Defect 72417, reduce fscrit serialization.
;                          Moved NoSetDir from PTDA to TCB

.xlist
        include fnotask.inc
        include dosseg.inc
        include basemaca.inc
        include osmaca.inc
        include error.inc
        include protmode.inc
        include seldesc.inc
        include mi.inc
        include kmode.inc
        include sysentry.inc
        include timer.inc
        include basemid.inc                     ; message numbers
        include virtsym.inc
        include ostypes.inc
        include osconst.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include infoseg.inc
        include pgexport.inc
        include vmowner.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include curdir.inc
        include dirent.inc
        include find.inc
        include screen.inc
        include sem.inc
        include tkptda.inc
        include dbcs.inc                        ; must go after tkptda.inc
        include ordinals.inc
        include mmpha.inc
.list
        ModName sci,FNO_SCI_ASM
        CpuMode 386

;        ReferGlobal     ras_systr_flags,BYTE
;        ReferGlobal     ras_mec_table,BYTE

        ReferGlobal     Sctmask,WORD

        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pTSDCur,DWORD
        ReferFlat       _pPTDACur,DWORD

;       EXTRN           TEMPBUFLEN:ABS

        EXTRNFAR        Yield
        EXTRNFAR        ExitCritSecUnHoldSig
        EXTRNFAR        w_Read
        EXTRNFAR        Sec16Router                                       ;@SEC


        EXTRN           GetTempBuffer:near
        EXTRN           FreeTempBuffer:near
        EXTRN           UsrArgFault:near
        EXTRN           UsrArgFaultMsg:near

        EXTRN32         _PGVerifyInfoSegs
        EXTRN32         _KSEMRequestMutex
        EXTRN32         _KSEMReleaseMutex
        EXTRNF32        _TKTstnClrKernelSem


;**     Internal macros
;

threadop macro  name
to_&name proc   near
        public  to_&name
        endm

endop   macro   name
to_&name endp
        endm

SCI_Take_Krnl_Sem macro
        mov     edx,[_pPTDACur]
        add     edx,offset ptda_ptdasem         ; EDX = hksem
        CCALL32 _KSEMRequestMutex,<edx, -1>
        neg     eax
        endm

SCI_Clear_Krnl_Sem macro
        mov     edx,[_pPTDACur]
        add     edx,offset ptda_ptdasem         ; EDX = hksem
        CCALL32 _KSEMReleaseMutex,<edx>
        endm


          FARCODE HIGH3
HIGH3CODE SEGMENT

;***    Sci  --  system call interpreter
;
;       Sci executes the argument processing thread for a system call.
;
;       ALGORITHM
;               - save user registers
;               - enter "kernel context"
;               - create "shadow" register frame on stack
;               - save number of bytes of parameters on stack in TCB
;               - execute thread program
;
;               Actual call to kernel work routine is handled by
;               thop farcall.
;
;               Exit processing gets some help from farcall, most work
;               done by return routines.
;
;       ENTRY   SS      - TCB (PTDA) segment for current process
;
;               On kernel stack, which looks as defined above in kstk
;               after KmEnterKmodeSCI. Please see to_Return which calls
;               the KMExitKmodeSCI to cleans up all the stuff put by
;               KmEnterKmodeSCI and thop adr and cbargs put by CALLSCI.
;
;       EXIT    Via one of the thread operators, expected exit conditions:
;               (ax) = return status code
;               user stack will have arguments cleaned off
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

;***    pre_sci_ret - table used by pre_sci for SCIReturn
;       offsets in the high word of a dword with zero in the
;       low word.

pre_sci_ret   label   dword

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

        dw      0
        dw      offset DOSHIGH3CODE:SCIReturn&x

ENDM

        public  pre_sci
pre_sci:
        pushfd
        push    eax
        movzx   esp,sp
        movzx   eax,word ptr [esp+10]   ; argwords * 2
        test    al,2                    ; if even number of argwords
        jz      short pre1              ;   skip adjustments
        add     ax,2                    ; ax = argbytes (dword rounded)
        sub     dword ptr 20[esp][eax],2  ; adjust old esp for gate return
        mov     [esp+10],ax             ; save argbytes (dword rounded)

pre1:   mov     eax,dword ptr pre_sci_ret[eax]  ; eax = offset of SCIReturn&x
                                                ;   in high-word, 0 in low-word
        xchg    [esp+4],eax             ; fix stack
        xchg    [esp+8],eax             ; fix stack
        xchg    [esp],eax               ; fix stack & restore eax

Procedure sci,near

        ?sci_ds = ?SCI_UNKNOWN
        ?sci_es = ?SCI_UNKNOWN
        KMEnterKmodeSCI
        ASSUME  DS:DOSGROUP, ES:NOTHING

        TimerOn TIM_SCI_ENTRY

;       Put shadow frame on stack

        xor     edx,edx
        .errnz  (sreg_rpl - sreg_filler) - 2
        .errnz  (sreg_filler - sreg_tempbuf2) - 4
        push    edx                     ; space for rpl, sreg_filler = 0
        .errnz  (sreg_filler - (sreg_tempbuf2 + size sreg_tempbuf2))
        push    edx                     ; sreg_tempbuf2 = 0
        .errnz  (sreg_tempbuf2 - sreg_tempbuf1) - 4
        push    edx                     ; sreg_tempbuf1 = 0
        .errnz  (sreg_tempbuf1 - sreg_ds) - 2
        .errnz  (sreg_ds - sreg_es) - 2
        push    edx                     ; sreg_ds = 0, sreg_es = 0
        sub     sp,offset sreg_es

;       kstk_frame now describes stack starting at [bp] (after 3 more
;       instructions)
;
;       Have the system kill the proc if there is a GP fault when
;       fetching arguments.  Also setup TCB pointer to app cs:ip

        mov     si,ss:[TKOPTDA].CurrTSD ; (SI) = TSD offset
        mov     word ptr ss:[si].TSDpfnFault,offset DOSHIGH3CODE:UsrFaultHandler
        mov     bp,sp                   ; (ss:bp) -> shadow frame
        mov     si,ss:[si].TSDUserSS    ; Save a copy of the
        or      si,RPL_RING2            ; user RPL having the RPL
        and     si,3                    ; set to not below 2
        mov     [bp].sreg_rpl,si        ; save SI (RPL only)

;       It is an error to make a system call with interrupts off
;       Here is where me make the check.  If interrupts were off
;       jump down a few lines.

        test    [bp].kstk_ssf.ssf_eflag,F_INTERRUPT
        jz      short sci2              ; interrupts are off
        jmp     [bp].kstk_ssf.ssf_thopadr ; Go execute thread instructions

;       Interrupts off.  Call UsrArgFault to set up for hard error
;       popup and set force flag to kill process.

sci2:   mov     ax,MSG_INTERRUPTS_DISABLED
        call    UsrArgFaultMsg          ; generate harderr when process dies
        jmp     CleanUpReturn           ; exit system call

;***    thread  --  thread interpreter loop
;
;       This is the "main loop", for the thread interpreter.
;
;       A special thread opcode will exit the loop and return to the user.
;
;       Each time we dispatch to a new thread op, the following will be true:
;
;       (ss)    =  TCB (PTDA) stack segment
;       (ss:bp) -> shadow register frame
;       (ss:si) -> next user argument to process, advances
;       (cs:bx) -> PREVIOUS thread instruction, advances

EndProc sci


;****************************************************************************
;
;       Thread units.
;
;****************************************************************************

;***    farcall  --  execute system call
;
;       to_farcall does the call to the kernel routine that actually
;       does the work of the current system call.
;
;       ENTRY   (ss)                =  user's TCB (PTDA) stack segment
;               (ss:bp)             -> shadow register frame
;               (ax,bx,cx,dx,si,di) =  working values
;
;               [InDos]  > 0 --  Assume we won't be reentered
;
;       EXIT    (ax,bx,cx,dx,si,di) =  working values
;
;               if (worker returned CF = 1)
;                       DI = returned AX
;                       AX = 0
;               else /* worker returned CF = 0 */
;                       DI = 0
;                       AX = returned AX
;
;               Thus: DI = error status value, AX = work value
;
;       WARNING ?sci_argptr will be rewound to the START of the user argument
;               stack, the same place it was when thread program processing
;               began.
;
;       NOTE    The thread program is expected to move AX into the proper
;               return argument location provided by user.
;
;               The return thread ops will load AX with DI before returning
;               to the user, thus returning with AX = status value
;
;               We use DI because none of the workers return anything
;               in DI.

threadop farcall

;       Must load DS and ES while sreg frame is valid for
;       UsrFaultHandler to be able to properly cleanup the stack.

        mov     ds,[bp].sreg_ds
        mov     es,[bp].sreg_es

;       Clear the local fault handler and some file system variables

        push    bp
        push    bx
        xor     bx,bx
        mov     bp,ss:[TKOPTDA].CurrTSD ; restore the system GP fault handler
        mov     word ptr [bp].TSDpfnFault,bx
        mov     bp,ss:[TKOPTDA].CurrTCB
        mov     [bp].TCBFailErr,bx      ; FAIL not in progress
        mov     [bp].TCBJfnFlag,bl      ; clear JFN_FAIL (and others)

;       Call worker routine

        mov     bx,sp
        mov     bp,word ptr ss:[bx+4]   ; set bp = return address
        add     bp,6                    ; calc new return address         ;@SEC
                                        ;@SEC +2 extra to skip over ord
                                        ;   (must skip dword ptr to worker)
                                        ;   (must skip dword ptr to worker)
        xchg    word ptr ss:[bx+4],bp   ; bp -> dword ptr to worker
                                        ; stack has updated return address
        pop     bx

;@SEC   If the Security Subsystem needs to screen 16-bit workers,
;@SEC   the following clc will be "patched" to stc.
;@SEC   fc02: calls the subsystem (who is then responsible for
;@SEC   calling worker)

        public  fc01                    ;@SEC
fc01    label   byte                    ;@SEC So we can patch clc
        clc                             ;@SEC stc-if sec hooks 16bit workers
        jc      fc02                    ;@SEC jump if were hooked

        TimerOff TIM_SCI_ENTRY
        call    dword ptr cs:[bp]       ; far call, stay in protected mode
        ASSUME  DS:nothing,ES:nothing
        TimerOn TIM_SCI_EXIT

fc03:                                   ;@SEC  cleanup as usual...

;*      Clean up after the call to the kernel routine that
;       actually does the work of the current system call.
;
;       ENTRY   (ss)    =  user's TCB (PTDA) stack
;               (ss:bp)             -> shadow register frame
;               (ax)    = error / data value
;               (cf)    = error / no error indicator
;               other registers as returned by worker
;
;       EXIT    if (worker returned CF = 1)
;                       DI = returned AX
;                       AX = 0
;               else /* worker returned CF = 0 */
;                       DI = 0
;                       AX = returned AX
;
;               Thus: DI = error status value, AX = work value
;
;       WARNING ?sci_argptr will be rewound to the START of the user argument
;               stack, the same place it was when thread program processing
;               began.
;
;       NOTE    The thread program is expected to move AX into the proper
;               return argument location provided by user.
;
;               The return thread ops will load AX with DI before returning
;               to the user, thus returning with AX = status value
;
;               We use DI because none of the workers return anything
;               in DI.

        jc      short clu2              ; CF = 1, set up DI, AX for error ret
        xor     di,di                   ; (di) = error status = 0
                                        ; (ax) = return value

;       error set up code at clu2 will jump back here
;
;       The flow structure is a bit convoluted to optimize the normal path,
;       but we always come here even if we've jmped to clu2

clu1:

;       Have system kill user program if we hit a GP fault

        mov     bp,ss:[TKOPTDA].CurrTSD
        mov     word ptr [bp].TSDpfnFault,offset DOSHIGH3CODE:UsrFaultHandler

        pop     bp
        mov     si,[bp].sreg_rpl

;       (ss)    =  user's TCB (PTDA) stack again
;       (si)    =  user's RPL
;       (ss:bp) -> shadow registers again
;       (ss:[bp+?sci_argptr]) -> user argument frame (will be rewound in API)

        ret                             ; Return to system call thread


;       This code here so common case (no error) can fall through to clu1

clu2:   call    AdjustError             ; if failed, fetch hard error code
        mov     di,ax                   ; (di) = error status worker returned
        xor     ax,ax                   ; (ax) = return value for error cases
        jmp     short clu1              ; back to main line

;@SEC   And this code is here to optimize common case (no Security Subsystem)

fc02:                                                                     ;@SEC
        ;@SEC Grab ordinal from farcall codeseg                           ;@SEC
        push    bp                                                        ;@SEC
        push    bx                                                        ;@SEC

        shl     eax, 16                                                   ;@SEC
        mov     bx,  sp                                                   ;@SEC
        mov     bp,  ss:[bx+6]                                            ;@SEC
        sub     bp,  2                                                    ;@SEC
        mov     ax,  word ptr cs:[bp]                                     ;@SEC
        ror     eax, 16                                                   ;@SEC

        pop     bx                                                        ;@SEC
        pop     bp                                                        ;@SEC

        push    cs:[bp]                  ;@SEC push worker address - SEG
        push    cs:[bp+2]                ;@SEC push worker address - OFFSET
        CALLFAR Sec16Router              ;@SEC
        jmp  short fc03                  ;@SEC All done, jump to cleanup

endop    farcall

;**     AdjustError - retrieve masked-off hard error
;
;       AdjustError is used to retrieve the real hard error code that has
;       been masked off by the internal kernel routines.  The current
;       architecture has all hard errors being reported at the lowest level
;       with all actions occurring there.  The lowest level routines do NOT
;       return error codes, simply an error indication. At the higher levels,
;       we note this error condition and map it into some known error.  This
;       is due to the 3.x compatability requirement.    Since the low-level
;       error is not readily accessible, we need to reach into some of the
;       more global objects to retrive it.
;
;       This code has no effect on the compatability box.
;
;       ENTRY       [TCBFailErr] = error code or 0
;       EXIT        (AX) = true error code (as much as possible)
;       USES        AX
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure AdjustError,<hybrid, far16>

;       See if there was a failed hard error

        push    bp
        mov     bp,ss:[TKOPTDA.CurrTCB]
        cmp     [bp].TCBFailErr,0       ; was there a low-level failure?
        jz      short ae9               ; no, go return as we are all done

;       TCBFailErr always has the proper extended error so now we just
;       return it.

        mov     ax,[bp].TCBFailErr      ; retrieve true extended error

ae9:    pop     bp
        ret
EndProc AdjustError


;*
;*      Asciiz string thops
;*


;***    getusrbufz3 - same as getusrbufz except for error return
;
;       This thop gets a temporary buffer, records the fact in a
;       stack variable so we can free it, edits current argument
;       with the temporary buffer address and copies the asciiz
;       string to it. It doesn't change the argument pointer so
;       the existing register load thop's can be used to get the
;       address of the asciiz now in the temporary buffer. Returns
;       ERROR_PATH_NOT_FOUND (3) if the name is above 128 characters
;       long, instead of ERROR_INVALID_NAME.
;
;       ENTRY   (ss:bp) -> shadow register frame
;               (ss:di) -> current argument to perform thread operation on
;               (si)     = RPL value
;
;       EXIT    (current argument) = pointer to kernel buffer
;
;       USES    SI, DI, DS, ES, Flags

threadop getusrbufz3
        push    ERROR_PATH_NOT_FOUND
        jmp     short gub0
endop    getusrbufz3


;### CP20 PTR B721314 ###
;### Add function getusrbufz206
;***    getusrbufz206 - same as getusrbufz except for error return
;
;       This thop gets a temporary buffer, records the fact in a
;       stack variable so we can free it, edits current argument
;       with the temporary buffer address and copies the asciiz
;       string to it. It doesn't change the argument pointer so
;       the existing register load thop's can be used to get the
;       address of the asciiz now in the temporary buffer. Returns
;       ERROR_FILENAME_EXCED_RANGE (206) if the name is above 128
;       characters long, instead of ERROR_INVALID_NAME.
;
;       ENTRY   (ss:bp) -> shadow register frame
;               (ss:di) -> current argument to perform thread operation on
;               (si)     = RPL value
;
;       EXIT    (current argument) = pointer to kernel buffer
;
;       USES    SI, DI, DS, ES, Flags

threadop getusrbufz206
        push    ERROR_FILENAME_EXCED_RANGE
        jmp     short gub0
endop    getusrbufz206


;***    getusrbufz - copy a asciiz string from user space to a kernel buffer
;
;       This thop gets a temporary buffer, records the fact in a
;       stack variable so we can free it, edits current argument
;       with the temporary buffer address and copies the asciiz
;       string to it. It doesn't change the argument pointer so
;       the existing register load thop's can be used to get the
;       address of the asciiz now in the temporary buffer. Returns
;       ERROR_INVALID_NAME is the name is above 128 characters long.
;
;       ENTRY   (ss:bp) -> shadow register frame
;               (ss:di) -> current argument to perform thread operation on
;               (si)     = RPL value
;
;       EXIT    (current argument) = pointer to kernel buffer
;
;       USES    SI, DI, DS, ES, Flags

threadop getusrbufz
        push    ERROR_INVALID_NAME

gub0:   SaveReg <ax,bx,cx>              ; save work regs
        mov     bx,di                   ; set bx = stack offset of user pointer
        or      word ptr ss:[bx+2],si   ; adjust the RPL of source selector

        mov     si,ss:[TKOPTDA].CurrTSD ; (ss:si) = Current TSD pointer
        mov     ss:[si].TSDpfnFault,0
        call    GetTempBuffer           ; (es:di) = temporary buffer
        mov     word ptr ss:[si].TSDpfnFault,offset DOSHIGH3CODE:UsrFaultHandler

        cmp     [bp].sreg_tempbuf1,0
        je      short gub2
        cmp     [bp].sreg_tempbuf2,0
        jne     short gub1

        mov     [bp].sreg_tempbuf2.Offst,di
        mov     [bp].sreg_tempbuf2.Segmt,es
        jmp     short gub3

gub1:   InternalError <getusrbufz: too many buffers>

gub2:   mov     [bp].sreg_tempbuf1.Offst,di
        mov     [bp].sreg_tempbuf1.Segmt,es

gub3:   lds     si,dword ptr ss:[bx]    ; (ds:si) = source string pointer
        mov     ss:[bx].Segmt,es
        mov     ss:[bx].Offst,di
        mov     cx,PMPATHLEN            ; (cx) = maximum size of string

        TestDBCSProcess                 ; DBCS active?
        jz      short gub40             ; yes, do DBCS copy


; do quick non-DBCS copy; convert / to \

        align   4
gub32:  lodsb                           ; al = character
        cmp     al,'/'                  ; forward slash?
        je      short gub34             ; yes, go convert it
        stosb                           ; store character
        or      al,al                   ; end of string?
        loopnz  gub32                   ; loop if not end and not limit
        jz      short gub55             ; no error
        jmp     short gub65             ; string too long error if not end

gub34:  mov     al,'\'                  ; convert into \
        stosb                           ; store \
        dec     cx                      ;
        jnz     gub32                   ; next character
        jmp     short gub65             ; string too long error

; do DBCS copy

        align   4
gub40:  lodsb                           ; get next char. from ds:si into al
        TestDBCSLeadByte
        jnz     short gub50             ; not DBCS lead byte; go copy

; copy DBCS lead and trail byte. No need to check for path separator or string
; terminator

        stosb                           ; store DBCS lead byte
        lodsb                           ; get DBCS trail byte
        stosb                           ; store DBCS trail byte
        dec     cx                      ; substract from string limit
        loopnz  gub40                   ; next character if limit not reached
        jmp     short gub65             ; reached string limit; return error

; not DBCS character. Convert / into \

gub50:  cmp     al,'/'                  ; forward slash?
        je      short gub60             ; yes, go convert it

        stosb                           ; put char. (al) into next pos at es:si
        or      al,al                   ; end of string?
        loopnz  gub40                   ; jmp if char was not NUL
        jnz     short gub65             ; jump if not end of string

gub55:  RestoreReg <cx,bx,ax>
        add     sp,2                    ; skip error info
        ret                             ; return to execute next thread op

gub60:  mov     al,'\'                  ; convert forward slash to back slash
        stosb                           ; put char. (al) into next pos at es:si
        dec     cx                      ;
        jnz     gub40                   ; next character

gub65:
        RestoreReg <cx,bx,ax,di>        ; clean up stack, DI = error info
        jmp     short CleanUpReturn     ; exit sci (return addr still on stack)

endop    getusrbufz


;***    UsrArgError - User gave bogus reserved argument
;                     complain, report what happened, and exit.
;
;       ENTRY:  (ss:bp) -> shadow frame
;
;       EXIT:   di = error status
;
;       USES:   NONE

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure UsrArgError

        mov     di,ERROR_INVALID_PARAMETER        ; set return code
        jmp     short CleanUpReturn               ; exit system call

EndProc UsrArgError


;***    UsrFaultHandler - User fault handler for sci
;       possibly marks thread to die of a protection fault
;
;       ENTRY:
;                  (AX) = ERROR_PROTECTION_VIOLATION,ERROR_INTERRUPT
;
;               ES and DS cleared.
;
;       EXIT:   (SS:BP) = sci stack frame pointer
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure UsrFaultHandler,NEAR,LOCAL
        cmp     ax,ERROR_INTERRUPT
        lea     sp,[bp].sreg_di         ; reset stack pointer
        jne     short usrflt
        mov     di,ax                   ; setup to return error in real (ax)
        jmp     short CleanUpReturn

usrflt: call    UsrArgFault             ; do fault processing

;       Fall into CleanUpReturn

;IF2
;        .errnz  CleanUpReturn-$
;ENDIF

EndProc   UsrFaultHandler

;***    CleanUpReturn -- clean up interrupt thop program
;***    return  --  common return code for system calls
;
;       Does common system call cleanup and returns.
;
;       WORK
;               1. Copy DI (error status) onto saved user AX,
;                  so proper ax can be returned.
;               2. Edit stack so we can return to caller.
;               3. Clean shadow frame off stack.
;               4. Call Exit_Kmode.
;
;       ENTRY   (ss:bp) -> shadow register frame
;
;       EXIT    (ax) = return/error status
;               All other regs will be as they were at entry to sci.
;
;       CALLS   KMExitKmodeSCI

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure CleanUpReturn
        FlatContext <ds,es>,ax
        CALLF32 _TKTstnClrKernelSem     ; make sure semaphore is cleared

;       Fall through to next threadop

; NOTE:   DS must still = Flat Context

EndProc CleanUpReturn

SCI_return_StartHook:
threadop return

        lea     sp,[bp].sreg_di         ; reset stack pointer

;       (SS:BP) = kstk frame pointer

        movzx   eax,di                  ; (AX) = return (error) status

SCI_return_EndHook:
        mov     [bp].kstk_ssf.ssf_eax,eax

;       free all the temp buffers allocated

        mov     ax,[bp].sreg_tempbuf1.Segmt
        or      ax,[bp].sreg_tempbuf2.Segmt
        jnz     trt20                   ; have a tempbuf to return

trt10:  mov     si,ss:[TKOPTDA].CurrTSD ; restore the system GP fault handler
        mov     word ptr ss:[si].TSDpfnFault,0

        add     sp,size sreg            ; shadow frame is gone
        TimerOff TIM_SCI_EXIT

sciKernelExit:
        ?sci_ds = ?SCI_FLAT             ; DS = Flat Context on to_return entry
        ?sci_es = ?SCI_UNKNOWN
        KMExitKmodeSCI

;       Have temp buffer(s) - release them

trt20:  or      [bp].sreg_tempbuf1.Segmt, 0
        jz      short trt30
        les     di,[bp].sreg_tempbuf1
        call    FreeTempBuffer          ; NOTE: DS must still = Flat Context
trt30:  or      [bp].sreg_tempbuf2.Segmt, 0
        jz      trt10
        les     di,[bp].sreg_tempbuf2
        call    FreeTempBuffer          ; NOTE: DS must still = Flat Context
        jmp     trt10

endop   return


;***    enterfscrit - enter file system thread critical section
;
;       enterfscrit seizes the semaphore used to serialize thread entry
;       to the file system.
;
;       ENTRY   (ss:bp) -> shadow register frame
;               (ds)    =  Flat Context
;               ax,cx,dx already saved if in use
;
;       EXIT    TmpSem = Held by this thread  (see pdata.asm)
;                   or
;               Error Condition due to abnormal wakeup indicating that
;                   we've gotten a kill signal.  Exit via direct jump
;                   to CleanUpReturn, which unwinds from the sci
;                   mechanism and eventually calls Exit_Kmode.
;
;
;       WARNING ** MAY PROCBLOCK **
;
;       USES    ax,di,es

threadop enterfscrit

        ASSUME DS:FLAT,ES:NOTHING,SS:NOTHING

        mov     di,ss:[TKOPTDA].CurrTSD
        mov     ss:[di].TSDpfnFault,0   ; restore the system GP fault handler
        FlatContext <es>,ax
        SCI_Take_Krnl_Sem
        jc      CleanUpReturn           ; abnormal wakeup, take error exit
                                        ;   (return address still on stack)

;       Caller assumes that DS & ES = Flat Context on exit

                                        ; Start Defect 72417
        mov     di,ss:[TKOPTDA].CurrTCB
        mov     ss:[di].TCBNoSetDir,0 ; set directories on search
                                        ; End Defect 72417
        mov     di,ss:[TKOPTDA].CurrTSD
        mov     word ptr ss:[di].TSDpfnFault,offset DOSHIGH3CODE:UsrFaultHandler
        ret

endop   enterfscrit


;***    exitfscrit - exit file system thread critical section
;
;       exitfscrit releases the semaphore used to serialize thread entry
;       to the file system.
;
;       ENTRY   (ss:bp)   -> shadow register frame
;
;       EXIT    (SS:TmpSem) = 0
;
;       USES    si,ds,es

threadop exitfscrit

        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

        SaveReg <ax,cx,dx>
        FlatContext <ds,es>,ax
        mov     si,ss:[TKOPTDA].CurrTSD
        mov     ss:[si].TSDpfnFault,0   ; restore the system GP fault handler
        SCI_Clear_Krnl_Sem

;       Caller assumes that DS & ES = Flat Context on exit

        mov     si,ss:[TKOPTDA].CurrTSD
        mov     word ptr ss:[si].TSDpfnFault,offset DOSHIGH3CODE:UsrFaultHandler
        RestoreReg <dx,cx,ax>
        ret

endop   exitfscrit


;***EP  SCIReturnX - kernel exit return code
;
;       ENTRY   NONE
;
;       EXIT    back to user code
;

        align   4

IRP     x,<0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36>      ;B729310

Procedure   SCIReturn&x
            popfd
            RETFD     x
EndProc     SCIReturn&x

ENDM

;***ED  _ausSCIReturn - array of kernel exit return functions
;
;       This array is used by the kmode rebuild stack frame
;       functions to setup the "ssf_sciret" field in the sci
;       stack frame.

        public  _ausSCIReturn
_ausSCIReturn   label   word

IRP     x,<0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36>      ;B729310

        dw      offset DOSHIGH3CODE:SCIReturn&x

ENDM
.errnz  (MAXRETN - ($ - _ausSCIReturn - 2))                          ;B729310

;***ED  _fpfnSCIKernelExit - kernel exit routine address
;
;       This is the far address of the function to return to
;       after the stack frame has be rebuilt.
;
                        public  _fpfnSCIKernelExit
_fpfnSCIKernelExit      df      sciKernelExit


;***    Take_Kernel_Sem - Grab the PTDA semaphore
;
;       Called to serialize multiple threads which use per-process
;       variables.
;
;       A thread may set the serialization semaphore multiple times
;       during the course of a system call.  Each call to Take_Kernel_Sem
;       must be matched by a call to Clear_Kernel_Sem.  The final call
;       will release the semaphore to a waiting thread, if any.
;
;       Note that we don't have to protect this code with CLI/STI since
;       this semaphore is only claimed and cleared at task time
;
;       ENTRY   NONE
;       EXIT    ptda_ptdasem modified
;                       or
;               Carry Flag set indicating an abnormal wakeup from TKSleep
;
;       USES    None
;
;       EFFECTS Calls _KSEMRequestSem
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure Take_Kernel_Sem,<hybrid>
        SaveReg <ds,es,eax,edx,ecx>
        FlatContext <ds,es>,ax
        SCI_Take_Krnl_Sem
        .errnz  NO_ERROR
        RestoreReg <ecx,edx,eax,es,ds>
        ret
EndProc   Take_Kernel_Sem


;***    Clear_Kernel_Sem  --  Clears the PTDA Semaphore
;
;       Releases the PTDA semaphore.  In the case of the DosICreateThread
;       system call, the child thread should not own the semaphore.  This
;       sitaution must be detected.
;
;
;       ENTRY   None
;       EXIT    ptda_ptdasem is released if nesting level is 0
;
;       USES    None
;
;       EFFECTS Calls _KSEMRequestSem

        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure Clear_Kernel_Sem,<hybrid>
        SaveReg <ds,es,eax,ecx,edx>
        FlatContext <ds,es>,ax
        SCI_Clear_Krnl_Sem
        RestoreReg <edx,ecx,eax,es,ds>
        ret
EndProc  Clear_Kernel_Sem


HIGH3CODE ENDS

        END
