;       SCCSID = @(#)setfetum.asm       6.5 92/02/02
SCCSID  equ     "@(#)setfetum.asm       6.5 92/02/02"
PAGE    ,132
TITLE   setfetum - Set/Fetch User Memory = protected transfer routines
NAME    setfetum

;***    Protected mode routines to transfer data to/from user space
;
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;
;       Inside the kernel it is often necessary to get data from
;       or return data to user space.  Examples:
;
;       1)  Many of the file system calls have the application pass
;           in a pointer to a string with a path and file name.
;           Inside the kernel it is necessary to follow this pointer
;           and read this string.
;
;       2)  DosQCurDir has the application pass in a pointer to a
;           buffer.  The api call fills the buffer with the current
;           directory.
;
;       Transfers of this nature must be handled carefully.  Several
;       problems present themselves.  The most common is the application
;       program gives the Dos a bad pointer.  When the dos tries to
;       use this pointer a general protection fault occurs.  General
;       protection faults in application code are not much of a
;       problem.  We kill the process.  A general protection
;       fault in kernel mode is a big problem.  We must distinguish
;       between the application giving dos a bad pointer and dos
;       itself causing the fault.  In the first case we want to
;       kill the offending process.  In the second case dos is sick
;       and we want to issue a panic message and halt.
;
;       To distinguish between the two cases all transfers from and to
;       user space from the kernel MUST be done with one of the
;       procedures in this file.
;
;       User space pointers are always passed in as BX:offset, NOT
;       DS:offset or ES:offset.  Kernel space pointers are passed using
;       the selector.  We must use BX in order to have the caller trap
;       from simply trying to load the selector register with the bogus
;       selector.
;
;       In case of success, the segment register used to point to user
;       space will be nulled out before returning.  This is because such
;       a selector is unreliable accross a procblock, and we don't want
;       the caller to trap by accident because we left it active.
;
;       In case of failure, both DS and ES will be nulled out.  This supports
;       the notion of not leaving user selectors in segment registers.  We
;       zap both of them for simplicity in trap handler code.
;
;       So far the following procedures have been defined:
;
;       SuByte - set user byte
;       SuWord - set user word
;       SuBuff - set user buffer
;       SuBuffz- set user buffer until NUL byte or cx = 0
;       FuByte - fetch user byte
;       FuWord - fetch user word
;       FuBuff - fetch user buffer
;       FuBuffz- fetch user buffer until NUL byte or cx = 0
;
;       Each of these routines performs a "no-fail" data transfer.
;       They all work as follows:
;
;               (1) Set a protection trap exit address, all routines
;                   share the same address.  Setting this address disables
;                   panics on traps.
;
;               (2) Perform the transfer.  If it fails due to a protection
;                   violation, the trap exit will be taken, and we'll
;                   exit with carry set.
;
;               (3) If the transfer didn't fail, reset the trap handler,
;                   and clear carry to tell the caller things are ok.
;
;       ProbeBuff - Test address range access for I/O.  ProbeBuff does not
;                   actually perform any I/O, but rather is used for address
;                   checking by I/O workers.
;
;       Modification History:
;       Created BMW     2/13/86
;
;               DJG     4/24/86
;                       Added SuBuffz.
;
;               KCD     5/2/86
;                       Added documentation.
;                       Added SuByte, SuWord, FuByte, FuWord, FuBuff, FuBuffZ.
;
;               BMW     06/12/86
;                       Updated documentation
;                       Made all the routines live
;
;               BWS     09/22/86
;                       ProbeBuff fails requests that will cause segment wrap.
;
;               FJD     08/12/87 1.0 PTM 7010
;                       Added FuWordNF, SuWordNF
;
;               FJD     09/27/87
;                       Added FuBuffNF, FuBuffzNF
;
;               FJD     01/11/88
;                       Added SuByteNF
;
;               JSF     07/19/88
;                       Returned error if segment wrap in ProbeBuff
;                       (1.2 PTM 1143)
;
;   11/18/94   87518   Change Team   If ProbeBuff is called in a VDM's process,
;                                    assume ring 3 stack segment selector and
;                                    don't check the TSDUserSS
;

.xlist
include dosseg.inc
include basemaca.inc
include ostypes.inc
include osconst.inc
include dossymc.inc
include osmaca.inc
include fnotask.inc
include error.inc
include filesys.inc
include pmode.inc
include mi.inc
include sigdef.inc
include except.inc
include infoseg.inc
include pgexport.inc
include vmowner.inc
include vmexport.inc
include tkexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include sem.inc
include tkptda.inc
include proc.inc     ;87518
.list

        ModName , FNO_SETFETUM_ASM

        ReferGlobal     TCResched,BYTE
        ReferFlat       _CpuType,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pgcPageFaults,DWORD                            ;d73114

        EXTRN32         _SELVirtToLin
        EXTRN32         _TKProbeBuff
        EXTRN32         _TKTCYield
        EXTRN32         _TKCatchFault
        EXTRN32         _TKClrFault

        EXTRNFAR        TCYield
        EXTRNFAR        UsrArgFault

