mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
16 KiB
555 lines
16 KiB
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
|
|
|