|
|
title "Fast Protected Mode services" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; fastwow.asm ; ; Abstract: ; ; This module implements a fast mechanism for WOW apps to go back ; and forth from app code (protected mode, 16-bit segmented) to ; WOW32.DLL (flat). ; ; ; Author: ; ; Bob Day (bobday) 07-29-92 ; copied from FASTPM.ASM written by Dave Hastings (daveh) 26-Jul-91 ; ; barryb 11-nov-92 added fast callback mechanism, pared ; WOWBopEntry to the bare essentials (hopefully) ; ; ; rules of thumb: ; - monitor context gets saved on monitor stack on entry to ; FastWOWCallbackCall and restored on the way out of FastWOWCallbackRet ; - no need to save any VDM general registers - krnl286 uses the ; WOW16Call stack frame for this ; - you can't save anything on the 16-bit stack because the stack ; gets tinkered with by DispatchInterrupts ; ; WARNING WARNING WARNING WARNING WARNING WARNING WARNING ; these routines are optimized for the straight-through case, no ; interrupt being dispatched, so there's duplicate code in the ; routines. if you add stuff, be sure to add it to both paths. ; WARNING WARNING WARNING WARNING WARNING WARNING WARNING ; ; sudeepb 09-Dec-1992 ; Changed all the refernces to virtual interrupt flag in the VDMTIB ; to fixed DOS location. ;-- .386p
include ks386.inc include bop.inc include wowtd.inc
if DBG DEBUG equ 1 endif
ifdef DEBUG DEBUG_OR_WOWPROFILE equ 1 endif ifdef WOWPROFILE DEBUG_OR_WOWPROFILE equ 1 endif
include wow.inc include callconv.inc include vint.inc include vdmtib.inc
EXTRN __imp__CurrentMonitorTeb:DWORD EXTRN __imp__FlatAddress:DWORD
_TEXT SEGMENT PARA PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
EXTRNP _DispatchInterrupts,0 EXTRNP @W32PatchCodeWithLpfnw32, 2 EXTRNP @InterpretThunk, 2
EXTRNP _Ssync_WOW_CommDlg_Structs, 3
ifdef DEBUG EXTRNP _logargs,2 EXTRNP _logreturn,3 endif
_TEXT ENDS
_DATA SEGMENT DWORD PUBLIC 'DATA' extrn _aw32WOW:Dword
public _WowpLockPrefixTable _WowpLockPrefixTable label dword dd offset FLAT:_wowlock1 dd offset FLAT:_wowlock2 dd offset FLAT:_wowlock3 dd offset FLAT:_wowlock4 dd offset FLAT:_wowlock5 dd offset FLAT:_wowlock6 dd 0
Stack16 LABEL DWORD _savesp16 DW 0 _savess16 DW 0
_saveip16 DD 0 _savecs16 DD 0
_saveebp32 DD 0
_saveeax DD 0 _saveebx DD 0 _saveecx DD 0 _saveedx DD 0
_fKernelCSIPFixed DB 0 _DATA ENDS
public _saveeax public _saveebx public _saveecx public _saveedx
public _savesp16 public _savess16 public _savecs16 public _saveip16 public _saveebp32 public _fKernelCSIPFixed public Stack16
; ; The variable fKernelCSIPFixed is used so the fastbop/callback mechanism ; knows when it no longer needs to pop the 16-bit return address off the ; 16-bit stack on entry to WOWBopEntry and FastWOWCallbackRet because kernel ; has booted and the address won't be changing. ; ; ; The assumption is that we're always coming from the same place in kernel. ; It also saves a jmp when returning to kernel. ;
_TEXT SEGMENT
page ,132 subttl "WOWBopEntry" ;++ ; ; Routine Description: ; ; This routine switches from the VDM context to the monitor context. ; Then executes the appropriate WOW api call. ; Then returns back to the VDM context. ; ; Arguments: ; ; none ; ; Returns: ; ; puts result of WOW api call (EAX) into pFrame->wDX, pFrame->wAX ; ; [LATER] add a field to the frame and set it to !0 if a task switch ; has occurred. kernel can read this off the stack instead ; of having to load the kernelDS to look at wCurTDB ;
assume DS:Nothing,ES:Nothing,SS:Nothing ALIGN 16 cPublicProc _WOWBopEntry,0 mov bx,KGDT_R3_DATA OR RPL_MASK ; Move back to Flat DS mov ds,bx ; so push and pop size same assume ds:_DATA mov _saveeax,eax ; Multi Media Apps call api's mov _saveebx,ebx ; at interrupt time which trash mov _saveecx,ecx ; the high parts of the 32 bit mov _saveedx,edx ; registers. [LATER] this ; should be moved to fame pushfd ; Save them flags
; we make here the assumption that the c compiler always keeps the ; direction flag clear. We clear it here instead of restoring it ; from the context for performance cld
mov eax,KGDT_R3_TEB OR RPL_MASK ; restore flat FS mov fs,ax mov ebx, dword ptr fs:[PcTeb] mov ebx, dword ptr [ebx].TeVdm ; get pointer to contexts
; ; start saving some of the 16-bit context ;
; Interrupts are always enabled in WOW, but if we are trying ; to simulate the fact that they are disabled, we need to ; turn them off in the structure.
pop eax mov edx,dword ptr ds:FIXED_NTVDMSTATE_LINEAR ; Get simulated bits or edx,NOT VDM_VIRTUAL_INTERRUPTS and eax,edx ; Pass on interrupt bit if it was on mov dword ptr [ebx].VtVdmContext.CsEFlags,eax
; Save the calling address ; this address remains constant once krnl286 has booted. ; these three instructions are cheaper than saving it ; [LATER] this can be reduced. how?
cmp _fKernelCSIPFixed, 0 je wbegetretaddress add esp, 8 ; pop cs, ip
wbe10: ; Save the stack (pre-call) mov _savess16,di ; 16-bit SS put in di by krnl286 mov _savesp16,sp
; switch Stacks
.errnz (CsEsp + 4 - CsSegSS) lss esp, [ebx].VtMonitorContext.CsEsp
; Now running on Monitor stack
; save hiword of ESI, EDI ; note that di has already been trashed ; ; [LATER] move this to the vdmframe, don't need to push it twice
push esi push edi push ebp
cPublicFpo 6,0 ; locals, params ; we only save 3 but FastWOWCallbackCall ; saves 3 as well
; Set up flat segment registers
mov edx,KGDT_R3_DATA OR RPL_MASK mov es,dx ; Make them all point to DS mov gs,dx ; [LATER] do we really have to set up GS?
mov eax,KGDT_R3_TEB OR RPL_MASK ; restore flat FS mov fs,ax
mov ebp, _saveebp32
; Update the CURRENTPTD->vpStack mov eax,fs:[PcTeb] mov ecx,[eax+TbWOW32Reserved] ; move CURRENTPTD into ecx mov esi, [Stack16] mov dword ptr [ecx],esi ; PTD->vpStack = vpCurrentStack
; Convert the 16:16 SS:SP pointer into a 32-bit flat pointer ; DI = 16-bit SS, put there by kernel
and esi, 0FFFFh ; esi = 16-bit sp and edi, 0FFFFh ; remove junk from high word shr edi, 3 ; remove ring and table bits
mov edx, dword ptr [__imp__FlatAddress] add esi, dword ptr [edx+edi*4] ; esi is now a flat pointer to the frame
mov eax,dword ptr [ecx+cbOffCOMMDLGTD] ; ecx = CURRENTPTD cmp eax,0 ; eax = PTD->PCOMMDLGTD jnz SsyncCommDlg16to32
SsyncContinue1:
ifdef DEBUG push ecx stdCall _logargs,<3,esi> pop ecx endif
; Convert the wCallID into a Thunk routine address.
mov edx, dword ptr [esi].vf_wCallID
; esi = pFrame mov [ecx].WtdFastWowEsp, esp test edx, 0ffff0000h mov eax, edx jnz MakeCall ifdef DEBUG imul edx, size W32 else .errnz (size W32 - 4) shl edx, 2 endif mov edx, dword ptr [_aw32WOW + edx] ; eax = aw32WOW[edx].lpfnW32 test edx, 0ffff0000h ; check for intthunk (hiword 0) jnz @f mov eax, @InterpretThunk@8 jmp MakeCall @@: mov ecx, esi ; ecx = pFrame & edx = lpfnW32 call @W32PatchCodeWithLpfnw32@8 MakeCall: mov ecx, esi ; ecx = pFrame & edx = lpfnW32 call eax
ApiReturnAddress:
; ; IMPORTANT NOTE: Upon retruned from wow32 worker routine, the ; non-volatile registers may be destroyed if it is returned via ; try-except handler. ;
mov ebx, dword ptr [__imp__CurrentMonitorTeb] mov ecx,fs:[PcTeb] mov [ebx], ecx ; Tell NTVDM which is the active thread mov ecx,[ecx+TbWOW32Reserved] ; move CURRENTPTD into ecx
mov ebx,dword ptr [ecx+cbOffCOMMDLGTD] ; ecx = CURRENTPTD cmp ebx,0 ; ebx = PTD->PCOMMDLGTD jnz SsyncCommDlg32to16
SsyncContinue2:
ifdef DEBUG push ecx push eax
movzx esi, word ptr [ecx] ; offset movzx edi, word ptr [ecx+2] ; selector shr edi, 3 ; remove ring and table bits mov ebx, dword ptr [__imp__FlatAddress] add esi, dword ptr [ebx+edi*4] ; esi = offset + base
stdCall _logreturn,<5, esi, eax>
pop eax pop ecx endif mov ebx, dword ptr fs:[PcTeb] mov ebx, dword ptr [ebx].TeVdm ; get pointer to contexts
push dword ptr [ebx].VtVdmContext.CsEFlags ;get 16-bit flags popfd ; in case the direction flag was set
test dword ptr ds:FIXED_NTVDMSTATE_LINEAR,dword ptr VDM_INTERRUPT_PENDING jnz wl70
wl40: mov _saveebp32, ebp
pop ebp pop edi pop esi
mov [ebx].VtMonitorContext.CsEsp,esp mov [ecx].WtdFastWowEsp,esp
; return to vdm stack push word ptr [ecx+2] push word ptr 0 push word ptr [ecx] lss esp,[esp]
; stick the API return value in the stack for kernel to pop. ; it's been in EAX since we called the thunk routine.
mov bx, sp ; for temporary addressing mov dword ptr ss:[bx].vf_wAX, eax
; return to VDM, fake a far jump push _savecs16 push _saveip16
mov eax,_saveeax ; Retore High parts of regs mov ebx,_saveebx mov ecx,_saveecx mov edx,_saveedx
retf
; wl60: ; Interrupts are disabled, turn them off in the virtual flags ; _wowlock1: lock and dword ptr ds:FIXED_NTVDMSTATE_LINEAR,NOT VDM_VIRTUAL_INTERRUPTS jmp wl40
; wl70: ; Interrupt came in, dispatch it ;
; translate the interrupt flag to the virtual interrupt flag ; if interrupts are disabled then blow it off
test [ebx].VtVdmContext.CsEFlags,dword ptr EFLAGS_INTERRUPT_MASK jz wl60 _wowlock2: lock or dword ptr ds:FIXED_NTVDMSTATE_LINEAR,dword ptr VDM_VIRTUAL_INTERRUPTS
push eax ; save API return value push ebx push ecx
; ; refresh VdmTib.VtVdmContext so DispatchInterrupts can party ;
; ecx points to CURRENTPTD->vpStack 16-bit ss:sp
mov si, [ecx + 2] xor edi,edi mov di, [ecx]
mov eax, _saveip16 mov dword ptr [ebx].VtVdmContext.CsEip, eax mov eax, _savecs16 mov dword ptr [ebx].VtVdmContext.CsSegCs, eax
mov word ptr [ebx].VtVdmContext.CsSegSs, si mov dword ptr [ebx].VtVdmContext.CsEsp, edi
stdCall _DispatchInterrupts test dword ptr [ebx].VtVdmContext.CsEFlags,VDM_VIRTUAL_INTERRUPTS jnz wl80 _wowlock3: lock and dword ptr ds:FIXED_NTVDMSTATE_LINEAR,NOT VDM_VIRTUAL_INTERRUPTS
wl80: pop ecx ; points to ptd->vpStack pop ebx pop eax ; eax = API return value
mov _saveebp32, ebp
; restore the hiwords of esi, edi pop ebp pop edi pop esi
mov dword ptr [ebx].VtMonitorContext.CsEsp,esp mov [ecx].WtdFastWowEsp,esp
pushfd and dword ptr [esp], 0ffffbfffH popfd
; ; switch to 16-bit stack (probably an interrupt stack) ;
.errnz (CsEsp + 4 - CsSegSS) lss esp, [ebx].VtVdmContext.CsEsp
; ; fake far jump to the 16-bit interrupt routine ;
push dword ptr [ebx].VtVdmContext.CsEflags push dword ptr [ebx].VtVdmContext.CsSegCs push dword ptr [ebx].VtVdmContext.CsEip
; stick the API return value in the stack for kernel to pop. ; it's been in EAX since we called the thunk routine.
; les bx, Stack16 les bx, [ecx] mov dword ptr es:[bx].vf_wAX, eax
mov eax,_saveeax ; Retore High parts of regs mov ebx,_saveebx mov ecx,_saveecx mov edx,_saveedx iretd
wbegetretaddress:
pop eax mov _saveip16, eax pop edx mov _savecs16, edx
jmp wbe10
SsyncCommDlg16to32:
push ecx ; save CURRENTPTD
push dword ptr [esi+cbOffwThunkCSIP] ; esi = pFrame push 1 ; 16to32 flag push eax ; pComDlgTD call _Ssync_WOW_CommDlg_Structs@12
pop ecx jmp SsyncContinue1
SsyncCommDlg32to16: push ecx ; save CURRENTPTD push eax ; preserve API return value
; build a flat ptr to pFrame mov esi, [ecx] and esi, 0FFFFh ; esi = 16-bit sp ; edi should already be set
mov edx, dword ptr [__imp__FlatAddress] add esi, dword ptr [edx+edi*4] ; esi = pframe
push dword ptr [esi+cbOffwThunkCSIP] ; esi = pFrame push 0 ; 32to16 flag push ebx ; pComDlgTD call _Ssync_WOW_CommDlg_Structs@12
pop eax pop ecx jmp SsyncContinue2
stdENDP _WOWBopEntry
cPublicProc _PostExceptionHandler,0 assume ds:_DATA
; ; Fixed up Exception registration pointer (just in case) ;
mov eax, fs:[PcExceptionList] peh00: cmp eax, esp jb short @f
mov fs:[PcExceptionList], eax xor eax, eax jmp ApiReturnAddress
@@: mov eax, [eax] ; move to next reg record jmp short peh00
stdENDP _PostExceptionHandler
;++ ; ; VOID ; W32SetExceptionContext ( ; PCONTEXT ContextRecord ; ); ; ; Routine Description: ; ; This functions updates exception context to our predefined ; post-exception handler such that if we continue exception ; execution the control will continue on our post-exception ; handling code. ; ; Arguments: ; ; ContextRecord - supplies a pointer to the exception context. ; ; Returns: ; ; Exception context updated. ;--
cPublicProc _W32SetExceptionContext,1 assume ds:_DATA
push fs ; I don;t think we need this, just in case mov ecx,KGDT_R3_TEB OR RPL_MASK ; restore flat FS mov fs,cx mov ecx, fs:[PcTeb] mov ecx, dword [ecx].TeVdm pop fs
mov edx, [esp+4] ; (edx) = Context Record mov ecx, dword ptr [ecx].VtMonitorContext.CsEsp mov [edx].CsEsp, ecx mov [edx].CsEip, offset FLAT:_PostExceptionHandler stdRET _W32SetExceptionContext
stdENDP _W32SetExceptionContext
;++ ; ; BOOLEAN ; IsW32WorkerException ( ; VOID ; ); ; ; Routine Description: ; ; This function checks if the exception occurred in WOW32 API. ; ; Arguments: ; ; None ; ; Returns: ; ; returns a BOOLEAN value to indicate if this is WOW32 API exception. ; ;--
cPublicProc _IsW32WorkerException, 0 assume ds:_DATA
mov ecx, fs:[PcTeb] mov ecx, [ecx].TbWOW32Reserved mov eax, dword ptr [ecx].WtdFastWowEsp or eax, eax jz @f ; FastWowEsp is zero, not worker exception mov edx, [eax-4] cmp edx, offset FLAT:ApiReturnAddress ; eax is still monitor esp, so it's nonzero je short @f xor eax, eax @@: stdRET _IsW32WorkerException
stdENDP _IsW32WorkerException
_TEXT ends
page ,132 subttl "FastWOWCallbackCall" ;++ ; ; Routine Description: ; ; This routine is a fast callback from WOW32 to 16-bit code. ; It assumes that the 16-bit stack pointer is already in Stack16. ; The caller must set this up with FastBopSetVDMStack() before ; calling this routine. ; ; WARNING: only the minimal set of registers are saved/restored ; as a speed optimization. ; ; Arguments: ; ; none ; ; Returns: ; ; nothing. ;
_TEXT SEGMENT
assume DS:FLAT cPublicProc _FastWOWCallbackCall,0
; Save monitor general registers on monitor stack ; we'll pick em back up on the way out of FastWOWCallbackRet
push esi push edi push ebx
mov ebx, fs:[PcTeb] mov ebx, dword ptr [ebx].TeVdm
; translate the interrupt flag to the virtual interrupt flag
test [ebx].VtVdmContext.CsEFlags,dword ptr EFLAGS_INTERRUPT_MASK jz fe10 _wowlock4: lock or dword ptr ds:FIXED_NTVDMSTATE_LINEAR,dword ptr VDM_VIRTUAL_INTERRUPTS
test dword ptr ds:FIXED_NTVDMSTATE_LINEAR,dword ptr VDM_INTERRUPT_PENDING jnz fe70
jmp fe20
fe10: _wowlock5: lock and dword ptr ds:FIXED_NTVDMSTATE_LINEAR, NOT VDM_VIRTUAL_INTERRUPTS fe20:
mov _saveebp32, ebp
mov ecx, fs:[PcTeb] mov ecx, [ecx].TbWOW32Reserved ; move CURRENTPTD into ecx mov [ebx].VtMonitorContext.CsEsp,esp mov [ecx].WtdFastWowEsp,esp
pushfd pop ecx mov [ebx].VtMonitorContext.CsEflags,ecx
pushfd and dword ptr [esp], 0ffffbfffH popfd
; switch to vdm stack push _savess16 push word ptr 0 push _savesp16 lss esp, [esp]
; before going to 16 bit, patch ebp to zero the high bits ; hijaak pro app is relying upon hiword of ebp being 0 and ebp, 0ffffh
; put flags, cs, and ip onto the 16-bit stack so we can iret to krnl286
push dword ptr [ebx].VtVdmContext.CsEflags push _savecs16 push _saveip16
; return to krnl286
iretd
fe70: ; Interrupt pending, dispatch it ;
push ebx push eax
; ; refresh VdmTib.VtVdmContext so DispatchInterrupts can party ;
mov eax, _saveip16 mov edx, _savecs16 mov dword ptr [ebx].VtVdmContext.CsEip, eax mov dword ptr [ebx].VtVdmContext.CsSegCs, edx
mov ax, _savess16 xor edx,edx mov dx, _savesp16 mov word ptr [ebx].VtVdmContext.CsSegSs, ax mov dword ptr [ebx].VtVdmContext.CsEsp, edx
stdCall _DispatchInterrupts
test dword ptr [ebx].VtVdmContext.CsEFlags,VDM_VIRTUAL_INTERRUPTS jnz fe80
_wowlock6: lock and dword ptr ds:FIXED_NTVDMSTATE_LINEAR,NOT VDM_VIRTUAL_INTERRUPTS
fe80: pop eax pop ebx
mov _saveebp32, ebp
mov ecx, fs:[PcTeb] mov ecx, [ecx].TbWOW32Reserved mov [ebx].VtMonitorContext.CsEsp,esp mov [ecx].WtdFastWowEsp,esp
pushfd pop ecx mov [ebx].VtMonitorContext.CsEflags,ecx pushfd and dword ptr [esp], 0ffffbfffH popfd
; switch to vdm stack push word ptr [ebx].VtVdmContext.CsSegSs push dword ptr [ebx].VtVdmContext.CsEsp lss esp, [esp]
; put flags, cs, and ip onto the 16-bit stack so we can iret to krnl286
; before interrupts are dispatched, patch ebp to zero the high bits ; hijaak pro app is relying upon hiword of ebp being 0 and ebp, 0ffffh
push dword ptr [ebx].VtVdmContext.CsEflags push dword ptr [ebx].VtVdmContext.CsSegCs push dword ptr [ebx].VtVdmContext.CsEip
; return to krnl or interrupt routine
iretd
stdENDP _FastWOWCallbackCall
;++ ; ; Routine Description: ; ; This routine is called by krnl286 upon returning from a ; callback. ; ; Arguments: ; ; none ; ; Returns: ; ; nothing ; ; WARNING: only the minimal set of registers are saved/restored ; as a speed optimization. ; ; assume DS:Nothing,ES:Nothing,SS:Nothing ALIGN 16 cPublicProc _FastWOWCallbackRet,0
mov ebx,KGDT_R3_DATA OR RPL_MASK mov ds,bx assume ds:_DATA
mov ebx, KGDT_R3_TEB OR RPL_MASK mov fs, bx mov ebx, fs:[PcTeb] mov ebx, dword ptr [ebx].TeVdm
pushfd pop eax mov dword ptr [ebx].VtVdmContext.CsEFlags,eax
; the words at top of stack are the return address of our ; caller (krnl286). ; this address remains constant once krnl286 has booted. ; these three instructions are cheaper than saving it
cmp _fKernelCSIPFixed, 0 je fwcbrgetretaddress
add esp, 8 ; pop cs, ip
fwcbr10:
; refresh CURRENTPTD->vpStack with the current stack
;mov ecx, KGDT_R3_TEB OR RPL_MASK ;mov fs, cx mov eax, fs:[PcTeb] mov ecx, [eax+TbWOW32Reserved] ; move CURRENTPTD into ecx
mov _savess16,ss mov _savesp16,sp
mov esi, [Stack16] mov [ecx], esi
movzx esi,si
mov dword ptr [ebx].VtVdmContext.CsEsp, esi mov word ptr [ebx].VtVdmContext.CsSegSs, ss
; switch Stacks .errnz (CsEsp + 4 - CsSegSS) lss esp, [ebx].VtMonitorContext.CsEsp
; Now running on Monitor stack
test dword ptr ds:FIXED_NTVDMSTATE_LINEAR,dword ptr VDM_VIRTUAL_INTERRUPTS jz fl10
or [ebx].VtVdmContext.CsEFlags,dword ptr EFLAGS_INTERRUPT_MASK jmp fl20
fl10: and dword ptr [ebx].VtVdmContext.CsEFlags, NOT EFLAGS_INTERRUPT_MASK fl20:
; set up Monitor regs
mov esi, KGDT_R3_DATA OR RPL_MASK mov es, si ; [LATER] do we really have to set up the GS? mov gs, si
; set up Monitor general regs
mov ebp, _saveebp32
; return
pop ebx pop edi pop esi ret
fwcbrgetretaddress:
pop eax pop edx
mov _saveip16, eax mov _savecs16, edx
jmp fwcbr10
stdENDP _FastWOWCallbackRet
;++ ; ; Routine Description: ; ; This returns the current task's 16-bit ss:sp (vpStack) ; ; Arguments: ; ; none ; ; Returns: ; ; returns a 32-bit value that can be passed to GetVDMPointer ;
assume DS:_DATA,SS:Nothing cPublicProc _FastBopVDMStack,0 mov eax, [Stack16] stdRET _FastBopVDMStack
stdENDP _FastBopVDMStack
;++ ; ; Routine Description: ; ; This sets the current task's 16-bit ss:sp (vpStack), ; used only when FASTSTACK is enabled. ; ; Arguments: ; ; vpStack (32-bit ss:sp, not a flat pointer) ; ; Returns: ; ; none ;
cPublicProc _FastBopSetVDMStack,1
mov eax, [esp+4] mov [Stack16], eax stdRET _FastBopSetVDMStack
stdENDP _FastBopSetVDMStack
_TEXT ends
end
|