;*      Maximum size of a block can be called before yielding

MAX_STRING_EXPONENT     equ     11
MAX_STRING              equ     (1 SHL MAX_STRING_EXPONENT)

          FARCODE HIGH2
HIGH2CODE SEGMENT


;*      SetTrapExit   - make "TrapExit" GP fault handler for this thread
;*      SetTrapExitNF - make "TrapExitNF" GP fault handler for this thread
;
;       SetTrapExit sets up a fatal trap handler.
;       SetTrapExitNF sets up a nonfatal trap handler.
;
;       The previous fault handler (if any) is saved on the stack.
;       Both of these routines use SetTrap and PushTrap to do the work, and
;       SetTrap should not be used anywhere else in the file.
;
;       (sel) is saved away in "TSDselFault" for "TrapExit" which
;       passes it to be displayed as the error code.  If (sel) is
;       blank, "TSDselFault" is not modified.
;
;       ENTRY:  (sel) is the user selector that we are checking.
;
;       EXIT:   NONE
;
;       USES:   BP, SP

SetTrapExit     macro   sel
        push    dx
        SetTrap <offset DOSHIGH2CODE:TrapExit>,sel
        endm

SetTrapExitNF   macro   sel
        push    dx
        SetTrap <offset DOSHIGH2CODE:TrapExitNF>,sel
        endm


;***    ClrTrapExit - restore previous fault handler
;
;       This macro invokes ClrTrap.
;
;       ENTRY:  NONE
;       EXIT:   NONE
;       USES:   BP, SP

ClrTrapExit macro
        ClrTrap
        pop     dx
        endm


;***LM  tkCopyData16 - Copy data for 16-bit segmented code
;
;       ENTRY:  (ds:si) = source pointer
;               (es:di) = destination pointer
;               (cx)    = byte count
;
;       EXIT:   (ds:si) = ptr to one past last byte copied in source
;               (es:di) = ptr to one past last byte copied in dest
;               (cx) = 0
;
;       USES:   dx, cx, si, di, fl

tkCopyData16 macro
        local   bc0,bc1

        cmp     cx,9        ; Small copy ?.
        jbe     bc1         ; Yes - do simple rep movsb.
        mov     dx,di
        neg     dx          ; d1d0 = count to quad-align.
        and     dx,3        ; Is dst quad-aligned ?
; dx = number of bytes remaining after round off to quad-aligned.
        jz      bc0         ; Yes.
        sub     cx,dx       ; cx must be > 9.
; dst is not quad-aligned, copy enough to make it quad-aligned.
        xchg    dx,cx       ; cx = # of bytes to copy to make quad-aligned.
    B1EMac rep movsb
        mov     cx,dx       ; cx = reminder of byte count.
bc0:
        mov     dx,cx       ; set ax in case we took a jump to bc0.
        shr     cx,2        ; cx = reminder of dword count.
        and     dx,3        ; dx = reminder byte count after dword move.

        CpuMode 386
    B1EMac rep movsd
        CpuMode reset

        mov     cx,dx       ; cx = Remaining byte count.
bc1:
    B1EMac rep movsb

        endm



;***    SuByte - set user byte
;***    SuByteNF - set user byte (not fatal on error)
;
;       SuByte preforms a one byte data transfer from AL to user space
;       watching for protection faults.
;
;       SuByteNF does the same thing, but does not kill the process.
;
;       ENTRY:  AL    =  byte to write into user space
;               BX:DI -> pointer to destination buffer (may be in user space)
;
;       EXIT:   Carry clear (no general protection fault)
;                       Byte in AL written to user space
;                       DI modified based on state of direction flag (+/- 1)
;                       Direction flag unchanged
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process (SuByte only)
;                       Direction flag is cleared
;
;
;       USES:   AX, DI, DS, ES, FLAGS
;
;       CALLS:  TrapExit (if protection fault)
;
;       WARNING: This routine does not assume the state of the direction
;               flag.  Upon exit, di will be modified based on
;               the state of the direction flag.
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Target selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

;***************************** WARNING ***********************************
;       If we exit with CY set, the direction flag WILL BE CLEAR
;       regardless of how it was set when we entered.  Otherwise
;       it will be preserved.
;*************************************************************************

Procedure SuByteNF,<near,faronly>
        SetTrapExitNF                   ; set nonfatal trap handler
        jmp     short sb1
EndProc   SuByteNF

Procedure SuByte,<near,faronly,far16>
        SetTrapExit bx                  ; set trap handler and save away sel
sb1:    mov     es,bx

;       AL    =  data byte
;       ES:DI -> target in user space, ES has proper RPL

        stosb                           ; move AL to es:di

;       If we get here, operation worked

        xor     ax,ax
        mov     es,ax                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset previous trap handler
        clc
        ret                             ; return with carry clear
EndProc SuByte


