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