;       SCCSID = @(#)tkumem2.asm	6.7 91/10/17
SCCSID  equ     "@(#)tkumem2.asm	6.7 91/10/17"
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Tasking - Accessing User Memory Part II - Low level routines
;
;       Created Jan 1989        Jameel Hyder

.xlist
INCL_ERRORS     EQU     1
include fnotask.inc
include osconst.inc
include bseerr.inc
include mi.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include infoseg.inc
include sigdef.inc
include except.inc
include dossymc.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include tkexport.inc
include ldrxport.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include sem.inc
include tkptda.inc
include bsetib.inc
include tklocal.inc
include protmode.inc
include kmode.inc                                                       ;b723248
.list

        CpuMode 386
        ModName tkumem2,FNO_TKUMEM2_ASM

        ReferGlobal     TCResched, BYTE
        ReferFlat       _pTSDCur,  DWORD
        ReferFlat       _pfntkSuBuff,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _CpuType,DWORD
        ReferFlat       _pgcPageFaults,DWORD                            ;b790505
        EXTRN32         _TKTCYield
        EXTRN32         _TKCatchFault
        EXTRN32         _TKClrFault
        EXTRN32         _PGVerifyPermission
        EXTRN32         KMQUERYCLIENTREGISTER                           ;b723248

COPY    MACRO   skip
        local   cpy1,cpy2,cpy3,cpy4

    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
        rep     movsd           ; mov cx dwords
        mov     ecx,ebx         ; ebx has residual count
cpy3:   rep     movsb           ; copy the residue
cpy4:
ENDM


        FARCODE HIGH32
_TEXT   SEGMENT

;***EP  TKCopyBuff - Copy Data
;
;       This routine actually attempts a copy.  It is called from
;       TKSuBuff and TKFuBuff and loader.
;
;       ENTRY:  TKCopyBuff(pMemDst, pMemSrc, Len);
;               pMemDst = address of destination buffer
;               pMemSrc = address of source buffer
;               len     = buffer length
;
;       EXIT    NONE
;
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _TKCopyBuff,near
ArgVar  pMemDst,DWORD                   ; destination offset
ArgVar  pMemSrc,DWORD                   ; source offset
ArgVar  Len,DWORD                       ; length of copy
        EnterProc
        mov     ecx,Len
        or      ecx,ecx                 ; too far for jecxz
        jz      tkcbe                   ; zero count, done
        SaveReg <ebx,esi,edi>
        mov     esi,pMemSrc             ; load source
        mov     edi,pMemDst             ; load destination
        cmp     ecx,PAGESIZE            ; need to split copy ?
        jbe     short tkcb3             ; 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
tkcb1:  mov     ecx,PAGESIZE
        COPY    skip
        cmp     TCResched,0             ; do we have to yield ?
        jz      short tkcb2
        SaveReg <eax,edx>               ; save count of pages and residual
        CCALL32 _TKTCYield              ; yield to time critical threads
        RestoreReg <edx,eax>            ; (EAX) restore count of pages
tkcb2:  dec     ax
        jnz     short tkcb1             ; more pages to copy
        mov     ecx,edx
tkcb3:  COPY                            ; copy residual
        RestoreReg <edi,esi,ebx>
tkcbe:  LeaveProc
        RETC
EndProc _TKCopyBuff


;***LP  tkScanBuffz - Scan a null-terminated (possibly two  nulls) user buffer
;
;       tkScanBuffz scans a user buffer stopping either after the
;       first NULL (2 NULLs) byte found, or after a specified number of bytes
;       has been scanned, whichever comes first.
;
;       ENTRY:  tkScanBuffz(pLen, pMem, MaxLength, fDNULL);
;               pLen  = pointer to where length of buffer is returned
;               pMem  = address of buffer
;               MaxLength = max. length to scan.
;               fDNULL    = if TRUE scan for a double NULL
;
;       EXIT    NO_ERROR, if sucessful - Length is copied to pLen
;               ERROR_TERMINATOR_NOT_FOUND

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _tkScanBuffz,near
ArgVar  pLen,DWORD                      ; pointer to where length is returned
ArgVar  pMem,DWORD                      ; pointer to buffer
ArgVar  MaxLength,DWORD                 ; max length
ArgVar  fDNULL,DWORD                    ; TRUE for double NULL scan
        EnterProc
        push    edi
        mov     edi,pMem                ; starting pointer
        mov     edx,MaxLength           ; Max. length to scan
        xor     eax,eax                 ; Scan for zero
sbz0:   cmp     edx,0                   ; Count expired ?
        je      short sbze              ; yes, nul not found
        mov     ecx,edx                 ; get count for a fresh scan
        cmp     ecx,PAGESIZE            ; exceeds PAGESIZE ?
        jbe     short sbz1
        mov     ecx,PAGESIZE            ; Max. length to scan in one go
sbz1:   sub     edx,ecx                 ; EDX has bytes remaining to scan
        repnz   scasb
        jnz     short sbz4              ; Jump if not found
        cmp     fDNULL,0                ; scan single NULL ?
        jz      short sbz2              ; yes, done
        dec     ecx                     ; account for another scasb
        scasb                           ; look for second null
        jz      short sbz2              ; found, done
        add     edx,ecx                 ; add the remaining count to balance
        jmp     short sbz4
sbz2:   mov     ecx,edi
        sub     ecx,pMem                ; ecx = length of buffer scan'd
        mov     edx,pLen
        mov     dword ptr [edx],ecx
        jmp     short sbz3
sbz4:   cmp     TCResched,0
        je      short sbz0
        SaveReg <edx>                   ; Save residual count
        CCALL32 _TKTCYield
        RestoreReg <edx>
        jmp     short sbz0
sbze:   mov     eax,ERROR_TERMINATOR_NOT_FOUND
sbz3:   pop     edi
        LeaveProc
        RETC
EndProc _tkScanBuffz


;***EP  TKSuBuff - Copy from Resident System Memory to User Memory
;
;       TkSuBuff performs a buffer transfer from resident system memory
;       to user memory as appropriate to the CPU being used.
;
;       ENTRY   pdstUsr         - destination buffer in user space
;               psrcRes         - source buffer in resident memory
;               cb              - byte count
;               fl              - flags
;                                 TK_UM_FATAL - Error is fatal to task.  A
;                                 GP fault is simulated and an error is
;                                 returned to the caller.
;
;       EXIT    NO_ERROR, If successful
;                   Buffer will have been written to user space
;               ERROR_PROTECTION_VIOLATION
;                   The specified user memory could not be written to or
;                   is not writable from user (ring 3) code via the
;                   specified virtual addresses.
;
;       Control transfers to _pfntkSuBuff.

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _TKSuBuff,near
        jmp     dword ptr _pfntkSuBuff
EndProc _TKSuBuff


;***EP  TKSuByte - Set user byte
;***EP  TKSuWord - Set user word
;***EP  TKSuDWord - Set user dword
;***EP  TKSuByteNF - Set user byte (non-fatal to task)
;***EP  TKSuWordNF - Set user word (non-fatal to task)
;***EP  TKSuDWordNF - Set user dword (non-fatal to task)
;
;       Performs data transfer from resident data to user data watching
;       for protection violations.  A protection violation is fatal to the
;       calling task for non-NF versions.
;
;       Protection violations on the source data access are treated as
;       an internal error, since the source data is resident and should be
;       readable.  Protection violations on the destination data access
;       are treated as user errors.  An error code is returned to the caller,
;       all threads of the current task are marked to terminate, and a hard
;       error popup is generated.
;
;       ENTRY   data    data to write to user buffer
;                       BYTE to TKSuByte[NF]
;                       WORD to TKSuWord[NF]
;                       DWORD to TKSuDWORD[NF]
;               pMemUsr pointer to user data
;
;       EXIT    NO_ERROR, If successful
;                   Data will have been written to user space
;               ERROR_PROTECTION_VIOLATION
;                   The specified user memory could not be written to or
;                   is not writable from user (ring 3) code via the
;                   specified virtual addresses.

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _TKSuByte,near
ArgVar  data,DWORD
ArgVar  pMemUsr,DWORD
        enter   ?frame,0
        mov     ecx,1                   ; Count of bytes
        mov     eax,TK_UM_FATAL         ; fatal
        jmp     short tks10
Entry   _TKSuByteNF
        enter   ?frame,0
        xor     eax,eax                 ; non-fatal
        mov     ecx,1                   ; Count of bytes
        jmp     short tks10
Entry   _TKSuWord
        enter   ?frame,0
        mov     ecx,2                   ; Count of bytes
        mov     eax,TK_UM_FATAL         ; fatal
        jmp     short tks10
Entry   _TKSuWordNF
        enter   ?frame,0
        mov     ecx,2                   ; Count of bytes
        xor     eax,eax                 ; non-fatal
        jmp     short tks10
Entry   _TKSuDWord
        enter   ?frame,0
        mov     ecx,4                   ; Count of bytes
        mov     eax,TK_UM_FATAL         ; fatal
        jmp     short tks10
Entry   _TKSuDWordNF
        enter   ?frame,0
        mov     ecx,4                   ; Count of bytes
        xor     eax,eax                 ; non-fatal
tks10:  SSToDS  edx,[data]              ; [EDX] = pointer to data to be copied
        CCALL32 <[_pfntkSuBuff]>,<pMemUsr,edx,ecx,eax>
        leave
        RETC
EndProc _TKSuByte


;***EP  TKProbeBuff - Probe I/O buffer for access validity
;
;       TKProbeBuff determines validity of the address range of the an I/O
;       buffer by verifying that each page is accessible from user code.
;
;               pUserBuff User buffer to be probed
;               Length    length of buffer in bytes
;               Flags     Access and fail option
;                 TK_UM_READ  - Read access to be checked
;                 TK_UM_WRITE - Read/Write access to be checked.
;                 TK_UM_FATAL - Error is fatal to task.
;                 TK_UM_SELPROBE - Selector-based probe (caller is
;                       ProbeBuff in setfetum.asm); causes PG_U to be
;                       suppressed.
;
;       EXIT    NO_ERROR, If successful
;               ERROR_PROTECTION_VIOLATION
;                   The specified user memory is not readable or writable
;                   from user level (ring 3) code.
;
;

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


Procedure _TKProbeBuff,near
ArgVar  pMemUsr,DWORD
ArgVar  cchlen,DWORD
ArgVar  Flags,DWORD
LocalVar faultbuf,<CBOBJNAMEBUF+CBERRCODEBUF>
LocalVar lj,<SIZE ljmp_s>
        EnterProc
        xor     eax,eax
        .errnz  NO_ERROR
        cmp     [cchlen],0
        je      tkpbx

        SaveReg <ebx>                                                   ;b790505

        SSToDS  eax,faultbuf                 ; Initialize TCB_FaultBuf[L]
        mov     edx,[_pTCBCur]
        mov     [edx].TCB_FaultBuf,eax
        mov     [edx].TCB_FaultBufL,CBOBJNAMEBUF

        mov     ecx,[pMemUsr]           ; (ECX) = pMemUsr
        mov     edx,ecx                 ; (EDX) = pMemUsr
        add     edx,[cchlen]
        dec     edx                     ; (EDX) = pMemUsr + Length - 1

;       If CPUType is CPU_386 *and* it is a write operation (TK_UM_WRITE)
;       then call PGVerifyPermission. Else just touch the buffer

        cmp     [_CpuType],CPU_486      ; Is it a 80486 ?
        jge     short tkpb10            ; OK to just test and probe
        test    [Flags],TK_UM_WRITE     ; Is it a write operation ?
        jnz     short tkpb40            ; Yes, call PGVerifyPermission

tkpb10: SaveReg <ecx,edx>
        SSToDS  eax,lj                  ; Install Fault handler
        CCALL32 _TKCatchFault,<eax>
        RestoreReg <edx,ecx>
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short tkpbx1            ; If we faulted, just return    ;b790505

;       Record the running count of page faults.  If this value         ;b790505
;       changes  while executing the following loop, then retart.       ;b790505
;       This will ensure that all pages in the range are present        ;b790505
;       at the completion of this routine.                              ;b790505
tkpb15: mov     ebx, _pgcPageFaults                                     ;b790505
        mov     ecx,[pMemUsr]           ; (ECX) = pMemUsr               ;b790505

;       Read a byte from each page and force it present

tkpb20: mov     al,byte ptr [ecx]       ; Read byte
        test    [Flags],TK_UM_WRITE     ; Test writability
        jz      short tkpb30            ; No, done
        mov     byte ptr [ecx],al       ; Write the byte just read
tkpb30: add     ecx,PAGESIZE
        and     ecx,NOT PAGEMASK        ; (ECX) -> beginning of next page
        cmp     ecx,edx                 ; Does it lie within the buffer ?
        jbe     short tkpb20                    ; Yes, touch that too

        cmp     ebx, _pgcPageFaults                                     ;b790505
        jnz     tkpb15                                                  ;b790505


        CCALL32 _TKClrFault             ; Clear fault handler
        xor     eax,eax
        .errnz  NO_ERROR
;       mov     ecx,[_pTCBCur]         ;;; AZ08327 - do not do this here - it
;       mov     [ecx].TCB_FaultBufL,ax ;;; has to be done for every way out
        jmp     short tkpbx1                                            ;b790505

;       Its a write operation on a 386.
;       ECX = pMemUsr
;       EDX = pMemUsr + length - 1

tkpb40: mov     ebx,[_pPTDACur]         ; (EBX) = pPTDACur
        mov     eax,PG_FORCEPRESENT + PG_R + PG_W
        test    [Flags],TK_UM_SELPROBE
        jnz     short tkpb50
        or      eax,PG_U
tkpb50: shr     ecx,PAGESHIFT           ; (ECX) = pMemUsr >> PAGESHIFT
        shr     edx,PAGESHIFT           ; (EDX) = (pMemUsr + length - 1) >> PAGESHIFT
        sub     edx,ecx                 ; (EDX) = (# of pages)-1 to verify  ;b726043
        inc     edx                     ; (EDX) = # of pages to verify      ;b726043
        CCALL32 _PGVerifyPermission,<ecx, ebx, edx, eax>

tkpbx1:
        ; AZ08327 - need to reset the fault buffer pointers.
        xor     ebx,ebx
        mov     edx,[_pTCBCur]
        mov     [edx].TCB_FaultBuf,ebx
        mov     [edx].TCB_FaultBufL,bx

        RestoreReg <ebx>                                                ;b790505

tkpbx:  LeaveProc
        RETC
EndProc _TKProbeBuff

;***LP  tkBufferWithinUserDSLimit                                       ;b723248
;*                                                                      ;b723248
;*      This procedure returns error if the end of the passed buffer    ;b723248
;*      is not within the user's DS selector limit.                     ;b723248
;*                                                                      ;b723248
;*                                                                      ;b723248
;*      ENTRY   Buffer          - base of the user buffer               ;b723248
;*              BufSize         - size of the user buffer               ;b723248
;*                                                                      ;b723248
;*      EXIT    NO_ERROR, If successful                                 ;b723248
;*              ERROR_PROTECTION_VIOLATION, otherwise                   ;b723248
;*                                                                      ;b723248
;*      CALLS   KMQUERYCLIENTREGISTER                                   ;b723248
;*                                                                      ;b723248
;*/                                                                     ;b723248
Procedure _tkBufferWithinUserDSLimit,near                               ;b723248
ArgVar   Buffer,     DWORD
ArgVar   BufSize,    DWORD
LocalVar userDS,     DWORD

        EnterProc

        ;Get the user's DS selector value.
        SSToDS  eax,[userDS]
        PCALL32 KMQUERYCLIENTREGISTER, <_pTCBCur, KM_REG_DS, eax>

        mov     eax, ERROR_PROTECTION_VIOLATION    ; assume failure

        ;Find the limit for this selector.
        lsl     edx, userDS
        jnz     bwdl_err
                                ; edx=offset of last addressable byte in seg.

        ;Find the offset to the last byte of the buffer.
        mov     ecx, Buffer
        add     ecx, BufSize
        jc      bwdl_err
        dec     ecx             ; ecx=offset of last byte in buffer

        ;Is the buffer within the user's DS limit?
        cmp     ecx, edx
        ja      bwdl_err
        xor     eax, eax
bwdl_err:
        LeaveProc
        RETC

EndProc _tkBufferWithinUserDSLimit

_TEXT   ENDS
        END