;***    SuWord   - set user word
;***    SuWordNF - set user word (not fatal on error)
;***    SuDWord  - Set user DWord
;
;       SuWord preforms a one word data transfer from AX to user space
;       watching for protection faults.
;
;       SuWordNF does the same thing, but does not kill the process.
;
;       ENTRY:  AX    =  word to write into user space
;               (EAX = word to write in case of SuDword)
;               BX:DI -> pointer to destination buffer (may be in user space)
;
;       EXIT:   Carry clear (no general protection fault)
;                       Word in AX written to user space
;                       DI modified based on state of direction flag (+/- 2)
;                       Direction flag unchanged
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process (SuWord only)
;                       Direction flag is cleared
;
;       USES:   AX, DI, DS, ES, FLAGS
;
;       CALLS   TrapExit or TrapExitNF (if protection fault)
;
;       WARNING: This routine does not assume the state of the direction
;               flag.  Upon exit, di will be modified based on
;               the state of the direction flag.
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Target selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

;***************************** WARNING ***********************************
;       If we exit with CY set, the direction flag WILL BE CLEAR
;       regardless of how it was set when we entered.  Otherwise
;       it will be preserved.
;*************************************************************************

Procedure SuWordNF,<near,faronly,far16>
        SetTrapExitNF                   ; set nonfatal trap handler
        jmp     short sw1
EndProc SuWordNF

Procedure SuWord,<near,faronly>

        SetTrapExit bx                  ; set trap handler and save away sel
sw1:    mov     es,bx

;       AX    =  data byte
;       ES:DI -> target in user space, ES has proper RPL

        stosw                           ; move AX to es:di

;       If we get here, operation worked

        xor     ax,ax
        mov     es,ax                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset previous trap handler
        clc
        ret                             ; return with carry clear

EndProc SuWord

        CpuMode 386

Procedure SuDWord,<near,faronly>

        SetTrapExit bx                  ; set trap handler and save away sel
        mov     es,bx

;       EAX    =         data byte
;       ES:DI -> target in user space, ES has proper RPL

        stosd                           ; move EAX to es:di

;       If we get here, operation worked

        xor     ax,ax
        mov     es,ax                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset previous trap handler
        clc
        ret                             ; return with carry clear
EndProc SuDWord

        CpuMode reset

;***    FuByte - Fetch user byte
;***    FuByteNF - Fetch user byte (not fatal on error)
;
;       FuByte preforms a one byte data transfer from user space to AL
;       watching for protection faults.
;
;       ENTRY:  BX:SI -> pointer to source buffer (may be in user space)
;
;       EXIT:   Carry clear (no general protection fault)
;                       AL = byte from BX:SI
;                       SI modified based on state of direction flag (+/- 1)
;                       Direction flag unchanged
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process
;                       Direction flag is cleared
;
;       USES:   AX, BX, SI, DS, ES, FLAGS
;
;       CALLS:  TrapExit (if protection fault)
;
;       WARNING: This routine does not assume the state of the direction
;               flag.  Upon exit, si will be modified based on
;               the state of the direction flag.
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Source selector is in BX, NOT DS.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

;***************************** WARNING ***********************************
;       If we exit with CY set, the direction flag WILL BE CLEAR
;       regardless of how it was set when we entered.  Otherwise
;       it will be preserved.
;*************************************************************************

Procedure FuByteNF,<near,faronly,far16>
        SetTrapExitNF                   ; set nonfatal trap handler
        jmp     short fb1
EndProc   FuByteNF

Procedure FuByte,<near,faronly>
        SetTrapExit bx                  ; set trap handler and save away sel
fb1:    mov     ds,bx

;       DS:SI -> source in user space, DS has proper RPL

        lodsb                           ; (AL) = user data byte

;       If we get here, operation worked

        xor     bx,bx
        mov     ds,bx                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset default trap handler
        clc
        ret                             ; return with carry clear
EndProc FuByte


;***    FuWord   - Fetch user word
;***    FuWordNF - Fetch user word (not fatal on error)
;
;       FuWord preforms a one word data transfer from user space to AX
;       watching for protection faults.
;
;       FuWordNF does the same thing, but does not kill process.
;
;       ENTRY:  BX:SI -> pointer to source buffer (may be in user space)
;
;       EXIT:   Carry clear (no general protection fault)
;                       AX = word from BX:SI
;                       SI modified based on state of direction flag (+/- 2)
;                       Direction flag unchanged
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process (FuWord only)
;                       Direction flag is cleared
;
;       USES:   AX, BX, SI, DS, ES, FLAGS
;
;       CALLS:  TrapExit or TrapExitNF (if protection fault)
;
;       WARNING: This routine does not assume the state of the direction
;               flag.  Upon exit, di will be modified based on
;               the state of the direction flag.
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Source selector is in BX, NOT DS.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

;***************************** WARNING ***********************************
;       If we exit with CY set, the direction flag WILL BE CLEAR
;       regardless of how it was set when we entered.  Otherwise
;       it will be preserved.
;*************************************************************************

Procedure FuWordNF,<near,faronly,far16>
        SetTrapExitNF                   ; set nonfatal trap handler
        jmp     short fw1
EndProc   FuWordNF

Procedure FuWord,<near,faronly>
        SetTrapExit bx                  ; set trap handler and save away sel
