;       SCCSID = @(#)tkv1api.asm        6.6 91/11/13
SCCSID  equ     "@(#)tkv1api.asm        6.6 91/11/13"
TITLE   TKV1API - worker procedures for version 1.1 API calls
PAGE ,132

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;   Applications Program Interface extensions for OS/2 Version 1.1
;
;       w_GetPPID
;       w_CALLBACK
;       w_RETFORWARD
;       w_R2STACKREALLOC
;       DOSICALLBACK
;       DOSIRETFORWARD
;
;   Modification History:
;
;       sb  12-Jul-1989 Moved Tasking Api's from v1wrk.asm to this file
;                       and modified DosIcallBack and DosRetForward to
;                       to use new Enter/Exit kmode interfaces.
;       mlm 18-Nov-95   Defect 72417, moved find.inc after tkexport.inc

INCL_ERROR_H    EQU     1       ; for bseerr.inc
INCL_ERROR2_H   EQU     1       ; for bseerr.inc
.xlist
include fnotask.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include dossymc.inc
include mi.inc
include basemid.inc
include ordinals.inc
include kmode.inc
include sysentry.inc
include protmode.inc
include seldesc.inc
include bseerr.inc
include ostypes.inc
include osconst.inc
include sigdef.inc
include except.inc
include infoseg.inc
include pgexport.inc
include vmowner.inc
include vmexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include screen.inc
include sem.inc
include tkexport.inc
include find.inc
include tkptda.inc
include dbgex.inc
include mmpha.inc
.list
        CpuMode 386
        ModName tkv1api,FNO_TKV1API_ASM

        ReferGlobal     TSS,BYTE

; These are all the external subroutines needed by this source file.

        EXTRN           w_ReallocSeg:near       ; w_r2stackrealloc
        EXTRN           UsrArgFault:near
        EXTRN           HandleGetSelector:near
        EXTRNFAR        ExitCritSecUnHoldSig

        EXTRN32         _TKPidToPTDA            ; w_getppid

            FARCODE HIGH3
HIGH3CODE    SEGMENT

;***LP  w_GetPid - Return process ID
;
;       Return process ID into the pointer passed in.
;
;       ENTRY   none
;
;       EXIT    (AX) = PID
;               (BX) = TID
;               (CX) = PPID
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_GetPid,faronly
        mov     ax,ss:[TKOPTDA].ptda_Pid        ; (AX) = process ID
        mov     bx,ss:[TKOPTDA].CurrTCB         ; (BX) = thread ID (TCBOrdinal field)
        mov     bx,ss:[bx].TCBOrdinal
        mov     cx,ss:[TKOPTDA].ptda_Ppid       ; (CX) = parent process ID
        clc                                     ; make sure no error
        ret
EndProc w_GetPid


;***LP  w_GetPPID - Get parent process ID
;
;       This routine returns the parent process ID of a process.
;
;       Entry:
;               AX = ProcessID
;               0  => the requestor's process is assummed
;
;       Exit-Normal:
;               AX = 0
;               DX = parent PID
;               0 => no parent
;
;       Exit-Error:
;               AX = Error Code
;               ERROR_INVALID_PROCID
;
;       USES:   EAX, ECX, EDX, DS, ES, EFLAGS
;
;       CALLS: _TKPidToPTDA
;
;       Pseudo Code:
;           Procedure w GetPPID(PID;Status,PPID)
;           Handle the magic number (0 means use current ptda)
;           Map PID to PTDA pointer (call _TKPidToPTDA)
;           If PID is not valid
;               Set return code to ERROR_INVALID_PROCID
;           Else
;               Get the parent pid
;           Endif
;           Return
;

    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
Procedure w_GetPPID,faronly
LocalVar pptda,DWORD

        EnterProc
        TaskTime

        mov     dx,ss:[TKOPTDA].ptda_Ppid       ; Assume parent of current.
        cmp     ax,ss:[TKOPTDA].ptda_Pid        ; Query current process?
        je      short gppid1                    ; yes; done

        or      ax,ax                           ; Query current process?
        jz      short gppid2                    ; yes; done

        FlatContext <ds,es>,dx
        movzx   eax,ax
        SSToDS  edx,pptda
        CCALL32 _TKPidToPTDA,<eax,edx>
        or      eax,eax
        .errnz  NO_ERROR
        stc
        jnz     short gppid2

        mov     eax,pptda
        mov     dx,[eax].ptda_Ppid              ; get the parent PID
gppid1: xor     eax,eax
        .errnz  NO_ERROR
gppid2: LeaveProc
        RETC
EndProc w_GetPPID


;***LP  w_GetEnv - Get address of process' environment string
;
;       Return handle of process' environment string and start of
;       command line.
;
;       ENTRY   none
;
;       EXIT    Carry Clear
;                       (AX) = Handle to envirnment segment
;                       (BX) = Command offset
;               Carry Set
;                       (AX) = error, no environment
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_GetEnv,faronly
        MMPHPreDOSGetEnv                ; Performance Hook (14/06)
        mov     si,ss:[TKOPTDA].ptda_environ    ; (SI) = handle to environment
        or      si,si
        jz      wge
        call    HandleGetSelector               ; (AX) = environment selector
        mov     bx,ss:[TKOPTDA.ptda_infoseg].LIS_BX; (BX) = command line offset
        jnc     wgx                             ; no handle for environment
wge:    mov     ax,ERROR_INVALID_ACCESS
        stc
wgx:    MMPHPostDOSGetEnv               ; Performance Hook (14/86)
        ret                             ; (BX) = Command offset
EndProc w_GetEnv



;***EP  w_R2StackRealloc - Grow current thread's Ring 2 stack
;
;       This function is currently called as the worker for the
;       DOSR2STACKREALLOC api call.  It is passed a size in bytes,
;       and will grow the thread's Ring 2 stack (if there is one)
;       to that size.  Note that shrinking the stack is not allowed
;       and will return an error.
;
;       ENTRY   (BX) = new size in bytes (0 implies 64K)
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;                       ERROR_DYNLINK_FROM_INVALID_RING
;                       ERROR_IOPL_NOT_ENABLED
;                       ERROR_RING2_STACK_IN_USE
;                       ERROR_NOT_ENOUGH_MEMORY
;       USES  EAX, ECX, EDX, ESI, EDI, EBP, DS, ES, Flags
;
;       Note use of BP register

Procedure w_R2StackRealloc,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        mov     bp,ss:[TKOPTDA].CurrTCB ; [BP] = address of current TCB
        mov     si,ss:[TKOPTDA].CurrTSD ; [SI] = address of current TSD
        mov     dx,ss:[si].TSDUserSS    ; (dx) = user SS

;       Make sure we're being called from Ring 3

        and     dx,RPL_MASK             ; get the RPL
        cmp     dx,RPL_RING3            ; Is ring 3?
        je      short rsr10             ; Yes. Jump
        mov     ax,ERROR_DYNLINK_FROM_INVALID_RING
        jmp     short rsrerr            ; error

;       Get the initial ring 2 SS
;       Verify IOPL is enabled

rsr10:  mov     ax,[bp].TCBCpl2_SS      ; (AX) = ring 2 SS
        or      ax,ax                   ; Is there a Ring 2 stack?
        jnz     short rsr20             ; jump if so
        mov     ax,ERROR_IOPL_NOT_ENABLED
        jmp     short rsrerr

;       Get the initial ring 2 stack size

rsr20:  LOADSL  dx,ax
        inc     dx                      ; (DX) = ring 2 stack size
        jz      short rsr30             ; jump if stack is 64K

;       Make sure Ring 2 stack isn't in use

        mov     cx,[bp].TCBCpl2_SP      ; (CX) = ring 2 SP
        cmp     cx,dx                   ; is the stack empty?
        je      short rsr40             ; yes, continue
        mov     ax,ERROR_RING2_STACK_IN_USE
        jmp     short rsrerr

;       Stack is 64K.  If request is for 64K, return no error.
;       Else, fail immediately.

rsr30:  or      dx,bx
        jz      short rsrx              ; return no error
        jmp     short rsr50             ; return error, can't shrink

;       If the new size is equal to the ring 2 stack size,
;       return with no error

rsr40:  or      bx,bx                   ; is request for 64K?
        jz      short rsr60             ; yes, grow the stack

        mov     cx,ss:[si].TSDCpl2_SSSize

;       We know that:
;       DX >= CX
;       if BX > DX      Grow
;       if BX < CX      Error
;       Else CX <= BX <= DX - So update TSDCpl2_SSSize
        cmp     bx,dx                   ; is it a grow request?
        ja      short rsr60             ; yes, grow the segment
        je      short rsr70             ; OK-is the same size
        cmp     bx,cx
        jae     short rsr70


rsr50:  mov     ax,ERROR_CANNOT_SHRINK
        jmp     short rsrerr

;       Call w_ReallocSeg to allocate the specified size of the stack

rsr60:  SaveReg <bx,si>                 ; save new stack size and tsd pointer
        call    w_ReallocSeg            ; w_ReallocSeg(ax=sel,bx=new size)
        RestoreReg <si,bx>              ; get the new stack size
        jc      short rsrx              ; return error in AX if failed

;       Update the ring 2 SP value in TCB,TSD and TSS

        mov     [bp].TCBCpl2_SP,bx      ; (bx) = new ring 2 SP
        mov     ss:[si].TSDCpl2_SSSize,bx

        DosContext DS
        mov     [TSS].tss_sp2,bx
        jmp     short rsrx

;       We are here to check if the stack is being shrunk.

rsr70:  cmp     bx,cx
        jb      short rsr50             ; fail stack shrink
        mov     ss:[si].TSDCpl2_SSSize,bx
        jmp     short rsrx

rsrerr: stc
rsrx:   ret
EndProc w_R2StackRealloc


;************************** START OF SPECIFICATIONS *********************/
;*                                                                      */
;*      SUBROUTINE NAME: DOSICALLBACK                                   */
;*                                                                      */
;*     DESCRIPTIVE NAME: Transfer Control from ring 2 to ring 3         */
;*                                                                      */
;*     FUNCTION:  This routine is used to "call" from ring 2 to a       */
;*              ring 3 subroutine which returns to the ring 2 "caller". */
;*              This is effected by this routine, DOSICALLBACK, and 2   */
;*              other routines, DOSRETFORWARD (in this file) and        */
;*              mediator (in DOSCALL1).                                 */
;*                                                                      */
;*              Flow of control is shown below.                         */
;*                                                                      */
;*              : ring 3        : ring 2        : ring 0                */
;*              :               :               :                       */
;*              :               : caller        :                       */
;*              :               : push @subr    :                       */
;*              :               : DOSCALLBACK   :                       */
;*              :               :       |       :                       */
;*              :               : push @mediator:                       */
;*              :               : call >------------>DOSICALLBACK       */
;*              :               :               :       |               */
;*              :  subroutine <-------------------------<               */
;*              :       |       :  (retf)       :                       */
;*              : (ret) |       :               :                       */
;*              :       |       :               :                       */
;*              :   mediator >----------------------DOSRETFORWARD       */
;*              :               :  (call)       :       |               */
;*              :               :               :       |               */
;*              :               :  ring 2       :       |               */
;*              :               :  caller<--------------<               */
;*              :               :               : (retf)                */
;*              :               :               :                       */
;*                                                                      */
;*                                                                      */
;*                                                                      */
;*     ENTRY POINT:     DOSICALLBACK                                    */
;*        LINKAGE:      Call gate enters at this routine                */
;*                                                                      */
;*     INPUT:           The ring 2 stack, whose address is on the ring  */
;*                      0 stack at ss:sp+4, contains:                   */
;*                      Ring 3 mediator address at ss:sp                */
;*                      Ring 3 subroutine address at ss:sp+8            */
;*                                                                      */
;*              NOTE Call gate for this routine does not copy any       */
;*                    parameters to the ring 0 stack                    */
;*                                                                      */
;*     EXIT:                                                            */
;*              Control will pass to ring 3 subroutine, with registers  */
;*              unchanged. If a bad ring 3 address is passed, the user  */
;*              will fault at that address with an invalid cs:ip        */
;*                                                                      */
;*************************** END OF SPECIFICATIONS **********************/

;       Define the caller's ring 2 stack structure

ring2stack      STRUC
        Medip           dw      ?
        Medcs           dw      ?
        R2Retip         dw      ?
        R2Retcs         dw      ?
        Subrip          dw      ?
        Subrcs          dw      ?
ring2stack      ENDS

SysEntryBegin DOSICALLBACK,ORD_DOSICALLBACK,0,,,2,,,,noprepost   ;AA03898

assume ds:nothing, es:nothing, ss:nothing

        KMEnterKmodeCallGate 0,,PASCAL  ; Enter the kernel on call gate stack
                                        ; with no arguments es,ds are returned
                                        ; FLAT
        ifdef MMPHPreDOSICALLBACK                                ;AA03898
            MMPHPreDOSICALLBACK         ; Performance Hook       ;AA03898
        endif                                                    ;AA03898

;       Load si with TCBCpl2_SP (start point of ring 2 stack)

        mov     bx,ss:[TKOPTDA.CurrTCB]
        mov     si,ss:[bx].TCBCpl2_SP

;       Get user's ring 2 ss:sp from the ring 0 stack as saved by the
;       call gate, and load to ds:bx. We can safely load the user selector
;       since it must be valid and present since we just came in on it.

        mov     bx,ss:[TKOPTDA].CurrTSD
        mov     ds,ss:[bx].TSDUserSS            ; ring 2 ss
        assume  ds:nothing
        mov     dx,ss:[bx].TSDUserESP.loword    ; ring 2 sp

;       Set a trap handler and try and access the user's segments.
;       If that fails ( we take a trap) the process will be killed.

        SetTrap <offset cs:wcb3>,,bx

;       Overlay ring 2 ss:sp on ring 0 stack with ring 3 ss:sp from
;       ring 2 stack. This is because rather than returning to the ring 2
;       caller we are returning to the ring 3 subroutine, and we must have a
;       ring 3 stack to go with it.
;       (ss:sp 3 was saved on the ring 2 stack by the call gate when we first
;       entered ring 2. This is the first free address on the ring 3 stack).
;       Adjust the ring 3 sp to allow for mediator address that we are going
;       to add to the ring 3 stack.

        mov     ax,[si-2]               ; get ring 3 ss
        mov     ss:[bx].TSDUserSS,ax    ; overwrite ring 2 ss
        mov     di,[si-4]               ; get ring 3 sp
        sub     di,4                    ; make room on ring 3 stack
        mov     ss:[bx].TSDUserESP.loword,di    ; overwrite ring 2 sp

;       The saved ring 2 ss been overwritten with the ring 3 ss. Attempt
;       to load it into es. (It may fault)

        mov     es,ss:[bx].TSDUserSS    ; attempt load of ring 3 ss
        assume es:nothing

;       Get mediator address. This is first parameter on the ring 2 stack,
;       pushed by routine in DOSCALL1. (See diagram above)
;       Put address of Mediator onto ring 3 stack. This is done so that the
;       ring 3 subroutine will return to the mediator. If this address is
;       invalid for any reason, then we will fault when we try to return from
;       the user subroutine, but this will be blamed on the user.

        mov     bx,dx                   ; ring 2 esp

;       This is the ring 2 stack at this point

;       +---------+
;       | @Subr   | Pushed by caller
; bx+8->+---------+
;       | @Caller |
; bx+4->+---------+
;       |@Mediator| Pushed by DOSCALL1 routine
;ds:bx->+---------+

        mov     ax,[bx].Medip           ; mediator ip
        mov     es:[di].Offst,ax        ; to ring 3 stack
        mov     ax,[bx].Medcs           ; mediator cs
        or      al,RPL_RING3            ; stamp mediator CS ring 3
        mov     es:[di].Segmt,ax

;       Get ring 3 subroutine address from ring 2 stack. This is the address
;       pushed by the caller of DOSCALLBACK. (See diagram above). Overwrite
;       the ring 2 return address on the ring 0 stack so that when we return
;       at the end of this routine control will pass to the ring 3
;       subroutine.

        mov     di,ss:[TKOPTDA.CurrTCB]
        mov     edi,ss:[di].TCB_pFrameBase
        FlatContext <es>,ax
        movzx   eax,[bx].Subrip          ; get subroutine ip
        mov     es:[edi].sef_eip,eax
        mov     ax,[bx].Subrcs

;       We must adjust the passed subroutine selector to have an RPL of 3,
;       because if the user passes in a non ring 3 invalid selector,
;       (e.g. 0), then the kernel mode fault handler will incorrectly
;       interpret this as a kernel error.
;       The adjustment will not matter in the working case because the
;       selector must be ring 3 for the call to work anyway.

        or      al,RPL_RING3            ;force subroutine selector ring 3
        mov     es:[edi].sef_cs,ax      ;replace return selector

;       Get the caller address from the ring 2 stack and move it so that at
;       the return from DOSRETFORWARD control will return to the original
;       ring 2 caller. We are about to write back over the ring 2 stack.
;       If a fault happens any time after now then we will not be able
;       to retry the entire routine because the ring 2 stack has been
;       changed.

        mov     ax,[bx].R2Retip         ;overlay @Subr by @Caller
        mov     [bx].Subrip,ax
        mov     ax,[bx].R2Retcs
        mov     [bx].Subrcs,ax

;       Put TSS sp(2), si, onto ring 2 stack for use of DOSRETFORWARD

        mov     [bx].R2Retcs,si         ;save old TSS SP(2) value

;       We should take no more faults so clear the trap.

        ClrTrap

;       We have removed 2 dwords (the mediator address and ring3 subroutine
;       address) from the ring 2 stack, and we have put old value of
;       TSS sp(2) on the stack, so adjust stack pointer by the necessary 6
;       bytes to clear redundant parameters from the stack.

        add     bx,6                    ;adjust ring 2 sp

;       Save revised ring 2 sp (in bx) in TSS SP(2) and TCBCpl2_SP
;       The revised value is saved so that if the called ring 3
;       subroutine enters ring 2 again, it will not overwrite the
;       ring 2 stack that is in use, but will start at the appropriate point,
;       just after the ring 2 return cs:ip and old TSS sp (2)

        mov     si,ss:[TKOPTDA.CurrTCB]
        mov     ss:[si].TCBCpl2_SP,bx
        DosContext <ds>,ax
        mov     TSS.tss_sp2,bx

;       Restore user's registers for the ring 3 subroutine.

wcb2:   FlatContext     <ds,es>,si
        ifdef MMPHPostDOSICALLBACK                               ;AA03898
            MMPHPostDOSICALLBACK        ; Performance Hook       ;AA03898
        endif                                                    ;AA03898
        KMExitKmodeCallGate 0,noretval,PASCAL   ; Exit Kernel; No arguments


;   This is the trap handler. As we entered the kernel mode right in the
;   begining of this routine so it is either a GP fault or a NP fault. In
;   both case the user process will be killed with a popup.

assume ds:nothing, es:nothing, ss:nothing

;       Registers are as follows on entering this routine:
;
;       (AX) = error code

Entry wcb3,far
        ClrTrap
        cmp     ax,ERROR_INTERRUPT
        je      wcb2
        call    UsrArgFault         ; Put up a pop up
        jmp     wcb2

EndEntry DOSICALLBACK,NoGen

;************************** START OF SPECIFICATIONS *********************/
;*                                                                      */
;*      SUBROUTINE NAME: DOSRETFORWARD                                  */
;*                                                                      */
;*     DESCRIPTIVE NAME: Transfer Control from ring 3 to ring 2         */
;*                                                                      */
;*     FUNCTION: This routine is used with DOSICALLBACK (above), to     */
;*              allow a ring 2 routine to "call" a ring 3 routine. This */
;*              routine handles the "return" from ring 3 to the ring 2  */
;*              caller                                                  */
;*              See header for DOSICALLBACK for a fuller explanation.   */
;*                                                                      */
;*     ENTRY POINT:    DOSRETFORWARD                                    */
;*        LINKAGE:     Call gate enters at this routine                 */
;*                                                                      */
;*     INPUT:           Stacks are as shown in diagram below            */
;*                                                                      */
;*         +-------------+           +---------+                        */
;*         | @Caller     |           | ss:sp(3)|                        */
;*         +-------------+           +---------+                        */
;*         |old TSS SP(2)|           | cs:ip(3)|                        */
;* ss:sp-> +-------------+    ss:sp->+---------+                        */
;*                                                                      */
;* (ring 2)                  (ring 0)                                   */
;* (ring 2 ss:sp is in TCB)                                             */
;*                                                                      */
;*     EXIT:                                                            */
;*        Ring 2 stack contains the same data before the call to        */
;*         DOSICALLBACK                                                 */
;*        TCB ring 2 SP and TSS ring 2 SP are back to the orginal       */
;*            value                                                     */
;*                                                                      */
;*     NOTE Call gate for this routine does not copy any                */
;*                    parameters to the ring 0 stack                    */
;*                                                                      */
;*************************** END OF SPECIFICATIONS **********************/

;       This is the structure of the ring 2 stack pointed to by the
;       ring 2 ss:sp.

r2stack STRUC
        OldTSSsp2               dw      ?
        R2Callerip              dw      ?
        R2Callercs              dw      ?
r2stack ENDS

SysEntryBegin DOSRETFORWARD,ORD_DOSRETFORWARD,0,,,2,,,,noprepost ;AA03898

assume ds:nothing, es:nothing, ss:nothing

;       The TCB (and the TSS) contain the ss:sp of the next free space on the
;       ring 2 stack. Above this point DOSICALLBACK has left the old TSS sp 2
;       value (so that we can reset the TSS and TCB ring 2 sp value)
;       and the original ring 2 caller's return cs:ip.

        KMEnterKmodeCallGate 0,,PASCAL  ; Enter KMode as call gate stack
                                        ; es,ds  is returned as FLAT
        ifdef MMPHPreDOSRETFORWARD                               ;AA03898
            MMPHPreDOSRETFORWARD        ; Performance Hook       ;AA03898
        endif                                                    ;AA03898
;       Get ring 2 stack segment from the TCB.

        mov     di,ss:[TKOPTDA.CurrTCB]
        mov     dx,ss:[di].TCBCpl2_SS
        mov     bx,ss:[di].TCBCpl2_SP     ;get ring 2 sp
        mov     esi,ss:[di].TCB_pFrameBase

;       Replace the ring 3 ss with the original ring 2 ss. This is the ss
;       that will be loaded when we return from this routine to the
;       original ring 2 caller. Note that this segment may be invalid,
;       but if it is, then there's nothing we can do anyway, and we'll just
;       fault in the user space.

        mov     di,ss:[TKOPTDA].CurrTSD
        mov     ss:[di].TSDUserSS,dx      ;overwrite ring 3 ss

;       We may get a fault when we try and access the ring 2 stack,
;       (not present or GP fault) so set a trap handler in
;       case we are about to take a fault. This will cover the case where
;       accessing back up the stack is invalid because the user called
;       DOSRETFORWARD directly and there is nothing up the stack (i.e
;       accessing back up the stack would be outside the segment limit).
;       or any bad parameters from the user.

        SetTrap <offset cs:wrf3>,,di      ;set up local trap handler
        mov     ds,dx                     ;Attempt load of ring 2 ss
        assume ds:nothing

;       Replace the ring 3 return address on the ring 0 stack by the
;       ring 2 caller's return cs:ip. This sets up the call gate return
;       frame so we will return to the original ring 2 caller.

        movzx   eax,[bx].R2Callerip       ;get ring 2 ret ip
        mov     es:[esi].sef_eip,eax
        mov     ax,[bx].R2Callercs        ;get ring 2 return cs
        mov     es:[esi].sef_cs,ax        ;set up ring 2 ret segment
        mov     ax,[bx].OldTSSsp2

;       Finished accessing the ring 2 stack so we can clear the trap.

        ClrTrap di

;       Increment the ring 2 stack pointer past ring 2 return cs:ip and the
;       old TSS sp (2) now that we have read them.

        add     bx,6
        movzx   ebx,bx

;       Overwrite the ring 3 sp as saved by the call gate with the
;       adjusted ring 2 sp from the TCB. This completes the call gate return
;       frame with a ring 2 stack so that we can return to the original
;       ring 2 routine with the stack at the correct point.

        mov     ss:[di].TSDUserESP,ebx     ;overwrite ring 3 sp

;       Replace the original ring 2 sp value in the TSS and TCB.

        mov     di,ss:[TKOPTDA].CurrTCB
        mov     ss:[di].TCBCpl2_SP,ax
        DosContext <ds>,cx
        mov     TSS.tss_sp2,ax

;       Restore registers for the ring 2 routine

wrf2:   FlatContext <ds,es>,si
        ifdef MMPHPostDOSRETFORWARD                              ;AA03898
            MMPHPostDOSRETFORWARD       ; Performance Hook       ;AA03898
        endif                                                    ;AA03898
        KMExitKmodeCallGate 0,noretval,PASCAL

;   As we have entered the kernel mode right in the begining of this
;   so control will come here as NP fault or GP fault. In both cases
;   the user process will be killed with a popup.

assume ds:nothing, es:nothing, ss:nothing

Entry wrf3,far
        ClrTrap
        cmp     ax,ERROR_INTERRUPT
        je      wrf2
        call    UsrArgFault
        jmp     wrf2

EndEntry DOSRETFORWARD,NoGen

HIGH3CODE    ENDS

END
