title "Vdm Instuction Emulation" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; emv86.asm ; ; Abstract: ; ; This module contains the routines for emulating instructions and ; faults from v86 mode. ; ; Author: ; ; sudeep bharati (sudeepb) 16-Nov-1992 ; ; Environment: ; ; Kernel mode only. ; ; Notes: ; ; ; Revision History: ; ;-- .386p .xlist include ks386.inc include i386\kimacro.inc include mac386.inc include i386\mi.inc include callconv.inc include ..\..\vdm\i386\vdm.inc include vdmtib.inc .list extrn VdmOpcode0f:proc extrn _DbgPrint:proc extrn _KeI386VdmIoplAllowed:dword extrn _KeI386VirtualIntExtensions:dword EXTRNP _Ki386VdmDispatchIo,5 EXTRNP _Ki386VdmDispatchStringIo,8 EXTRNP _KiDispatchException,5 EXTRNP _Ki386VdmReflectException,1 EXTRNP _VdmEndExecution,2 EXTRNP _VdmDispatchBop,1 EXTRNP _VdmPrinterStatus,3 EXTRNP _VdmPrinterWriteData, 3 EXTRNP _VdmDispatchInterrupts,2 EXTRNP _KeBugCheck,1 EXTRNP _VdmSkipNpxInstruction,4 EXTRNP _VdmFetchBop1,1 ifdef VDMDBG EXTRNP _VdmTraceEvent,4 endif extrn _ExVdmOpcodeDispatchCounts:dword extrn OpcodeIndex:byte extrn _VdmUserCr0MapIn:byte extrn _MmUserProbeAddress:DWORD page ,132 ifdef VDMDBG %out Debugging version endif ; Force assume into place _PAGE SEGMENT DWORD PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING _PAGE ENDS _TEXT$00 SEGMENT DWORD PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING _TEXT$00 ENDS PAGEDATA SEGMENT DWORD PUBLIC 'DATA' ; ; Instruction emulation emulates the following instructions. ; The emulation affects the noted user mode registers. ; ; ; In V86 mode, the following instruction are emulated in the kernel ; ; Registers (E)Flags (E)SP SS CS ; PUSHF X X ; POPF X X ; INTnn X X X ; INTO X X X ; IRET X X X ; CLI X ; STI X ; ; ; INSB ; INSW ; OUTSB ; OUTSW ; INBimm ; INWimm ; OUTBimm ; OUTWimm ; INB ; INW ; OUTB ; OUTW ; ; WARNING What do we do about 32 bit io instructions?? ; OpcodeDispatchV86 - table of routines used to emulate instructions ; in v86 mode. public OpcodeDispatchV86 dtBEGIN OpcodeDispatchV86,OpcodeInvalidV86 dtS VDM_INDEX_0F , Opcode0FV86 dtS VDM_INDEX_ESPrefix , OpcodeESPrefixV86 dtS VDM_INDEX_CSPrefix , OpcodeCSPrefixV86 dtS VDM_INDEX_SSPrefix , OpcodeSSPrefixV86 dtS VDM_INDEX_DSPrefix , OpcodeDSPrefixV86 dtS VDM_INDEX_FSPrefix , OpcodeFSPrefixV86 dtS VDM_INDEX_GSPrefix , OpcodeGSPrefixV86 dtS VDM_INDEX_OPER32Prefix , OpcodeOPER32PrefixV86 dtS VDM_INDEX_ADDR32Prefix , OpcodeADDR32PrefixV86 dtS VDM_INDEX_INSB , OpcodeINSBV86 dtS VDM_INDEX_INSW , OpcodeINSWV86 dtS VDM_INDEX_OUTSB , OpcodeOUTSBV86 dtS VDM_INDEX_OUTSW , OpcodeOUTSWV86 dtS VDM_INDEX_PUSHF , OpcodePUSHFV86 dtS VDM_INDEX_POPF , OpcodePOPFV86 dtS VDM_INDEX_INTnn , OpcodeINTnnV86 dtS VDM_INDEX_INTO , OpcodeINTOV86 dtS VDM_INDEX_IRET , OpcodeIRETV86 dts VDM_INDEX_NPX , OpcodeNPXV86 dtS VDM_INDEX_INBimm , OpcodeINBimmV86 dtS VDM_INDEX_INWimm , OpcodeINWimmV86 dtS VDM_INDEX_OUTBimm , OpcodeOUTBimmV86 dtS VDM_INDEX_OUTWimm , OpcodeOUTWimmV86 dtS VDM_INDEX_INB , OpcodeINBV86 dtS VDM_INDEX_INW , OpcodeINWV86 dtS VDM_INDEX_OUTB , OpcodeOUTBV86 dtS VDM_INDEX_OUTW , OpcodeOUTWV86 dtS VDM_INDEX_LOCKPrefix , OpcodeLOCKPrefixV86 dtS VDM_INDEX_REPNEPrefix , OpcodeREPNEPrefixV86 dtS VDM_INDEX_REPPrefix , OpcodeREPPrefixV86 dtS VDM_INDEX_CLI , OpcodeCLIV86 dtS VDM_INDEX_STI , OpcodeSTIV86 dtS VDM_INDEX_HLT , OpcodeHLTV86 dtEND MAX_VDM_INDEX PAGEDATA ENDS _PAGE SEGMENT DWORD USE32 PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING page ,132 subttl "Overide Prefix Macro" ;++ ; ; Routine Description: ; ; This macro generates the code for handling override prefixes ; The routine name generated is OpcodeXXXXPrefix, where XXXX is ; the name used in the macro invocation. The code will set the ; PREFIX_XXXX bit in the Prefix flags. ; ; Arguments ; name = name of prefix ; esi = address of reg info ; edx = opcode ; ; Returns ; user mode Eip advanced ; eax advanced ; edx contains next byte of opcode ; ; NOTE: This routine exits by dispatching through the table again. ;-- opPrefix macro name public Opcode&name&PrefixV86 Opcode&name&PrefixV86 proc or ebx,PREFIX_&name ifdef VDMDBG _DATA segment Msg&name&Prefix db 'NTVDM: Encountered override prefix &name& %lx at ' db 'address %lx', 0ah, 0dh, 0 _DATA ends push [ebp].TsEip push [ebp].TsSegCs push offset FLAT:Msg&name&Prefix call _DbgPrint add esp,12 endif jmp OpcodeGenericPrefixV86 ; dispatch to next handler Opcode&name&PrefixV86 endp endm irp prefix, opPrefix prefix endm page ,132 subttl "Instruction Emulation Dispatcher for V86" ;++ ; ; Routine Description: ; ; This routine dispatches to the opcode specific emulation routine, ; based on the first byte of the opcode. Two byte opcodes, and prefixes ; result in another level of dispatching, from the handling routine. ; This code is called at APC_LEVEL to prevent modifications to the ; trap frame from NtSetContextThread. ; ; Arguments: ; ; [esp+4] = pointer to trap frame ; ; Returns: ; ; EAX = 0 failure ; 1 success cPublicProc _Ki386DispatchOpcodeV86,1 push ebp mov ebp, [esp+8] movzx esi,word ptr [ebp].TsSegCs shl esi,4 and dword ptr [ebp].TsEip, 0FFFFH and dword ptr [ebp].TsHardwareEsp, 0FFFFH add esi,[ebp].TsEip ; ; Probe and fetch the first byte from the instruction stream. ; Since we should be at APC_LEVEL here the trap frame can't be ; modified by the set context code. We don't have to capture. ; stdCall _VdmFetchBop1, movzx edx, OpcodeIndex[eax] ;get opcode index mov edi,1 xor ebx,ebx ; All handler routines will get the following on entry ; ebx -> prefix flags ; ebp -> trap frame ; cl -> byte at the faulting address ; interrupts enabled and Irql at APC level ; esi -> address of faulting instruction ; edi -> instruction length count ; All handler routines return ; EAX = 0 for failure ; EAX = 1 for success if DEVL inc _ExVdmOpcodeDispatchCounts[edx * type _ExVdmOpcodeDispatchCounts] endif ifdef VDMDBG pushad stdCall _VdmTraceEvent, popad endif call dword ptr OpcodeDispatchV86[edx * type OpcodeDispatchV86] pop ebp stdRet _Ki386DispatchOpcodeV86 stdENDP _Ki386DispatchOpcodeV86 page ,132 subttl "Invalid Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an invalid opcode. It prints the invalid ; opcode message, and causes a GP fault to be reflected to the ; debuger ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeInvalidV86 OpcodeInvalidV86 proc xor eax,eax ; ret fail ret OpcodeInvalidV86 endp page ,132 subttl "Generic Prefix Handler" ;++ ; ; Routine Description: ; ; This routine handles the generic portion of all of the prefixes, ; and dispatches the next byte of the opcode. ; ; Arguments: ; ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeGenericPrefixV86 OpcodeGenericPrefixV86 proc inc esi inc edi movzx ecx, byte ptr [esi] movzx edx, OpcodeIndex[ecx] ;get opcode index if DEVL inc _ExVdmOpcodeDispatchCounts[edx * type _ExVdmOpcodeDispatchCounts] endif jmp OpcodeDispatchV86[edx * type OpcodeDispatchV86] OpcodeGenericPrefixV86 endp page ,132 subttl "Byte string in Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INSB opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; WARNING size override? ds override? public OpcodeINSBV86 OpcodeINSBV86 proc push ebp ; trap frame push edi ; size of insb movzx eax,word ptr [ebp].TsV86Es shl eax,16 movzx ecx,word ptr [ebp].TsEdi or eax,ecx push eax ; address mov eax,1 xor ecx, ecx test ebx,PREFIX_REP ; prefixREP jz oisb20 mov ecx, 1 movzx eax,word ptr [ebp].TsEcx oisb20: push eax ; number of io ops push TRUE ; read op push ecx ; REP prefix ? push 1 ; byte op movzx eax,word ptr [ebp].TsEdx push eax ; port number ; Ki386VdmDispatchStringIo enables interrupts IFDEF STD_CALL call _Ki386VdmDispatchStringIo@32 ; use retval ELSE call _Ki386VdmDispatchStringIo ; use retval add esp,24 ENDIF ret OpcodeINSBV86 endp page ,132 subttl "Word String In Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INSW opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINSWV86 OpcodeINSWV86 proc push ebp ; trap frame push edi ; size of insw movzx eax,word ptr [ebp].TsV86Es shl eax,16 movzx ecx,word ptr [ebp].TsEdi or eax,ecx push eax ; address mov eax,1 xor ecx, ecx test ebx,PREFIX_REP ; prefixREP jz oisw20 mov ecx, 1 movzx eax,word ptr [ebp].TsEcx oisw20: push eax ; number of io ops push TRUE ; read op push ecx ; REP prefix ? push 2 ; word op movzx eax,word ptr [ebp].TsEdx push eax ; port number ; Ki386VdmDispatchStringIo enables interrupts IFDEF STD_CALL call _Ki386VdmDispatchStringIo@32 ; use retval ELSE call _Ki386VdmDispatchStringIo ; use retval add esp,24 ENDIF ret OpcodeINSWV86 endp page ,132 subttl "Byte String Out Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTSB opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeOUTSBV86 OpcodeOUTSBV86 proc push ebp ; trap frame push edi ; size of outsb movzx eax,word ptr [ebp].TsV86Ds shl eax,16 movzx ecx,word ptr [ebp].TsEsi or eax,ecx push eax ; address mov eax,1 xor ecx, ecx test ebx,PREFIX_REP ; prefixREP jz oosb20 mov ecx, 1 movzx eax,word ptr [ebp].TsEcx oosb20: push eax ; number of io ops push FALSE ; write op push ecx ; REP prefix ? push 1 ; byte op movzx eax,word ptr [ebp].TsEdx push eax ; port number ; Ki386VdmDispatchStringIo enables interrupts IFDEF STD_CALL call _Ki386VdmDispatchStringIo@32 ; use retval ELSE call _Ki386VdmDispatchStringIo ; use retval add esp,24 ENDIF ret OpcodeOUTSBV86 endp page ,132 subttl "Word String Out Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTSW opcode. Currently, it prints ; a message, and ignores the instruction ; ; Arguments: ; ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeOUTSWV86 OpcodeOUTSWV86 proc push ebp ; trap frame push edi ; size of outsw movzx eax,word ptr [ebp].TsV86Ds shl eax,16 movzx ecx,word ptr [ebp].TsEsi or eax,ecx push eax ; address mov eax,1 xor ecx, ecx test ebx,PREFIX_REP ; prefixREP jz oosw20 mov ecx, 1 movzx eax,word ptr [ebp].TsEcx oosw20: push eax ; number of io ops push FALSE ; write op push ecx ; REP prefix ? push 2 ; word op movzx eax,word ptr [ebp].TsEdx push eax ; port number ; Ki386VdmDispatchStringIo enables interrupts IFDEF STD_CALL call _Ki386VdmDispatchStringIo@32 ; use retval ELSE call _Ki386VdmDispatchStringIo ; use retval add esp,24 ENDIF ret OpcodeOUTSWV86 endp page ,132 subttl "PUSHF Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an PUSHF opcode. Currently, it prints ; a message, and simulates the instruction. ; ; Get SS ; shift left 4 ; get SP ; subtract 2 ; get flags ; put in virtual interrupt flag ; put on stack ; update sp ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodePUSHFV86 OpcodePUSHFV86 proc test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz short puf00 mov eax,dword ptr [ebp].TsEFlags lea ecx,ds:FIXED_NTVDMSTATE_LINEAR or dword ptr [ecx], VDM_VIRTUAL_INTERRUPTS test eax, EFLAGS_VIF ; Is vif on jnz short puf03 and dword ptr [ecx], NOT VDM_VIRTUAL_INTERRUPTS and eax, NOT EFLAGS_INTERRUPT_MASK jmp short puf03 puf00: lea eax,ds:FIXED_NTVDMSTATE_LINEAR mov edx,dword ptr [ebp].TsEFlags mov eax, dword ptr [eax] ; get virtual int flag and edx,NOT EFLAGS_INTERRUPT_MASK and eax,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC OR VDM_VIRTUAL_NT or eax,edx or eax,EFLAGS_IOPL_MASK puf03: movzx ecx,word ptr [ebp].TsHardwareSegSS movzx edx,word ptr [ebp].TsHardwareEsp shl ecx,4 sub dx,2 test ebx,PREFIX_OPER32 ; check operand size jnz puf10 mov [ecx + edx],ax puf05: mov word ptr [ebp].TsHardwareEsp,dx ; update client esp add dword ptr [ebp].TsEip,edi mov eax, ds:FIXED_NTVDMSTATE_LINEAR test eax, VDM_VIRTUAL_INTERRUPTS jz short @f test eax, VDM_INTERRUPT_PENDING jz short @f call VdmDispatchIntAck @@: mov eax,1 ret puf10: sub dx,2 mov [ecx + edx],eax jmp puf05 OpcodePUSHFV86 endp page ,132 subttl "POPF Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an POPF opcode. Currently, it prints ; a message, and returns to the monitor. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodePOPFV86 OpcodePOPFV86 proc lea eax,ds:FIXED_NTVDMSTATE_LINEAR ; get pointer to VDM State mov ecx,[ebp].TsHardwareSegSS movzx edx,word ptr [ebp].TsHardwareEsp shl ecx,4 mov ecx,[ecx + edx] ; get flags from stack => ecx add edx,4 test ebx,PREFIX_OPER32 ; check operand size jnz pof10 and ecx,0ffffh ; only lower 16 bit for 16bit code sub edx,2 pof10: mov [ebp].TsHardwareEsp,edx and ecx, NOT EFLAGS_IOPL_MASK mov ebx,ecx ; [ebx]=[ecx]=user EFLAGS - IOPL and ebx, NOT EFLAGS_NT_MASK ; [ebx]=user eflags - iopl - NT and ecx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK) ; [ecx]=IF + AC + NT of User eflgs ; [ebx]=User eflgs - IOPL - NT test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz short pof15 and ebx, NOT (EFLAGS_VIP + EFLAGS_VIF) ; [ebx]=UserFlg -IOPL - NT - VIP - VIF test ebx, EFLAGS_INTERRUPT_MASK jz short @f or ebx, EFLAGS_VIF ; [ebx]=UserFlg-IOPL-NT-VIP+VIF @@: or ebx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK) ;[ebx]=UserFlg-IOPL-NT-VIP+VIF+IF and dword ptr [ebp].TsEFlags, EFLAGS_VIP or [ebp].TsEFlags,ebx jmp short pof20 pof15: or ebx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK) mov [ebp].TsEFlags, ebx pof20: MPLOCK and [eax],NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK) MPLOCK or [eax],ecx add dword ptr [ebp].TsEip,edi mov eax,dword ptr [eax] test eax,VDM_INTERRUPT_PENDING jz pof25 test eax,VDM_VIRTUAL_INTERRUPTS jz pof25 call VdmDispatchIntAck pof25: mov eax,1 ; handled ret OpcodePOPFV86 endp page ,132 subttl "INTnn Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INTnn opcode. It retrieves the handler ; from the IVT, pushes the current cs:ip and flags on the stack, ; and dispatches to the handler. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINTnnV86 OpcodeINTnnV86 proc ; ; Int nn in v86 mode always disables interrupts ; mov edx,[ebp].TsEflags ; ; If KeI386VdmIoplAllowed is true, direct IF manipulation is allowed ; test _KeI386VdmIoplAllowed,1 jz oinnv10 mov eax,edx ; save original flags and edx,NOT EFLAGS_INTERRUPT_MASK jmp oinnv20 ; ; Else, IF and some other flags bits are virtualized ; oinnv10: lea eax,ds:FIXED_NTVDMSTATE_LINEAR ; get pointer to VDM State mov ecx,dword ptr [eax] ;[ecx]=vdmstate MPLOCK and [eax],NOT VDM_VIRTUAL_INTERRUPTS mov eax, edx and eax, NOT EFLAGS_INTERRUPT_MASK .errnz (EFLAGS_INTERRUPT_MASK - VDM_VIRTUAL_INTERRUPTS) and ecx, VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC ; [edx]=eflags ; [eax]=eflgs-if ; [ecx]=IF + AC of vdmstate test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz oinnv15 ; ;VIF extension is enabled, we should migrate EFLAGS_VIF instead of ;VDM_VIRTUAL_INTERRUPT to the iret frame eflags IF. ;When VIF extension is enabled, RI_BIT_MASK is turned on. This in turn, ;redirects the FCLI/FSTI macro to execute cli/sti directly instead ;of simulation. Without this, we might disable v86 mode interrupt ;without the applications knowing it. ; and ecx, VDM_VIRTUAL_AC ;keep VDM_VIRTUAL_AC only or eax, ecx ;[eax]=eflags + ac -if mov ecx, edx and ecx, EFLAGS_VIF .errnz ((EFLAGS_VIF SHR 10) - EFLAGS_INTERRUPT_MASK) ror ecx, 10 ;VIF -> IF oinnv15: or eax, ecx ;[eax]=eflags +ac +if oinnv20: and edx,NOT (EFLAGS_NT_MASK OR EFLAGS_TF_MASK OR EFLAGS_VIF) mov [ebp].TsEflags,edx or eax, EFLAGS_IOPL_MASK movzx ecx,word ptr [ebp].TsHardwareSegSS shl ecx,4 movzx edx,word ptr [ebp].TsHardwareEsp ; ecx+edx is user stack sub dx,2 mov word ptr [ecx+edx],ax ; push flags mov ax,word ptr [ebp].TsSegCS sub dx,2 mov word ptr [ecx+edx],ax ; push cs movzx eax,word ptr [ebp].TsEip add eax, edi inc eax sub dx,2 mov word ptr [ecx+edx],ax ; push ip mov [ebp].TsHardwareEsp,dx ; update sp on trap frame inc esi movzx ecx,byte ptr [esi] ; ecx is int# ; ; Check if this is a v86 interrupt which must be reflected to a PM handler ; call oinnvuserrefs ; do user refs under a try/except block or eax, eax je oinnv30 ; ; Encode interrupt number in cs ; mov eax,ebx shr eax,16 ; bop cs sub eax,ecx ; new cs shl ecx,4 add ebx,ecx ; new ip jmp oinnv40 oinnv30: ; ; Not hooked, just pick up new vector from RM IVT ; mov ebx,[ecx*4] mov eax,ebx shr eax,16 ; new cs oinnv40: mov word ptr [ebp].TsEip,bx mov [ebp].TsSegCs,ax ; cs:ip on trap frame is updated mov eax,1 ret OpcodeINTnnV86 endp oinnvuserrefs proc push ebp push esp ; Pass current Esp to handler push offset oinnvuserrefs_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov eax,PCR[PcTeb] mov eax,[eax].TeVdm ; get pointer to VdmTib cmp eax, _MmUserProbeAddress ; Probe the TeVdm jae @f mov ebx,[eax].VtInterruptTable ; cmp ebx, 0 ; there is no interrupt table je @f ; so, don't reflect it. lea ebx,[ebx + ecx*8] cmp ebx, _MmUserProbeAddress ; Probe the TeVdm jae @f test [ebx].ViFlags, VDM_INT_HOOKED ; need to reflect to PM? jz @f lea ebx,[eax].VtDpmiInfo ; point to DpmiInfo mov ebx,[ebx].VpDosxRmReflector ; bop to reflect to PM mov eax, 1 pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp ret @@: oinnvuserrefs_fault_resume: pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp xor eax, eax ret oinnvuserrefs_fault: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. We can take ; this short cut because we know that our handler is a leaf-node. ; mov esp, [esp+8] ; (esp)-> ExceptionList jmp oinnvuserrefs_fault_resume oinnvuserrefs endp page ,132 subttl "INTO Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INTO opcode. Currently, it prints ; a message, and reflects a GP fault to the debugger. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINTOV86 OpcodeINTOV86 proc xor eax,eax ; ret fail ret OpcodeINTOV86 endp page ,132 subttl "IRET Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an IRET opcode. It retrieves the flags, ; and new instruction pointer from the stack and puts them into ; the user context. ; ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeIRETV86 OpcodeIRETV86 proc lea eax,ds:FIXED_NTVDMSTATE_LINEAR movzx ecx,word ptr [ebp].TsHardwareSegSS movzx edx,word ptr [ebp].TsHardwareEsp ; ebx+edx is user stack shl ecx,4 add ecx,edx test ebx,PREFIX_OPER32 jnz irt50 ; normally not movzx edi,word ptr [ecx] ; get ip value mov [ebp].TsEip,edi movzx esi,word ptr [ecx+2] ; get cs value mov [ebp].TsSegCs,esi add edx,6 mov [ebp].TsHardwareEsp,edx ; update sp on trap frame movzx ebx,word ptr [ecx+4] ; get flag value irt10: ; [ebx]=UserFlgs and ebx, NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_VIP OR EFLAGS_VIF) mov ecx,ebx ; [ecx]=[ebx]=UserFlgs - IOPL - NT - VIP - VIF test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz short irt15 or ebx, EFLAGS_VIF test ebx, EFLAGS_INTERRUPT_MASK jnz irt15 and ebx, NOT EFLAGS_VIF ; [ebx] = UserFlgs - IOPL - NT - VIP irt15: or ebx, (EFLAGS_V86_MASK OR EFLAGS_INTERRUPT_MASK) and dword ptr [ebp].TsEFlags, EFLAGS_VIP or [ebp].TsEFlags, ebx ; update flags n trap frame and ecx, EFLAGS_INTERRUPT_MASK MPLOCK and [eax],NOT VDM_VIRTUAL_INTERRUPTS MPLOCK or [eax],ecx mov ebx,[eax] ; at this point esi is the cs and edi is the ip where v86 mode ; will return. Now we will check if this returning instruction ; is a bop. if so we will directly dispatch the bop from here ; saving a full round trip. This will be really helpful to ; com apps. shl esi,4 add esi,edi cmp esi, _MmUserProbeAddress ; Probe 32 bit value jbe @f mov esi, _MmUserProbeAddress @@: mov ax, word ptr [esi] cmp ax, 0c4c4h je irtbop test ebx,VDM_INTERRUPT_PENDING jz short irt25 test ebx,VDM_VIRTUAL_INTERRUPTS jz short irt25 call VdmDispatchIntAck ; VdmDispatchIntAck enables interrupts irt25: mov eax,1 ; handled ret ; ireting to a bop irtbop: stdCall _VdmDispatchBop, jmp short irt25 irt50: mov edi, [ecx] ; get ip value mov [ebp].TsEip,edi movzx esi,word ptr [ecx+4] ; get cs value mov [ebp].TsSegCs,esi add edx,12 mov [ebp].TsHardwareEsp,edx ; update sp on trap frame mov ebx, [ecx+8] ; get flag value jmp irt10 ; rejoin the common path OpcodeIRETV86 endp page ,132 subttl "In Byte Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an in byte immediate opcode. Currently, it ; prints a message, and ignores the instruction. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINBimmV86 OpcodeINBimmV86 proc inc esi inc edi movzx ecx,byte ptr [esi] ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeINBimmV86 endp page ,132 subttl "Word In Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an in word immediate opcode. Currently, it ; prints a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINWimmV86 OpcodeINWimmV86 proc inc esi inc edi movzx ecx,byte ptr [esi] ; edi - instruction size ; TRUE - read op ; 2 - word op ; ecx - port number ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeINWimmV86 endp page ,132 subttl "Out Byte Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an invalid opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeOUTBimmV86 OpcodeOUTBimmV86 proc inc edi inc esi movzx ecx,byte ptr [esi] ; edi - instruction size ; FALSE - write op ; 1 - byte op ; ecx - port # ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeOUTBimmV86 endp page ,132 subttl "Out Word Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an out word immediate opcode. Currently, ; it prints a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeOUTWimmV86 OpcodeOUTWimmV86 proc inc esi inc edi movzx ecx,byte ptr [esi] ; edi - instruction size ; FALSE - write op ; 2 - word op ; ecx - port number ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeOUTWimmV86 endp page ,132 subttl "INB Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INB opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeINBV86 OpcodeINBV86 proc movzx ebx,word ptr [ebp].TsEdx ; edi - instruction size ; TRUE - read op ; 1 - byte op ; ebx - port number cmp ebx, 3bdh jz oib_prt1 cmp ebx, 379h jz oib_prt1 cmp ebx, 279h jz oib_prt1 oib_reflect: ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret oib_prt1: ; call printer status routine with port number, size, trap frame stdCall _VdmPrinterStatus, or al,al jz short oib_reflect ret OpcodeINBV86 endp page ,132 subttl "INW Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INW opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeINWV86 OpcodeINWV86 proc movzx ebx,word ptr [ebp].TsEdx ; edi - instruction size ; TRUE - read operation ; 2 - word op ; ebx - port number ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeINWV86 endp page ,132 subttl "OUTB Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTB opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeOUTBV86 OpcodeOUTBV86 proc movzx ebx,word ptr [ebp].TsEdx cmp ebx, 3bch jz oob_prt1 cmp ebx, 378h jz oob_prt1 cmp ebx, 278h jz oob_prt1 oob_reflect: ; edi - instruction size ; FALSE - write op ; 1 - byte op ; ebx - port number ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret oob_prt1: ; call printer write data routine with port number, size, trap frame stdCall _VdmPrinterWriteData, or al,al jz short oob_reflect ;al already has TRUE ret OpcodeOUTBV86 endp page ,132 subttl "OUTW Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTW opcode. Currently, it prints ; a message, and ignores the instruction. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeOUTWV86 OpcodeOUTWV86 proc movzx ebx,word ptr [ebp].TsEdx ; edi - instruction size ; FALSE - write op ; 2 - word op ; ebx - port # ; Ki386VdmDispatchIo enables interrupts stdCall _Ki386VdmDispatchIo, ret OpcodeOUTWV86 endp page ,132 subttl "CLI Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an CLI opcode. Currently, it prints ; a message, and clears the virtual interrupt flag in the VdmTeb. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeCLIV86 OpcodeCLIV86 proc lea eax,ds:FIXED_NTVDMSTATE_LINEAR test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz short oc50 mov edx, [ebp].TsEFlags ; redundant code. Just in case mov eax,dword ptr [eax] and edx, EFLAGS_VIF + EFLAGS_VIP cmp edx, EFLAGS_VIF + EFLAGS_VIP jnz short oc50 test eax,VDM_INTERRUPT_PENDING jz short oc50 call VdmDispatchIntAck mov eax,1 ret oc50: MPLOCK and dword ptr [eax],NOT VDM_VIRTUAL_INTERRUPTS add dword ptr [ebp].TsEip,edi mov eax,1 ret OpcodeCLIV86 endp page ,132 subttl "STI Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an STI opcode. Currently, it prints ; a message, and sets the virtual interrupt flag in the VDM teb. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; ; public OpcodeSTIV86 OpcodeSTIV86 proc lea eax,ds:FIXED_NTVDMSTATE_LINEAR ; get pointer to VDM State test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz short os10 or [ebp].TsEFlags, dword ptr EFLAGS_VIF os10: MPLOCK or dword ptr [eax],EFLAGS_INTERRUPT_MASK os20: add dword ptr [ebp].TsEip,edi mov eax,dword ptr [eax] test eax,VDM_INTERRUPT_PENDING jz short os30 call VdmDispatchIntAck os30: mov eax,1 ret OpcodeSTIV86 endp ; ; If we get here, we have executed an NPX instruction in user mode ; with the emulator installed. If the EM bit was not set in CR0, the ; app really wanted to execute the instruction for detection purposes. ; In this case, we need to clear the TS bit, and restart the instruction. ; Otherwise we need to reflect the exception ; ; ; Reginfo structure ; public Opcode0FV86 Opcode0FV86 proc RI equ [ebp - REGINFOSIZE] push ebp mov ebp,esp sub esp,REGINFOSIZE push esi push edi ; Initialize RegInfo do10: mov esi,[ebp] ; initialize rest of the trap from which was'nt initialized for ; v86 mode mov eax, [esi].TsV86Es mov [esi].TsSegEs,eax mov eax, [esi].TsV86Ds mov [esi].TsSegDs,eax mov eax, [esi].TsV86Fs mov [esi].TsSegFs,eax mov eax, [esi].TsV86Gs mov [esi].TsSegGs,eax mov RI.RiTrapFrame,esi mov eax,[esi].TsHardwareSegSs mov RI.RiSegSs,eax mov eax,[esi].TsHardwareEsp mov RI.RiEsp,eax mov eax,[esi].TsEFlags mov RI.RiEFlags,eax mov eax,[esi].TsSegCs mov RI.RiSegCs,eax mov eax,[esi].TsEip dec edi add eax,edi ; for prefixes mov RI.RiEip,eax mov RI.RiPrefixFlags,ebx lea esi,RI CsToLinearV86 call VdmOpcode0f ; enables interrupts test eax,0FFFFh jz do20 mov edi,RI.RiTrapFrame mov eax,RI.RiEip ; advance eip mov [edi].TsEip,eax do19: mov eax,1 do20: pop edi pop esi mov esp,ebp pop ebp ret Opcode0FV86 endp ;++ ; ; Routine Description: VdmDispatchIntAck ; pushes stack arguments for VdmDispatchInterrupts ; and invokes VdmDispatchInterrupts ; ; Expects VDM_INTERRUPT_PENDING, and VDM_VIRTUAL_INTERRUPTS ; ; Arguments: ; EBP -> trap frame ; ; Returns: ; nothing ; ; public VdmDispatchIntAck VdmDispatchIntAck proc push ebp push esp ; Pass current Esp to handler push offset diafault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on test ds:FIXED_NTVDMSTATE_LINEAR, VDM_INT_HARDWARE ; check interrupt int mov eax,PCR[PcTeb] mov eax,[eax].TeVdm ; get pointer to VdmTib jz short dia20 cmp eax, _MmUserProbeAddress ; check if user address jae short dia10 ; if ae, then not user address pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp ; ; dispatch hardware int directly from kernel ; stdCall _VdmDispatchInterrupts, ; TrapFrame, VdmTib ret dia10: pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp ret ; ; Switch to monitor context to dispatch timer int ; dia20: cmp eax, _MmUserProbeAddress ; check if user address jae dia10 ; if ae, then not user address mov dword ptr [eax].VtEIEvent,VdmIntAck ; mov dword ptr [eax].VtEIInstSize,0 mov dword ptr [eax].VtEiIntAckInfo,0 pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp stdCall _VdmEndExecution, ; TrapFrame, VdmTib ret diafault: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. We can take ; this short cut because we know that our handler is a leaf-node. ; mov esp, [esp+8] ; (esp)-> ExceptionList pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp ret VdmDispatchIntAck endp public vdmDebugPoint vdmDebugPoint proc ret vdmDebugPoint endp page ,132 subttl "HLT Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an HLT opcode. If the halt instruction is ; followed by the magic number (to be found in a crackerjack box), ; we use the hlt + magic number as a prefix, and emulate the following ; instruction. This allows code running in segmented protected mode to ; access the virtual interrupt flag. ; ; Arguments: ; EAX -> pointer to vdm state in DOS arena ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; interrupts disabled ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; public OpcodeHLTV86 OpcodeHLTV86 proc add dword ptr [ebp].TsEip,edi mov eax,1 ret OpcodeHLTV86 endp _PAGE ends _TEXT$00 SEGMENT DWORD PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING subttl "NPX Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates all NPX opcodes, when the system ; has the R3 emulator installed and the c86 apps takes a ; trap07. ; ; Arguments: ; EBX -> prefix flags ; EBP -> trap frame ; CL -> byte at the faulting address ; ESI -> address of faulting instruction ; EDI -> instruction length count ; ; Returns: ; EAX = 0 for failure ; EAX = 1 for success ; ; All registers can be trashed except ebp/esp. ; moved from emv86.asm as it must be non-pagable public OpcodeNPXV86 OpcodeNPXV86 proc mov edx, PCR[PcInitialStack] mov edx, [edx].FpCr0NpxState test edx, CR0_EM ; Does app want NPX traps? jnz short onp40 ; MP bit can never be set while the EM bit is cleared, so we know ; the faulting instruction is not an FWAIT onp30: and ebx, PREFIX_ADDR32 stdCall _VdmSkipNpxInstruction, or al, al ; was it handled? jnz short onp60 ; no, go raise exception to app onp40: stdCall _Ki386VdmReflectException, <7> ; trap # onp60: mov eax,1 ret OpcodeNPXV86 endp ;++ KiVdmSetUserCR0 ; ; eax ; public KiVdmSetUserCR0 KiVdmSetUserCR0 proc and eax, CR0_MP OR CR0_EM ; Sanitize parameter shr eax, 1 movzx eax, _VdmUserCr0MapIn[eax] push esp ; Pass current Esp to handler push offset scr_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov edx,PCR[PcTeb] mov edx,[edx].TeVdm ; get pointer to VdmTib cmp edx, _MmUserProbeAddress ; probe the pointer jbe @f mov edx, _MmUserProbeAddress ; make us AV @@: mov [edx].VtVdmContext.CsFloatSave.FpCtxtCr0NpxState, eax scr10: pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack mov edx, PCR[PcInitialStack] ; Get fp save area mov ebx, PCR[PcPrcbData + PbCurrentThread] ; (ebx) = current thread scr20: cli ; sync with context swap and [edx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_PE) or [edx].FpCr0NpxState,eax ; set fp save area bits mov eax,cr0 and eax, NOT (CR0_MP+CR0_EM+CR0_TS) ; turn off bits we will change or al, [ebx].ThNpxState ; set scheduler bits or eax,[edx].FpCr0NpxState ; set user's bits mov cr0,eax sti ret scr_fault: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. We can take ; this short cut because we know that our handler is a leaf-node. ; mov esp, [esp+8] ; (esp)-> ExceptionList jmp short scr10 KiVdmSetUserCR0 endp _TEXT$00 ENDS end