fw1:    mov     ds,bx

;       DS:SI -> source in user space, DS has proper RPL

        lodsw                           ; (AX) = user data word

;       If we get here, operation worked

        xor     bx,bx
        mov     ds,bx                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset previous trap handler
        clc
        ret                             ; return with carry clear
EndProc   FuWord


;***    FuStrLen - Get length of user string (single null terminated buffer)
;
;       ENTRY:  BX:DI -> pointer to string (may be in user space)
;
;       EXIT:   Carry clear (no general protection fault)
;                       CX = length of string plus one for the null terminator
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process
;
;       USES:   AX,BX,CX,DI,DS,ES,flags
;
;       CALLS:  TrapExit (if protection fault)
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Source selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
;               So we don't delay dispatching to another task for too long,
;               this routine can't be called if the string to be moved is
;               greater than 2k.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure FuStrLen,<near,faronly,far16>
        SetTrapExit bx                  ; set trap handler and save away sel
        mov     es,bx

;       ES:DI -> source in user space, ES has proper RPL

        cld
        mov     cx,-1                   ; determine end of module pathname
        xor     al,al                   ; find end of pathname string
        repne scasb
        not     cx                      ; (CX) = pathname length + null byte

;       If we get here, operation worked

        xor     bx,bx
        mov     es,bx                   ; don't leave user selector lying around

        ClrTrapExit                     ; reset previous trap handler
        clc
        ret                             ; return with carry clear
EndProc   FuStrLen


;***    FuStrLenZ - find size of ASCIIZ strings with null terminator
;
;       Scans a user strings (usually the environment segment when
;       loading a program) until it hits a double null or the string
;       is greater than 32k.
;
;       ENTRY:  (BX:DI) is string to search
;
;       EXIT:   Carry clear
;                   (CX) = size
;               Carry set
;                   (AX) = error code
;                       ERROR_BAD_ENVIRONMENT
;                       ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, DX, DI, DS, ES, Flags
;
;       CALLS:  TrapExit (if protection fault)
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Source selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure FuStrLenZ,<near,faronly,far16>
        SetTrapExit bx                  ; set local handler and save away sel
        mov     es,bx

        mov     bx,di                   ; (BX) = start offset for length
        mov     dx,8000h/MAX_STRING     ; at most 32k (16 * 2k)
        xor     al,al
        cld

fslz1:  mov     cx,MAX_STRING
fslz2:  repnz scasb                     ; find the null byte
        jnz     fslz4                   ;  jump if end of 2k block

        dec     cx                      ; dec CX for next nul byte test
        scasb                           ; is there another null byte?
        jnz     fslz2                   ;  no, scan some more

        sub     di,bx                   ; this CLEARS the CARRY flag
        mov     cx,di                   ; (CX) = size of environment

fslz3:  ClrTrapExit
        mov     dx,0
        mov     es,dx                   ; clear user selector
        ret                             ; EXIT

fslz4:  dec     dx                      ; check if all 32k has been scanned
        jz      fslz5

;       Must yield to time critical threads every 2k.

        push    ds
        DosContext ds
        cmp     TCResched,0             ; if time crit threads need scheduling
        pop     ds
        ASSUME  DS:NOTHING
        je      fslz1

        push    es
        push    word ptr 0
        pop     es                      ; clear user selector during yield.
        push    bp
        PushTrap <CLEAR>,bp
        CALLFAR TCYield                 ; yield for time critical threads
        PopTrap bp
        pop     bp
        pop     es                      ; restore user selector
        jmp     short fslz1             ; jump to next 2k block

;       If string is >32k return an error.

fslz5:  mov     ax,ERROR_BAD_ENVIRONMENT
        stc                             ; CX is out...
        jmp     fslz3
EndProc   FuStrLenZ


;***    SuBuff - set user buffer
;
;       SuBuff copies a buffer of arbitrary (CX bytes) length into
;       user space, watching for protection faults.
;
;       ENTRY:  DS:SI -> source buffer
;               BX:DI -> target buffer, may be in user space
;               CX    =  transfer length
;
;       EXIT:   Carry Clear
;                       Transfer worked
;                       SI, DI, both advanced by rep movsb
;
;               Carry Set
;                       Protection violation
;                       AX = ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, SI, DI, DS, ES, FLAGS
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Target selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
;               So we don't delay dispatching to another task for too long,
;               this routine can't be called if the block to be moved is
;               greater than 2k.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure SuBuff,<near,faronly,far16>
        AssertCLD SuBuff
        SetTrapExit bx                  ; set local GP fault handler
        mov     es,bx                   ; (ES) = user buffer selector
        jmp     SuFuBuff                ; jump to common code
EndProc   SuBuff


