summaryrefslogtreecommitdiffstats
path: root/private/os2/client/thunk/r2xfer.asm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/os2/client/thunk/r2xfer.asm555
1 files changed, 555 insertions, 0 deletions
diff --git a/private/os2/client/thunk/r2xfer.asm b/private/os2/client/thunk/r2xfer.asm
new file mode 100644
index 000000000..7415f4ca4
--- /dev/null
+++ b/private/os2/client/thunk/r2xfer.asm
@@ -0,0 +1,555 @@
+ TITLE r2xfer.asm
+
+ .386P
+
+ OPTION SEGMENT:USE16
+ .MODEL LARGE
+
+
+COMMENT $
+
+This file contains code and data that handles the case where a R3 code
+contains a call to a R2 entry point.
+
+In OS/2, only the selector part (call gate) of the call destination is
+meaningful.
+The OS/2 SS replaces the original call with a call to a jump table
+entry in the R2XFER code segment (which is the second code segment of
+DOSCALLS.DLL).
+
+The R2XFER code segment contains code that emulates the stack changing
+when transferring protection rings. The size of this code is limited to a
+predefined size since it is followed by other data structures which are
+set at run time by the loader. The limit is currently set to 0x200
+(which can be changed anytime later if it is found that 0x200 is not
+sufficient to handle the ring transfer emulation code).
+
+The code generated by this file is followed in memory by an array of Ring 2
+info which is prepared by the OS/2 SS loader.
+The array is variable length and each entry contains the following
+information:
+
+struct _R2CallInfo {
+ UCHAR R2CallNearInst; // Opcode of the call relative near inst (0xE8)
+ USHORT R2CommonEntry; // This field will contain the relative offset to 0H
+ UCHAR R2BytesToCopy; // Total size of proc parameters to copy between stacks
+ USHORT R2EntryPointOff; // Offset of entry address of R2 routine
+ USHORT R2EntryPointSel; // Selector of entry address of R2 routine
+} R2CallInfo;
+
+When a ring 3 routine calls a ring 2 routine (which the OS/2 SS loader
+changes to a call to the appropriate R2CallInfo entry, which does another
+call to the CommonR2Xfer routine) the R3 stack looks upon entry to
+CommonR2Xfer:
+
+param1
+param2
+param3
+return-addr-r3 CS
+ IP
+return-addr-r2 IP <- SP
+
+We want to start the R2 code with an R2 stack in the following format:
+
+ <- TOS
+<16 bytes free> These free bytes are a local work-around for the PMNTPrint()
+ accesses to the stack.
+Old SS
+Old SP
+0 These 2 zeroes are used to catch (by trap) any access
+0 of the ring 2 code to the ring 3 stack
+param1
+param2
+param3
+return-addr-r3 CS
+ IP <- SS:SP
+
+This file also defines a data segment that is used to hold information of the
+OS/2 SS ring 2 stacks. This data segment is the 3'rd segment of DOSCALLS.DLL
+The data is organized as an array with each entry representing the ring 2
+stack of the appropriate thread.
+
+Each entry in the array has the following format:
+
+struct _R2Stacks {
+ USHORT R2StackSize;
+ SEL R2StackSel;
+} R2Stacks[256];
+
+Entry 0 is not used since there is no thread 0.
+OS/2 supports 255 threads so this array contains enough entries.
+An entry of the array is initialized when the CommonR2Xfer routine
+is called by an OS/2 thread for the first time, with same ThreadID as
+the index of the enrty.
+OS/2 allocates 512 bytes as the initial size of the R2 stack and so
+do we.
+
+$
+
+R2Stacks STRUCT 1
+R2InitSP WORD 512 ; 0:512 indicates that no R2 stack exists
+R2StackSel WORD 0
+R2Stacks ENDS
+
+R2CallInfo STRUCT 1
+R2bytesToCopy BYTE ?
+R2EntryPoint DWORD ?
+R2CallInfo ENDS
+
+EXTERN DosAllocSeg:FAR
+
+ .CODE
+ PUBLIC CommonR2Xfer
+CommonR2Xfer PROC FAR
+
+; Push all registered involved in the emulation. The order of the push
+; was selected to allow easy exchange of stacks.
+
+ push BP
+ mov BP,SP
+ push AX
+ push ES
+ push DI
+ push SI
+ push BX
+ push CX
+ push DX
+
+; The stack of the calling process is now:
+;
+; param1 SP+26 BP+12
+; param2 SP+24 BP+10
+; param3 SP+22 BP+8
+; return-addr-r3 CS SP+20 BP+6
+; IP SP+18 BP+4
+; return-addr-r2 IP SP+16 BP+2
+; BP SP+14 BP
+; AX SP+12 BP-2
+; ES SP+10 BP-4
+; DI SP+8 BP-6
+; SI SP+6 BP-8
+; BX SP+4 BP-10
+; CX SP+2 BP-12
+; DX SP BP-14
+
+ mov BX,FS:6 ; Get current thread ID. FS points to the LINFOSEG
+ shl BX,2 ;
+ add BX,2 ; BX points now to SEL part of the R2 TOS (Top Of Stack)
+ mov AX,SEG R2StacksInfo
+ mov ES,AX ;
+ mov AX,ES:[BX] ; AX contains the SEL of the R2 TOS
+ or AX,AX ; Verify that the thread has a R2 stack
+ jnz R2CheckIfCalledFromR2; If yes, skip creation of R2 Stack
+ ;
+ ; Create R2 Stack
+ ;
+ push 512 ; Allocate stack with size of 512 bytes (same as OS/2)
+ push ES ; ES:BX contain the address of the Stack entry
+ push BX ;
+ push 0 ; Flags for SEG_NONSHARED
+ call DosAllocSeg ; Allocate a R2 stack segment
+ or AX,AX ; Check if the allocation succeeded
+ jz R2StackExists ; Yes.
+ pop DX ; No. What to do? Restore all registers and return...
+ pop CX ;
+ pop BX ;
+ pop SI ;
+ pop DI ;
+ pop ES ;
+ pop AX ;
+ pop BP ;
+ add SP,2 ; Ignore the return to the R2CallInfo entry
+ retf ; Just return...
+ ;
+ ; Check if we call a ring 2 function from within ring 2.
+ ; If yes, no need to change stacks.
+ ; AX contains the ring 2 stack selector
+ ;
+R2CheckIfCalledFromR2:
+ mov CX,SS ; Get current Stack Segment
+ cmp CX,AX ; Compare with the R2 SS
+ jne R2StackExists ; Not the same. Change stacks.
+ mov SI,[BP+2] ; SI contains a pointer to the R2BytesToCopy field
+ ; in the call table entry
+ mov EAX,CS:[SI].R2CallInfo.R2EntryPoint ; EAX contains the Address of the R2 destination
+ pop DX ; Restore registers.
+ pop CX ;
+ pop BX ;
+ pop SI ;
+ pop DI ;
+ pop ES ;
+ push word ptr [BP] ; The old BP is going to be overwritten...
+ mov [BP],EAX ; Old BP and return-addr-r2 are overwritten.
+ pop BP ; Retsore Old BP from Stack
+ pop AX ;
+ retf ; return to the R2 code
+ ;
+ ; A R2 stack exists or was created
+ ;
+R2StackExists:
+ ;
+ ; The calls to DosXXX preserve all registers except AX,
+ ; so BX was not changed and we can use it
+ ;
+ les DI,ES:[BX-2] ; ES:DI points to the top of the R2 stack
+ sub DI,4+4+16 ; allocate place for old SS:SP, 2 zeroes and 16 free bytes
+ mov ES:[DI+6],SS ; Save old SS
+ mov BX,BP ;
+ add BX,4 ; 8-4 (the 4 in the R3 stack is used to move there
+ ; the return address to the function in R3 that
+ ; called the R2 routine).
+ mov SI,[BP+2] ; SI contains a pointer to the R2BytesToCopy field
+ ; in the call table entry
+ mov EDX,CS:[SI].R2CallInfo.R2EntryPoint ; EDX contains the Address of the R2 destination
+ movzx CX,CS:[SI].R2CallInfo.R2BytesToCopy ; CX contains the # of bytes to copy
+ jcxz no_params
+ mov AX,CX ; save a copy of the number of parameters
+ add BX,CX ; Assume as if the R3 params were removed
+ mov ES:[DI+4],BX ; Save old sp
+ mov word ptr ES:[DI],0 ; Clear the R3 access trap catch bytes
+ mov word ptr ES:[DI+2],0 ;
+ sub DI,CX ; DI points to lowest address for parameters of
+ ; the R2 stack
+ lea SI,[BP+8] ; source of the copy in the R3 stack
+ cld ;
+ shr CX,1
+ rep movs word ptr ES:[DI],SS:[SI] ; copy all the arguments
+ sub DI,AX ; DI now points to the begining of the lowest R2
+ ; arguments
+ jmp short params_copied
+
+no_params:
+ xor AX,AX ; Remember number of parameters
+ mov ES:[DI+4],BX ; Save old sp (no need to add CX)
+ mov word ptr ES:[DI],0 ; Clear the R3 access trap catch bytes
+ mov word ptr ES:[DI+2],0 ;
+;
+; The R2 stack looks now like this:
+;
+; <16 free bytes>
+; Old SS
+; Old SP
+; 0
+; 0
+; param1
+; param2
+; param3 <- ES:DI
+;
+params_copied:
+ sub DI,16 ; DI points at the lowest parameter on the R2 stack
+ mov ES:[DI+14],CS ; save return address from R2 procedure to this code
+ mov ES:[DI+12], OFFSET R2CodeRet
+ mov ES:[DI+8],EDX ; put on the R2 stack the address of the R2 procedure
+;
+; The R2 stack looks now like this:
+;
+; <16 free bytes>
+; Old SS
+; Old SP
+; 0
+; 0
+; param1
+; param2
+; param3
+; CS of this code DI+14
+; Offset of R2CodeRet DI+12
+; ring-2-entry CS DI+10
+; IP DI+8
+; free DI+6
+; free DI+4
+; free DI+2
+; free <- ES:DI
+;
+ ;
+ ; copy the R3 return address over the first parameter pushed onto the R3 stack
+ ;
+ mov EDX,[BP+4] ; old CS:IP
+ mov BX,BP ;
+ add BX,AX ; Add # of parameter bytes
+ mov SS:[BX+4],EDX ; save return address over first parameters
+;
+; The R3 stacks looks now like this:
+;
+; return-addr-r3 CS SP+26 BP+12
+; return-addr-r3 IP SP+24 BP+10
+; param3 SP+22 BP+8
+; return-addr-r3 CS SP+20 BP+6
+; IP SP+18 BP+4
+; return-addr-r2 IP SP+16 BP+2
+; BP SP+14 BP
+; AX SP+12 BP-2
+; ES SP+10 BP-4
+; DI SP+8 BP-6
+; SI SP+6 BP-8
+; BX SP+4 BP-10
+; CX SP+2 BP-12
+; DX SP BP-14
+;
+ pop DX ;
+ pop CX ;
+ pop BX ;
+ pop SI ;
+ mov EAX,[BP-2] ; EAX contains original BP:AX
+ mov ES:[DI+4],EAX ;
+ mov EAX,[BP-6] ; EAX contains original ES:DI
+ mov ES:[DI],EAX ;
+;
+; The R2 stack looks now like this:
+;
+; <16 free bytes>
+; Old SS
+; Old SP
+; 0
+; 0
+; param1
+; param2
+; param3
+; free
+; free
+; ring-2-entry CS
+; IP
+; original BP
+; original AX
+; original ES
+; original DI <- ES:DI
+;
+ mov SP,DI ; exchange the stack to the R2 stack
+ mov AX,ES ;
+ mov SS,AX ;
+ pop DI ; Restore all registers
+ pop ES ;
+ pop AX ;
+ pop BP ;
+;
+; The R2 stack looks now like this:
+;
+; <16 free bytes>
+; Old SS
+; Old SP
+; 0
+; 0
+; param1
+; param2
+; param3
+; CS of this code
+; Offset of R2CodeRet from beginning of segment
+; ring-2-entry CS
+; IP <- SS:ESP
+;
+
+ retf ; call the R2 actual code
+
+ ;
+ ; Here we return from the R2 code
+ ;
+R2CodeRet:
+ push BP
+ mov BP,SP
+ push AX
+ push ES
+ push DI
+ mov DI,FS:6 ; Index of thread
+ shl DI,2
+ mov AX, SEG R2StacksInfo
+ mov ES,AX
+ les DI,ES:[DI] ; ES:DI point to Top of R2 stack
+ les DI,ES:[DI-(4+16)] ; ES:DI contain the current R3 stack pointer
+ sub DI,8 ; DI points below return address to R3
+ mov EAX,[BP-2] ; move using EAX old BP:AX from R2 stack to R3 stack
+ mov ES:[DI+4],EAX ;
+ mov EAX,[BP-6] ; move using EAX ES:DI from R2 stack to R3 stack
+ mov ES:[DI],EAX ;
+ mov [BP],ES ;
+ mov [BP-2],DI ;
+ lss SP,[BP-2] ;
+ pop DI ;
+ pop ES ;
+ pop AX ;
+ pop BP ;
+ retf
+
+IF ($-CommonR2Xfer) GT 200H
+;
+; The size of the code segment exceeds 200H
+; The ldr assumes that it is not greater than 200H
+; If you have reached here and increased the value above 200H
+; you have to modify also the OS/2 SS file ldr\ldrinit.c
+;
+.ERR SizeOfSegmentExceeds200H
+ENDIF
+
+CommonR2Xfer ENDP
+
+;
+; Next statement is used to make the size of the segment exactly 64K
+; It causes the linker to issue warning L4020:
+;
+; segment: code segment size exceeds 64K-36
+;
+; Ignore the warning!
+;
+
+ ORG 0FFFFH ; This statement causes the linker to create
+ ; a segment virtual size of 64K
+
+ .DATA
+
+R2StacksInfo R2Stacks 256 DUP (<>)
+
+ .CODE THUNK16
+
+ PUBLIC DOSCALLBACK
+DOSCALLBACK PROC FAR
+
+; The ring 2 stack upon entry:
+;
+; Target Callee (segment)
+; Target Callee (offset)
+; Ret Address (segment)
+; Ret Address (offset) <- SP
+;
+
+;
+; Move to ring 3 and change the stack to ring 3 stack
+;
+
+ ; Preserve reDIsters accross the call
+
+ push BP
+ mov BP,SP
+ push AX
+ push ES
+ push DI
+ push DS
+ push SI
+
+; R2 stack is now:
+;
+; Target Callee (segment) BP+8
+; Target Callee (offset) BP+6
+; Ret Address (segment) BP+4
+; Ret Address (offset) BP+2
+; old BP <- BP
+; AX
+; ES
+; DI
+; DS
+; SI <- SP
+
+ mov DI,FS:6 ; Get current thread ID. FS points to the LINFOSEG
+ shl DI,2 ; DI points now to SEL:OFF of the R2 TOS (Top Of Stack)
+ mov AX,SEG R2StacksInfo
+ mov ES,AX ;
+ lds SI,ES:[DI] ; DS:SI contain the R2 TOS
+
+ mov AX,SS
+ cmp AX,DS:[SI-18] ; Check if we are on the R3 ring
+ jne doscallback_R2
+
+ ; The current ring is R3
+
+ pop SI
+ pop DS
+ pop DI
+ pop ES
+ pop AX
+
+ call dword ptr [BP+6]
+
+ pop BP
+ retf 4
+
+doscallback_R2:
+
+ push SI ; Save OS2 TOS
+ lds SI,DS:[SI-20] ; DS:SI contain the R3 current stack
+
+ mov ES:[DI],BP ; Set a new ring 2 TOS for ring 3 -> ring 2
+ add word ptr ES:[DI],4 ; transitions.
+
+ ;
+ ; set a return address to the DosRetForward() API
+ ;
+
+ sub SI,10 ; Allocate space on the ring 3 stack
+ mov AX, SEG DosRetForward
+ mov DS:[SI+8],AX
+ mov AX,OFFSET DosRetForward
+ mov DS:[SI+6],AX
+
+ mov AX,[BP+8] ; Target Callee (segment)
+ mov DS:[SI+4],AX ;
+ mov AX,[BP+6] ; Target Callee (offset)
+ mov DS:[SI+2],AX ;
+ mov AX,[BP] ; old BP
+ mov DS:[SI],AX ;
+ ;
+ ; move the R2 return address to a new location on the R2 stack
+ ; so that we can return back to the R2 function that called DosCallBack()
+ ;
+ mov AX,[BP+4] ; Ret address (segment)
+ mov [BP+8],AX ;
+ mov AX,[BP+2] ; Ret address (offset)
+ mov [BP+6],AX ;
+ ;
+ ; Save old R2 TOS in the R2 stacks data structure
+ ;
+ pop word ptr [BP+4]
+ ;
+ ; save ring 3 SS:SP for task switch (done by the LSS instruction below)
+ ;
+ mov [BP],SI
+ mov [BP+2],DS
+
+ pop SI ; Restore saved reDIster
+ pop DS
+ pop DI
+ pop ES
+ pop AX
+ lss SP,[BP] ; switch stacks
+ pop BP
+
+ retf ; perform call to user routine in ring 3
+
+DOSCALLBACK ENDP
+
+DOSRETFORWARD PROC FAR
+
+ sub SP,2 ; allocate space on the ring 3 stack
+ push BP
+ mov BP,SP
+ push AX
+ push ES
+ push DI
+ push DS
+ push SI
+
+ mov DI,FS:6 ; Get current thread ID. FS points to the LINFOSEG
+ shl DI,2 ; DI points now to SEL:OFF of the R2 TOS (Top Of Stack)
+ mov AX,SEG R2StacksInfo
+ mov ES,AX ;
+ lds SI,ES:[DI] ; DS:SI contain the R2 TOS
+ mov AX,DS:[SI] ; ring 2 prev TOS
+ mov ES:[DI],AX
+
+ mov AX,[BP] ; Get old BP and save it on the ring 2 stack
+ mov DS:[SI],AX
+ ;
+ ; save ring 2 SS:SP for task switch (done by the LSS instruction below)
+ ;
+ mov [BP+2],DS
+ mov [BP],SI
+
+ pop SI
+ pop DS
+ pop DI
+ pop ES
+ pop AX
+ lss SP,[BP]
+ pop BP
+
+ retf ; return to the ring 2 routine that called DosCallBack()
+
+DOSRETFORWARD ENDP
+ END
+