;***    FuBuff - Fetch user buffer
;***    FuBuffNF - Fetch user buffer (not fatal on error)
;
;       FuBuff copies a buffer of arbitrary (CX bytes) length from
;       user space to kernel space, while watching for protection faults.
;
;       FuBuffNF does the same thing, but does not kill the process.
;
;       ENTRY:  BX:SI -> pointer to source buffer (may be in user space)
;               ES:DI -> target buffer (in kernel space)
;               CX    =  number of bytes to copy
;
;       EXIT:   Carry clear (no general protection fault)
;                       Transfer worked
;                       SI, DI, both advanced by rep movsb
;
;               Carry Set (general protection fault)
;                       AX = ERROR_PROTECTION_VIOLATION
;                       Flags set in PTDA to kill process
;
;       USES:   AX, BX, CX, SI, DI, DS, ES, FLAGS
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Source selector is in BX, NOT DS.  This is because loading
;               a bad selector into ES would cause a fault.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure FuBuffNF,<near,faronly>
        SetTrapExitNF                   ; set nonfatal trap handler (no sel)
        jmp     short fbl0
EndProc   FuBuffNF

Procedure FuBuff,<near,faronly,far16>
        SetTrapExit bx                  ; set trap handler and save way sel
fbl0:   AssertCLD FuBuff
        mov     ds,bx                   ; (DS) = user's buffer selector

Entry   SuFuBuff,,local                 ; entry from SuBuff

;       (DS:SI) -> source
;       (ES:DI) -> target
;       (CX)    =  bytes to transfer

        cmp     cx,MAX_STRING
        jbe     fbl3                    ; jump if block is 2K or below

        mov     bx,cx
        and     bx,MAX_STRING - 1       ; (BX) = the remainder bytes to copy.

        mov     ax,cx
        shr     ax,MAX_STRING_EXPONENT  ; (AX) = number of 2048 byte blocks

fbl1:   mov     cx,MAX_STRING           ; set up for block move
        tkCopyData16

        push    ds
        DosContext ds
        cmp     TCResched,0             ; if time crit threads need scheduling
        ASSUME  DS:NOTHING
        je      fbl2

        push    es                      ; save user sel, if in es
        push    0
        pop     es                      ; clear user sel, if in es
        push    bp
        PushTrap <CLEAR>,bp
        CALLFAR TCYield                 ; yield for time critical threads
        PopTrap bp
        pop     bp
        pop     es                      ; restore possible user sel

fbl2:   pop     ds
        dec     ax
        jnz     fbl1
        mov     cx,bx                   ; get remaining number of bytes

;       (DS:SI) -> source
;       (ES:DI) -> target
;       (CX)    =  bytes to transfer

fbl3:   tkCopyData16                            ; copy block

;       If we get here, operation worked

        xor     bx,bx
        mov     ds,bx                   ; don't leave user selector lying around
        mov     es,bx

        ClrTrapExit                     ; reset default trap handler
        clc
        ret                             ; return with carry clear
EndProc   FuBuff


;***    SuBuffz - set user buffer (strncpy)
;
;       SuBuffz copies a NULL terminated string to user space.  The copy
;       will stop when CX = 0 or a NULL byte is hit.  Protection faults
;       will be caught and dealt with.
;
;       ENTRY:  DS:SI -> source buffer
;               BX:DI -> target buffer, may be in user space
;               CX    =  max. transfer length
;
;       EXIT:   Carry Clear
;                       Transfer worked OK
;                       SI, DI, advanced until limit hit, or nul hit
;                       CX decremented by count of bytes copied
;
;               Carry Set
;                       Protection violation
;                       AX = ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, SI, DI, DS, ES, FLAGS
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Target selector is in BX, NOT ES.  This is because loading
;               a bad selector into ES would cause a fault.
;
;               So we don't delay dispatching to another task for too long,
;               this routine can't be called if the string to be moved is
;               greater than 2k.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure SuBuffz,<near,faronly,far16>
        AssertCLD SuBuffz
        SetTrapExit bx          ; set local GP fault handler and save away sel
        mov     es,bx           ; (ES) = user buffer selector
        jmp     SuFuBuffz       ; jump to common code
EndProc SuBuffz


;***    FuBuffz - set user buffer (strncpy)
;***    FuBuffzNF - set user buffer (strncpy) (not fatal on error)
;
;       FuBuffz copies a NULL terminated string from user space.  The copy
;       will stop when CX = 0 or a NULL byte is hit.  Protection faults
;       will be caught and dealt with.
;
;       FuBuffzNF does the same thing, but does not kill the process.
;
;       ENTRY:  BX:SI -> source buffer
;               ES:DI -> target buffer, may be in user space
;               CX    =  max. transfer length
;
;       EXIT:   Carry Clear
;                       Transfer worked OK
;                       SI, DI, advanced until limit hit, or nul hit
;                       CX decremented by count of bytes copied
;
;               Carry Set
;                       Protection violation
;                       AX = ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, SI, DI, DS, ES, FLAGS
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
;               Target selector is in BX, NOT DS.  This is because loading
;               a bad selector into ES would cause a fault.
;
;               So we don't delay dispatching to another task for too long,
;               this routine can't be called if the string to be moved is
;               greater than 2k.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure FuBuffzNF,<near,faronly>
        SetTrapExitNF           ; set nonfatal fault handler (no sel)
        jmp     short fbz0
EndProc   FuBuffzNF

Procedure FuBuffz,<near,faronly>
        SetTrapExit bx          ; set local fault handler and save away sel
fbz0:   AssertCLD FuBuffz
        mov     ds,bx

Entry   SuFuBuffz,,local        ; entry from SuBuffz

;       DS:SI -> source
;       ES:DI -> target
;       CX    =  maximum transfer length

        jcxz    fbz2            ; jump if transfer length is zero

fbz1:   lodsb                   ; Get next char. from ds:si into al
        stosb                   ; Put char. (al) into next position at es:si
        or      al,al           ; end of string?
        loopnz  fbz1            ; Jump if char. was not NUL and still more
                                ;  chars. to copy

fbz2:   xor     bx,bx
        mov     ds,bx           ; don't leave user selector lying around
        mov     es,bx

        ClrTrapExit
        clc
        ret
EndProc  FuBuffz


;***    ProbeBuff - Probe I/O buffer for access validity
;
;       ProbeBuff determines validity of the address range of the an I/O
;       buffer by trying access operations on its ends.  If any operation
;       fails, the trap handler will dispatch to "TrapExit", which will
;       set carry and return.
;
;       We probe the ends by trying to read the first and last bytes of
;       the buffer.  We probe for write access by trying to write back
;       the first byte of the buffer.  If any byte is writeable, they are
;       all writeable.  Since we write back the byte we just read, the
;       buffer is preserved even in case of some later failure.
;
;       ENTRY:  BX:SI =  beginning of transfer buffer
;               CX    =  length of transfer buffer, 0 == 64k
;               DH    =  transfer type
;                         ACCTYPE_READ (0) - output, only need read access
;                         ACCTYPE_RW   (1) - input, need read/write access
;
;       EXIT:   Carry Clear
;                       Operation worked, I/O buffer is OK
;               Carry Set
;                       Buffer INVALID, AX = error code
;
;       USES:   EAX, EBX, ECX, DS, Flags
;
;       NOTE:   This routine does NOT do RPL stamping.  The caller must do
;               any necessary RPL stamping.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

;; 81117 - The trap handler (TrapExit) is very particular about the state
;;         of the stack when he is visited. We run into problems when we
;;         push stuff onto the stack (save registers) in ProbeBuff. I've
;;         changed the target trap handler to a small one which un-does
;;         the local storage and then falls thru to the old one. Change
;;         the following define (TMP_STORAGE) if you need more...

TMP_STORAGE     =       8               ;; 81117 (currently two dd's)

Procedure ProbeBuff,<near,faronly>
        push    dx                      ; set local fault handler and save sel
        SetTrap <offset DOSHIGH2CODE:TmpTrapExit>,bx
        sub     sp, TMP_STORAGE         ; allocate local storage

        mov     ds,bx                   ; (DS) = sel, verify privilege level
        mov     ax,ERROR_PROTECTION_VIOLATION                                  ;B724492
                                        ; Anticipate error
        cmp     bx,7                    ; Any selector below or equal to 7 is  ;B724492
        jbe     TmpTrapExit             ;   invalid.                           ;B724492
        movzx   ebx,cx                  ; (EBX) = length ( 0 => 64K)
        dec     bx
        add     bx,si                   ; (BX) = offset of last byte of buffer
        jc      TmpTrapExit             ; Segment wrap; not allowed!
        mov     al,[bx]                 ; Read last byte to check limit
        mov     cx,ds                   ; (CX) = selector

;       If CPUType is CPU_386 *and* it is a write operation
;       then call _TKProbeBuff. Else just touch the buffer.

        FlatContext <ds>,ax
        cmp     [_CpuType],CPU_486      ; 80486 or better ?
        jz      short prb03             ; OK to just test and probe     ;d73114

        or      dh,dh                   ; 'write into memory' operation ?            ;B724492
        jz short prb03                  ; no, use fast path probe       ;d73114

;; 87518 - The TSDUserSS may not be correct for VDM's if kernel mode was
;;         entered via trap06 (arpl).  For example,  Novell's VDD registers
;;         for a s/w int, receives the int, calls the Novell PDD which calls
;;         devhlp_verifyaccess.  The TSDUserSS actuall points to the vsf_altgs
;;         register instead of the user's stack segment selector.

        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM   ;87518 - if vdm, assume
        je short prb03                              ;87518   ring 3 user process

;; 81117 - I'm not changing anything here. I assume we are not going to
;;         take a trap here....
        push    edi
        push    ax
        mov     edi, _pTCBCur           ; get TCB addressability
        mov     edi, [edi].TCBpTSD      ; get TSD      "
        mov     ax, [edi].TSDUserSS     ; user's stack segment selector
        and     al, 3                   ; al = CPL
        cmp     al, 2                   ; Ring 2 caller?
        pop     ax
        pop     edi
        je short prb20                  ; yes, call _TKProbeBuff
;; ... 81117

;       Its a 80486 or better or it's a 386 and safe. Just touch buffer.
;       CX:SI   Pointer to first byte in buffer
;       CX:BX   Pointer to last byte in buffer

;       Record the running count of page faults.  If this value         ;d73114
;       changes  while executing the following loop, then retart.       ;d73114
;       This will ensure that all pages in the range are present        ;d73114
;       at the completion of this routine.                              ;d73114
prb03:  add     sp, TMP_STORAGE         ;; 81117 - reclaim stack
        push    edi                     ; save (EDI)                    ;d73114
prb05:  push    esi                     ; save (ESI)                    ;d73114
        FlatContext ds                                                  ;d73114
        mov     edi, _pgcPageFaults                                     ;d73114

        mov     ds,cx                   ; DS = sel of buffer
        movzx   esi,si                  ; DS:ESI, DS:EBX encompass the buffer
prb08:  mov     al,byte ptr [si]        ; (AL) = byte from this page
        or      dh,dh                   ; Check write access?
        jz      short prb10             ;  no, were done
        mov     byte ptr [si],al
prb10:  add     esi,PAGESIZE
        and     esi,NOT PAGEMASK
        cmp     esi,ebx
        jbe     prb08
        FlatContext ds                                                  ;d73114
        cmp     edi, _pgcPageFaults                                     ;d73114
        pop     esi                      ; restore or reset ESI         ;d73114
        jnz     prb05                                                   ;d73114
        pop     edi                                                     ;d73114
        sub     sp, TMP_STORAGE         ; 81117 - adjust for pops
        xor     ax,ax                   ; Clear carry
        jmp     short prbx


;       Its a 80386. Do it the hard way.
;       CX:SI is start of buffer
;       CX:BX is end of buffer
;       DS,ES FlatContext

prb20:
    CpuMode 386
        push    es
        EnterProc ,,alignesp
        push    edx
        FlatContext <es>,ax
;       mov     es,ax                                                          ;B724492
        mov     eax,TK_UM_READ+TK_UM_SELPROBE   ; Assume reading from user memory
        or      dh,dh                   ; Check write access?
        jz      short prb30             ;  no, check read access
        mov     eax,TK_UM_WRITE+TK_UM_SELPROBE  ; Check write access
prb30:  PushP   eax                     ; Flags for _TKProbeBuff
        inc     ebx                     ; Past last byte in buffer
        movzx   eax,si                  ; (EAX) = offset
        sub     ebx,eax                 ; (EBX) = TRUE length
        PushP   ebx                     ; Size for _TKProbeBuff
        CCallNest                       ; Push parameters for _SELVirtToLin:
        movzx   edx,cx                  ; (EDX) = selector
        PushP   edx                     ; Selector for _SELVirtToLin
        PushP   eax                     ; Offset for _SELVirtToLin
        CCALL32 _SELVirtToLin,,,PushP   ; (EAX) = linear address (or -1)
        PushP   eax                     ; Linear address for _TKProbeBuff
        CCALL32 _TKProbeBuff,,,PushP    ; Probe the buffer
        pop     edx
        LeaveProc                       ; Restore ESP
        pop     es
    ASSUME      ES:NOTHING
        or      eax,eax                 ; Failure?
        jnz     short TmpTrapExit       ;  yes, go handle it
    CpuMode 286

prbx:   add     sp, TMP_STORAGE         ;; 81117 - give it back
        ClrTrapExit
        clc                             ; Success
        ret

EndProc ProbeBuff

;; 81117 - reset stack before falling thru to TrapExit....
TmpTrapExit:
        add     sp, TMP_STORAGE
;; 81117 - Fall through !!!

;***    TrapExit   - possibly mark process to die, return with error
;***    TrapExitNF - return with error indication only
;
;       TrapExit is the GP fault handler for the "trusted" su/fu
;       routines, which reside in setfetum.asm.
;
;       If execution comes here, it means a protection violation was
;       encountered or an interrupt was taken during a not present fault.
;       If not interrupted, mark all the threads to die soon.  Clear
;       the Direction Flag, set the Carry Flag to indicate failure,
;       and return to caller of the transfer routine.
;
;       (This amounts to a common error exit for all of the su/fu routines)
;
;       This relies HIGHLY on TrapExit being in the same segment as
;       the su/fu routines or what every code segment the GP fault
;       happen in. It also relies on the stack being the same as while
;       in the su/fu routine, that is a fault can not happen in a
;       subroutines that the su/fu routines calls. All the su/fu routines
;       that use TrapExit must exit with a near ret. FARONLY routines won't
;       work.
;
;       TrapExitNF just returns an error code and carry, but does NOT
;       kill the process.
;
;       ENTRY:  (AX) = ERROR_PROTECTION_VIOLATION
;                      ERROR_INTERRUPT
;
;       EXIT:   Carry set
;               (AX) = ERROR_PROTECTION_VIOLATION
;                      ERROR_INTERRUPT
;               (TCBGPFErrCode) = bad selector, if (AX) = ERROR_INTERRUPT
;
;       USES:   AX, DS, ES, Flags
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure TrapExit,near,local
        cmp     ax,ERROR_INTERRUPT
        je      tex10                   ; do not kill interrupted process

        cld
        pusha
        mov     bp,ss:[TKOPTDA].CurrTSD
        mov     ax,[bp].TSDselFault             ; (AX) = bad selector
        mov     word ptr [bp].TSDerrcFault,ax
        CALLFAR UsrArgFault                     ; do fault processing
        popa

Entry   TrapExitNF,,LOCAL                       ; nonfatal entry.

tex10:  ClrTrapExit                     ; restore previous fault handler
        cld
        stc
        ret
EndProc TrapExit

HIGH2CODE ENDS
CpuMode 386
        FARCODE HIGH32
HIGH32CODE      SEGMENT

;*      copy_386 - copy data
;
;       ENTRY:  (ds:esi) = source pointer
;               (es:edi) = destination pointer
;               (cx)    = byte count
;
;       EXIT:   (ds:esi) = ptr to one past last byte copied in source
;               (es:edi) = ptr to one past last byte copied in dest
;               (cx) = 0

COPY_386 MACRO   skip
        local   cpy1,cpy2,cpy3,cpy4

ALIGN 4
    ifb <skip>
        jecxz   short cpy4      ; zero count, done
        cmp     ecx,16          ; don't bother optimizing short copies
        jbe     short cpy3
    endif
        test    esi,1
        jz      short cpy1      ; srce is atleast word aligned
        test    edi,1
        jz      short cpy1      ; dest is atleast word aligned

        movsb                   ; both are byte aligned, copy one byte to
                                ; make both even aligned
        dec     ecx             ; and adjust the count
cpy1:
        test    esi,3
        jz      short cpy2      ; srce is dword aligned
        test    edi,3
        jz      short cpy2      ; dest is dword aligned

        movsw                   ; even aligned, make dword aligned
        sub     ecx,2
cpy2:
        mov     ebx,ecx
        and     ebx,3           ; ebx has residual count
        shr     ecx,2           ; dword count
ALIGN 4
        rep     movsd           ; mov cx dwords
        mov     ecx,ebx         ; ebx has residual count
cpy3:   rep     movsb           ; copy the residue
cpy4:
ENDM


;***LP  _SuBuff386
;
;
;       ENTRY
;
;       EXIT
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
ALIGN 4
Procedure _SuBuff386,near

        ArgVar count,DWORD
        ArgVar usrIOoffset,DWORD
        ArgVar source,DWORD
        LocalVar lj,<SIZE ljmp_s>

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     edi,_pTCBCur
        mov     bx,[edi].TCBDMAAdd.Segmt
        mov     edi,usrIOoffset
        mov     esi,source
        mov     ecx,count
        SaveReg <ecx>
        SSToDS  eax,lj                  ; Install Fault handler
        CCALL32 _TKCatchFault,<eax>
        RestoreReg <ecx>
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short s386_1            ; If we faulted, just return
        mov     es,bx                   ; (ES) = user's buffer selector
        jmp     SuFuBuff386
s386_1: RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _SuBuff386

;***LP  _FuBuff386
;
;
;       ENTRY
;
;       EXIT
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
ALIGN 4
Procedure _FuBuff386,near

        ArgVar count,DWORD
        ArgVar usrIOoffset,DWORD
        ArgVar dest,DWORD
        LocalVar lj,<SIZE ljmp_s>

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     edi,_pTCBCur
        mov     bx,[edi].TCBDMAAdd.Segmt
        mov     esi,usrIOoffset
        mov     edi,dest
        mov     ecx,count
        SaveReg <ecx>
        SSToDS  eax,lj                  ; Install Fault handler
        CCALL32 _TKCatchFault,<eax>
        RestoreReg <ecx>
        or      eax,eax
        .errnz  NO_ERROR
        jnz     sfb4                    ; If we faulted, just return
        mov     ds,bx                   ; (DS) = user's buffer selector
Entry   SuFuBuff386,,local              ; entry from SuBuff

        cmp     ecx,PAGESIZE            ; need to split copy ?
        jbe     sfb3                    ; no, do it in one shot
        mov     edx,ecx
        and     edx,PAGESIZE-1          ; EDX - residual count
        mov     eax,ecx
        shr     eax,PAGESHIFT           ; EAX - count of pages to copy
sfb1:   mov     ecx,PAGESIZE
        COPY_386 skip
        SaveReg <ds>
        DosContext ds
        cmp     TCResched,0             ; do we have to yield ?
        RestoreReg <ds>
ASSUME DS:NOTHING
        jz      short sfb2
        SaveReg <eax,edx,ds,es>         ; save count of pages and residual
        FlatContext <ds,es>,ax
        CCALL32 _TKTCYield              ; yield to time critical threads
        RestoreReg <es,ds,edx,eax>      ; (EAX) restore count of pages
sfb2:   dec     ax
        jnz     sfb1                    ; more pages to copy
        mov     ecx,edx
sfb3:   COPY_386                        ; copy residual

;       If we get here, operation worked

        FlatContext <ds,es>,ax
        CCALL32 _TKClrFault             ; Clear fault handler
        xor     eax,eax                 ; clear clear
sfb4:   RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FuBuff386

HIGH32CODE      ENDS

        END
