title "Vdm Instuction Emulation" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; instemul.asm ; ; Abstract: ; ; This module contains the routines for emulating instructions and ; faults to a VDM. ; ; Author: ; ; Dave Hastings (daveh) 29-March-1991 ; ; Environment: ; ; Kernel mode only. ; ; Notes: ; ; ;sudeepb 09-Dec-1992 Very Sonn this file will be deleted and protected ; mode instruction emulation will be merged in ; emv86.asm. Particularly following routines will ; simply become OpcodeInvalid. ; OpcodeIret ; OpcodePushf ; OpcodePopf ; OpcodeHlt ; Other routines such as ; OpcodeCli ; OpcodeSti ; OpcodeIN/OUT/SB/Immb etc ; will map exactly like emv86.asm ; OpcodeInt will be the main differeing routine. ; ; OpcodeDispatch Table will be deleted. ; ; So before making any major changes in this file please see ; Sudeepb or Daveh. ; ;neilsa 19-Oct-1993 Size and performance enhancements ;jonle 15-Nov-1993 - The Debug messages for each opcode may no longer work ; correctly, because interrupts may not have been enabled ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; 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 OpcodeNPXV86:proc extrn VdmDispatchIntAck:proc ;; only OpcodeSti uses this ifdef VDMDBG EXTRNP _VdmTraceEvent,4 endif extrn CommonDispatchException:proc ;; trap.asm extrn _DbgPrint:proc extrn _KeI386VdmIoplAllowed:dword extrn _KeI386VirtualIntExtensions:dword extrn _MmHighestUserAddress:dword EXTRNP _Ki386GetSelectorParameters,4 EXTRNP _Ki386VdmDispatchIo,5 EXTRNP _Ki386VdmDispatchStringIo,8 EXTRNP _KiDispatchException,5 EXTRNP _VdmPrinterStatus,3 EXTRNP KfLowerIrql,1,IMPORT, FASTCALL EXTRNP _VdmPrinterWriteData, 3 EXTRNP _VdmClearPMCliTimeStamp, 0 EXTRNP _VdmSetPMCliTimeStamp, 1 extrn _MmUserProbeAddress:DWORD EXTRNP _VdmFetchULONG,1 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 PAGECONST SEGMENT DWORD PUBLIC 'DATA' ; ; Instruction emulation emulates the following instructions. ; The emulation affects the noted user mode registers. ; ; In protected mode, the following instructions are emulated in the kernel ; ; Registers (E)Flags (E)SP SS CS ; INTnn X X X X ; INTO X X X X ; CLI X ; STI X ; ; The following instructions are always emulated by reflection to the ; Usermode VDM monitor ; ; INSB ; INSW ; OUTSB ; OUTSW ; INBimm ; INWimm ; OUTBimm ; OUTWimm ; INB ; INW ; OUTB ; OUTW ; ; WARNING What do we do about 32 bit io instructions?? ; ; OpcodeIndex - packed 1st level table to index OpcodeDispatch table ; public OpcodeIndex diBEGIN OpcodeIndex,VDM_INDEX_Invalid dtI 0fh, VDM_INDEX_0F dtI 26h, VDM_INDEX_ESPrefix dtI 2eh, VDM_INDEX_CSPrefix dtI 36h, VDM_INDEX_SSPrefix dtI 3eh, VDM_INDEX_DSPrefix dtI 64h, VDM_INDEX_FSPrefix dtI 65h, VDM_INDEX_GSPrefix dtI 66h, VDM_INDEX_OPER32Prefix dtI 67h, VDM_INDEX_ADDR32Prefix dtI 6ch, VDM_INDEX_INSB dtI 6dh, VDM_INDEX_INSW dtI 6eh, VDM_INDEX_OUTSB dtI 6fh, VDM_INDEX_OUTSW dtI 9bh, VDM_INDEX_NPX dtI 9ch, VDM_INDEX_PUSHF dtI 9dh, VDM_INDEX_POPF dtI 0cdh, VDM_INDEX_INTnn dtI 0ceh, VDM_INDEX_INTO dtI 0cfh, VDM_INDEX_IRET dtI 0d8h, VDM_INDEX_NPX dtI 0d9h, VDM_INDEX_NPX dtI 0dah, VDM_INDEX_NPX dtI 0dbh, VDM_INDEX_NPX dtI 0dch, VDM_INDEX_NPX dtI 0ddh, VDM_INDEX_NPX dtI 0deh, VDM_INDEX_NPX dtI 0dfh, VDM_INDEX_NPX dtI 0e4h, VDM_INDEX_INBimm dtI 0e5h, VDM_INDEX_INWimm dtI 0e6h, VDM_INDEX_OUTBimm dtI 0e7h, VDM_INDEX_OUTWimm dtI 0ech, VDM_INDEX_INB dtI 0edh, VDM_INDEX_INW dtI 0eeh, VDM_INDEX_OUTB dtI 0efh, VDM_INDEX_OUTW dtI 0f0h, VDM_INDEX_LOCKPrefix dtI 0f2h, VDM_INDEX_REPNEPrefix dtI 0f3h, VDM_INDEX_REPPrefix dtI 0f4h, VDM_INDEX_HLT dtI 0fah, VDM_INDEX_CLI dtI 0fbh, VDM_INDEX_STI diEND NUM_OPCODE ; ; OpcodeDispatch - table of routines used to emulate instructions ; public OpcodeDispatch dtBEGIN OpcodeDispatch,OpcodeInvalid dtS VDM_INDEX_0F , Opcode0F dtS VDM_INDEX_ESPrefix , OpcodeESPrefix dtS VDM_INDEX_CSPrefix , OpcodeCSPrefix dtS VDM_INDEX_SSPrefix , OpcodeSSPrefix dtS VDM_INDEX_DSPrefix , OpcodeDSPrefix dtS VDM_INDEX_FSPrefix , OpcodeFSPrefix dtS VDM_INDEX_GSPrefix , OpcodeGSPrefix dtS VDM_INDEX_OPER32Prefix, OpcodeOPER32Prefix dtS VDM_INDEX_ADDR32Prefix, OpcodeADDR32Prefix dtS VDM_INDEX_INSB , OpcodeINSB dtS VDM_INDEX_INSW , OpcodeINSW dtS VDM_INDEX_OUTSB , OpcodeOUTSB dtS VDM_INDEX_OUTSW , OpcodeOUTSW dtS VDM_INDEX_INTnn , OpcodeINTnn dtS VDM_INDEX_INTO , OpcodeINTO dtS VDM_INDEX_INBimm , OpcodeINBimm dtS VDM_INDEX_INWimm , OpcodeINWimm dtS VDM_INDEX_OUTBimm , OpcodeOUTBimm dtS VDM_INDEX_OUTWimm , OpcodeOUTWimm dtS VDM_INDEX_INB , OpcodeINB dtS VDM_INDEX_INW , OpcodeINW dtS VDM_INDEX_OUTB , OpcodeOUTB dtS VDM_INDEX_OUTW , OpcodeOUTW dtS VDM_INDEX_LOCKPrefix , OpcodeLOCKPrefix dtS VDM_INDEX_REPNEPrefix , OpcodeREPNEPrefix dtS VDM_INDEX_REPPrefix , OpcodeREPPrefix dtS VDM_INDEX_CLI , OpcodeCLI dtS VDM_INDEX_STI , OpcodeSTI dtEND MAX_VDM_INDEX PAGECONST ENDS PAGEDATA SEGMENT DWORD PUBLIC 'DATA' public _ExVdmOpcodeDispatchCounts,_ExVdmSegmentNotPresent _ExVdmOpcodeDispatchCounts dd MAX_VDM_INDEX dup(0) _ExVdmSegmentNotPresent dd 0 PAGEDATA ENDS _PAGE SEGMENT DWORD 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 ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; 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&Prefix Opcode&name&Prefix proc or [esi].RiPrefixFlags,PREFIX_&name jmp OpcodeGenericPrefix ; dispatch to next handler Opcode&name&Prefix endp endm irp prefix, opPrefix prefix endm page ,132 subttl "Instruction Emulation Dispatcher" ;++ ; ; 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. ; ; Arguments: ; ; ebp = pointer to trap frame ; ; Returns: ; ; Nothing ; ; cPublicProc _Ki386DispatchOpcode,0 sub esp,REGINFOSIZE mov esi, esp ; scratch area CsToLinearPM [ebp].TsSegCs, doerr ; initialize reginfo mov edi,[ebp].TsEip ; get fault instruction address cmp edi,[esi].RiCsLimit ; check eip ja doerr add edi,[esi].RiCsBase cmp edi, [_MmHighestUserAddress] ja doerr movzx ecx,byte ptr [edi] ; get faulting opcode mov eax,ecx and eax,0F8h ; check for npx instr cmp eax,0D8h je do30 ; dispatch movzx eax, OpcodeIndex[ecx] mov ebx,1 ; length count, flags ; All handler routines will get the following on entry ; ebp -> trap frame ; ebx -> prefix flags, instruction length count ; ecx -> byte at the faulting address ; edx -> pointer to vdm state in DOS arena ; interrupts enabled and Irql at APC level ; edi -> address of faulting instruction ; esi -> reginfo struct ; All handler routines will return ; EAX = 0 for failure ; EAX = 1 for success if DEVL inc _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts] endif ifdef VDMDBG pushad stdCall _VdmTraceEvent, popad endif call OpcodeDispatch[eax * type OpcodeDispatch] do20: add esp,REGINFOSIZE stdRET _Ki386DispatchOpcode doerr: xor eax,eax jmp do20 ; ; 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 ; do30: call OpcodeNPXV86 jmp short do20 stdENDP _Ki386DispatchOpcode page ,132 subttl "Invalid Opcode Handler" ;++ ; ; Routine Description: ; ; This routine causes a GP fault to be reflected to the vdm ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeInvalid OpcodeInvalid proc xor eax,eax ; ret fail ret OpcodeInvalid 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: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; user mode Eip advanced ; edx contains next byte of opcode ; public OpcodeGenericPrefix OpcodeGenericPrefix proc inc edi ; increment eip inc ebx ; increment size cmp bl, 128 ; set arbitrary inst size limit ja ogperr ; in case of pointless prefixes mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja ogperr cmp edi, [_MmHighestUserAddress] ja ogperr mov cl,byte ptr [edi] ; get next opcode movzx eax, OpcodeIndex[ecx] if DEVL inc _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts] endif jmp OpcodeDispatch[eax * type OpcodeDispatch] ogperr: xor eax,eax ; opcode was NOT handled ret OpcodeGenericPrefix endp page ,132 subttl "0F Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates a 0Fh opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public Opcode0F Opcode0F proc mov eax,[ebp].TsEip ; get fault instruction address mov [esi].RiEip,eax mov [esi].RiTrapFrame,ebp mov [esi].RiPrefixFlags,ebx mov eax,dword ptr [ebp].TsEFlags mov [esi].RiEFlags,eax call VdmOpcode0F ; enables interrupts test eax,0FFFFh jz o0f20 mov eax,[esi].RiEip mov [ebp].TsEip,eax mov eax,1 o0f20: ret Opcode0F endp page ,132 subttl "Byte string in Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INSB opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; ; WARNING what to do about size override? ds override? public OpcodeINSB OpcodeINSB proc push ebp ; Trap Frame push ebx ; size of insb movzx eax,word ptr [ebp].TsSegEs shl eax,16 ; WARNING no support for 32bit edi mov ax,word ptr [ebp].TsEdi ; don't support 32bit'ness push eax ; address xor eax, eax mov ecx,1 test ebx,PREFIX_REP jz @f mov eax, 1 ; WARNING no support for 32bit ecx movzx ecx,word ptr [ebp].TsEcx @@: push ecx ; number of io ops push TRUE ; read op push eax ; REP prefix push 1 ; byte op movzx edx,word ptr [ebp].TsEdx push edx ; port number call _Ki386VdmDispatchStringIo@32 ; use retval ret OpcodeINSB endp page ,132 subttl "Word String In Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INSW opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINSW OpcodeINSW proc push ebp ; Trap frame push ebx ; sizeof insw movzx eax,word ptr [ebp].TsSegEs shl eax,16 ; WARNING no support for 32bit edi mov ax,word ptr [ebp].TsEdi push eax ; address xor eax, eax mov ecx,1 test ebx,PREFIX_REP jz @f mov eax, 1 ; WARNING no support for 32bit ecx movzx ecx,word ptr [ebp].TsEcx @@: movzx edx,word ptr [ebp].TsEdx push ecx ; number of io ops push TRUE ; read op push eax ; REP prefix push 2 ; word size push edx ; port number call _Ki386VdmDispatchStringIo@32 ; use retval ret OpcodeINSW endp page ,132 subttl "Byte String Out Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTSB opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTSB OpcodeOUTSB proc push ebp ; Trap Frame push ebx ; size of outsb movzx eax,word ptr [ebp].TsSegDs shl eax,16 ; WARNING don't support 32bit'ness, esi mov ax,word ptr [ebp].TsEsi push eax ; address xor eax, eax mov ecx,1 test ebx,PREFIX_REP jz @f mov eax, 1 ; WARNING don't support 32bit'ness ecx movzx ecx,word ptr [ebp].TsEcx @@: movzx edx,word ptr [ebp].TsEdx push ecx ; number of io ops push FALSE ; write op push eax ; REP prefix push 1 ; byte op push edx ; port number call _Ki386VdmDispatchStringIo@32 ; use retval ret OpcodeOUTSB endp page ,132 subttl "Word String Out Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTSW opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTSW OpcodeOUTSW proc push ebp ; Trap Frame push ebx ; size of outsb movzx eax,word ptr [ebp].TsSegDs shl eax,16 ; WARNING don't support 32bit'ness esi mov ax,word ptr [ebp].TsEsi push eax ; address xor eax, eax mov ecx,1 test ebx,PREFIX_REP jz @f mov eax, 1 ; WARNING don't support 32bit'ness ecx movzx ecx,word ptr [ebp].TsEcx @@: movzx edx,word ptr [ebp].TsEdx push ecx ; number of io ops push FALSE ; write op push eax ; REP prefix push 2 ; byte op push edx ; port number call _Ki386VdmDispatchStringIo@32 ; use retval ret OpcodeOUTSW 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: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; Current CS:IP on user stack ; RiCs:RiEip -> handler from IVT ; public OpcodeINTnn OpcodeINTnn proc mov eax, ds:FIXED_NTVDMSTATE_LINEAR and eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS) cmp eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS) jnz short oi10 call VdmDispatchIntAck jmp short oi99 oi10: mov eax,dword ptr [ebp].TsEFlags call GetVirtualBits ; set interrupt flag mov [esi].RiEFlags,eax movzx eax,word ptr [ebp].TsHardwareSegSs call SsToLinear test al,0FFh jz oinerr inc edi ; point to int # mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja oinerr cmp edi, [_MmHighestUserAddress] ja oinerr movzx ecx,byte ptr [edi] ; get int # inc eax ; inc past end of instruction mov [esi].RiEip,eax ; save for pushint's benefit call PushInt ; will return retcode in al test al,0FFh jz oinerr ; error! mov eax,[esi].RiEsp mov [ebp].TsHardwareEsp,eax mov ax,word ptr [esi].RiSegCs or ax, 7 ; R3 LDT selectors only mov word ptr [ebp].TsSegCs,ax mov eax,[esi].RiEFlags mov [ebp].TsEFlags,eax mov eax,[esi].RiEip mov [ebp].TsEip,eax oi99: mov eax,1 ret oinerr: xor eax,eax ret OpcodeINTnn endp page ,132 subttl "INTO Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INTO opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINTO OpcodeINTO proc xor eax,eax ret OpcodeINTO endp page ,132 subttl "In Byte Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an in byte immediate opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINBimm OpcodeINBimm proc inc ebx ; length count inc edi mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja oibi20 cmp edi, [_MmHighestUserAddress] ja oibi20 movzx ecx,byte ptr [edi] ; (eax) = inst. size ; read op ; I/O size = 1 ; (ecx) = port number stdCall _Ki386VdmDispatchIo, ret oibi20: xor eax, eax ; not handled ret OpcodeINBimm endp page ,132 subttl "Word In Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an in word immediate opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINWimm OpcodeINWimm proc inc ebx ; length count inc edi mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja oiwi20 cmp edi, [_MmHighestUserAddress] ja oiwi20 movzx ecx,byte ptr [edi] ; TRUE - read op ; 2 - word op ; ecx - port number stdCall _Ki386VdmDispatchIo, ret oiwi20: xor eax, eax ; not handled ret OpcodeINWimm endp page ,132 subttl "Out Byte Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an invalid opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTBimm OpcodeOUTBimm proc inc ebx ; length count inc edi mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja oobi20 cmp edi, [_MmHighestUserAddress] ja oobi20 movzx ecx,byte ptr [edi] ; FALSE - write op ; 1 - byte op ; ecx - port # stdCall _Ki386VdmDispatchIo, ret oobi20: xor eax, eax ; not handled ret OpcodeOUTBimm endp page ,132 subttl "Out Word Immediate Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an out word immediate opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTWimm OpcodeOUTWimm proc inc ebx ; length count inc edi mov eax,edi ; current linear address sub eax,[esi].RiCsBase ; make address eip cmp eax,[esi].RiCsLimit ; check eip ja oowi20 cmp edi, [_MmHighestUserAddress] ja oowi20 movzx ecx,byte ptr [edi] ; FALSE - write op ; 2 - word op ; ecx - port number stdCall _Ki386VdmDispatchIo, ret oowi20: xor eax, eax ; not handled ret OpcodeOUTWimm endp page ,132 subttl "INB Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INB opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINB OpcodeINB proc movzx eax,word ptr [ebp].TsEdx ; TRUE - read op ; 1 - byte op ; eax - port number cmp eax, 3bdh jz oib_prt1 cmp eax, 379h jz oib_prt1 cmp eax, 279h jz oib_prt1 oib_reflect: stdCall _Ki386VdmDispatchIo, ret oib_prt1: ; call printer status routine with port number, size, trap frame movzx ebx, bl ;clear prefix flags push eax stdCall _VdmPrinterStatus, or al,al pop eax jz short oib_reflect mov al, 1 ret OpcodeINB endp page ,132 subttl "INW Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an INW opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeINW OpcodeINW proc movzx eax,word ptr [ebp].TsEdx ; TRUE - read operation ; 2 - word op ; eax - port number stdCall _Ki386VdmDispatchIo, ret OpcodeINW endp page ,132 subttl "OUTB Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTB opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTB OpcodeOUTB proc movzx eax,word ptr [ebp].TsEdx cmp eax, 03BCh je short oob_printerVDD cmp eax, 0378h je short oob_printerVDD cmp eax, 0278h jz short oob_printerVDD oob_reflect: ; FALSE - write op ; 1 - byte op ; eax - port number stdCall _Ki386VdmDispatchIo, ret oob_printerVDD: movzx ebx, bl ; instruction size push eax ; save port address stdCall _VdmPrinterWriteData, or al,al ; pop eax jz short oob_reflect mov al, 1 ret OpcodeOUTB endp page ,132 subttl "OUTW Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an OUTW opcode. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeOUTW OpcodeOUTW proc movzx eax,word ptr [ebp].TsEdx ; FALSE - write op ; 2 - word op ; edi - port # stdCall _Ki386VdmDispatchIo, ret OpcodeOUTW endp page ,132 subttl "CLI Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an CLI opcode. It clears the virtual ; interrupt flag in the VdmTeb. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeCLI OpcodeCLI proc mov eax, ds:FIXED_NTVDMSTATE_LINEAR and eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS) cmp eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS) jnz short oc50 call VdmDispatchIntAck jmp short oc99 oc50: mov eax,[ebp].TsEFlags and eax,NOT EFLAGS_INTERRUPT_MASK call SetVirtualBits inc dword ptr [ebp].TsEip stdCall _VdmSetPMCliTimeStamp, <0> oc99: mov eax,1 ret OpcodeCLI endp page ,132 subttl "STI Opcode Handler" ;++ ; ; Routine Description: ; ; This routine emulates an STI opcode. It sets the virtual ; interrupt flag in the VDM teb. ; ; Arguments: ; EBP -> trap frame ; EBX -> prefix flags, BL = instruction length count ; ECX -> byte at the faulting address ; EDX -> pointer to vdm state in DOS arena ; ESI -> Reginfo struct ; EDI -> address of faulting instruction ; ; Returns: ; ; nothing ; public OpcodeSTI OpcodeSTI proc stdCall _VdmClearPMCliTimeStamp mov eax,[ebp].TsEFlags or eax,EFLAGS_INTERRUPT_MASK call SetVirtualBits inc dword ptr [ebp].TsEip mov eax, ds:FIXED_NTVDMSTATE_LINEAR test eax,VDM_INTERRUPT_PENDING jz os10 call VdmDispatchIntAck os10: mov eax,1 ret OpcodeSTI endp page ,132 subttl "Check Vdm Flags" ;++ ; ; Routine Description: ; ; This routine checks the flags that are going to be used for the ; dos or windows application. ; ; Arguments: ; ; ecx = EFlags to be set ; esi = address of reg info ; ; Returns: ; ; ecx = fixed flags ; public CheckVdmFlags CheckVdmFlags proc push eax mov eax,[esi].RiEFlags and eax,EFLAGS_V86_MASK test _KeI386VdmIoplAllowed,1 jnz cvf30 test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS jnz cvf40 cvf10: or ecx,EFLAGS_INTERRUPT_MASK cvf20: and ecx,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK OR EFLAGS_VIF OR EFLAGS_VIP) or ecx,eax ; restore original v86 bit pop eax ret cvf30: test eax,EFLAGS_V86_MASK jz cvf10 jmp cvf20 cvf40: test eax,EFLAGS_V86_MASK jz cvf60 test _KeI386VirtualIntExtensions,V86_VIRTUAL_INT_EXTENSIONS jz cvf10 cvf50: push eax mov eax,ecx and eax,EFLAGS_INTERRUPT_MASK shl eax,0ah pop eax jmp cvf10 cvf60: test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS jz cvf10 jmp cvf50 CheckVdmFlags endp page ,132 subttl "Get Virtual Interrupt Flag" ;++ ; ; Routine Description: ; ; This routine correctly gets the VDMs virtual interrupt flag and ; puts it into an EFlags image to be put on the stack. ; ; Arguments: ; ; eax = EFlags value ; ; Returns: ; ; eax = EFlags value with correct setting for IF ; ; Uses: ; ecx ; public GetVirtualBits GetVirtualBits proc push ebp push edx push ebx push esi push edi test _KeI386VdmIoplAllowed,1 jnz gvb60 test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS jnz gvb30 gvb10: and eax,NOT EFLAGS_INTERRUPT_MASK call gvbGetFixedStateLinear and ecx,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC or eax,ecx ; put virtual int flag into flags or eax,EFLAGS_IOPL_MASK ; make it look like a 386 gbvexit: pop edi pop esi pop ebx pop edx pop ebp ret gvb30: test eax, EFLAGS_V86_MASK jz gvb50 test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS jz gvb10 gvb40: mov ecx,eax and ecx,EFLAGS_VIF shr ecx,0ah ; mov vif to if posn and eax,NOT EFLAGS_INTERRUPT_MASK or eax,ecx call gvbGetFixedStateLinear and ecx,VDM_VIRTUAL_AC and eax,NOT EFLAGS_ALIGN_CHECK or eax,ecx or eax,EFLAGS_IOPL_MASK jmp gbvexit gvb50: test _KeI386VirtualIntExtensions, PM_VIRTUAL_INT_EXTENSIONS jz gvb10 jmp gvb40 gvb60: test eax,EFLAGS_V86_MASK jz gvb10 call gvbGetFixedStateLinear and ecx,VDM_VIRTUAL_AC and eax,NOT EFLAGS_ALIGN_CHECK or eax,ecx or eax,EFLAGS_IOPL_MASK jmp gbvexit gvbGetFixedStateLinear: push esp ; Pass current Esp to handler push offset GetVirtualBits_Handler push PCR[PcExceptionList] mov PCR[PcExceptionList], esp mov ecx, ds:FIXED_NTVDMSTATE_LINEAR gvbexit1: pop PCR[PcExceptionList] add esp, 8 ; pop out except handler ret GetVirtualBits_Handler: mov esp, [esp+8] ; (esp)-> ExceptionList xor ecx, ecx jmp gvbexit1 GetVirtualBits endp page ,132 subttl "Set Virtual Interrupt Flag" ;++ ; ; Routine Description: ; ; This routine correctly sets the VDMs virtual interrupt flag. ; ; Arguments: ; ; eax = EFlags value ; ; Returns: ; ; Virtual interrupt flag set ; public SetVirtualBits SetVirtualBits proc Flags equ [ebp - 4] push ebp push edx push ebx push esi push edi push esp ; Pass current Esp to handler push offset SetVirtualBits_Handler push PCR[PcExceptionList] mov PCR[PcExceptionList], esp mov ebp,esp sub esp,4 mov Flags,eax lea edx,ds:FIXED_NTVDMSTATE_LINEAR and eax,EFLAGS_INTERRUPT_MASK ; isolate int flag MPLOCK and [edx],NOT VDM_VIRTUAL_INTERRUPTS MPLOCK or [edx],eax ; place virtual int flag value test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS jnz svb40 svb20: ; WARNING 32 bit support! test ebx,PREFIX_OPER32 jz svb30 ; 16 bit instr mov eax,Flags and eax,EFLAGS_ALIGN_CHECK MPLOCK and dword ptr [edx],NOT EFLAGS_ALIGN_CHECK MPLOCK or [edx],eax svb30: mov esp,ebp svbexit: pop PCR[PcExceptionList] ; Remove handler lea esp, [esp+8] pop edi pop esi pop ebx pop edx pop ebp ret svb40: test Flags,dword ptr EFLAGS_V86_MASK jz svb60 test _KeI386VirtualIntExtensions,V86_VIRTUAL_INT_EXTENSIONS jz svb20 svb50: shl eax,0ah jmp svb20 svb60: test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS jz svb20 jmp svb50 SetVirtualBits_Handler: mov esp, [esp+8] ; (esp)-> ExceptionList jmp svbexit SetVirtualBits endp page ,132 subttl "Reflect Exception to a Vdm" ;++ ; ; Routine Description: ; ; This routine reflects an exception to a VDM. It uses the information ; in the trap frame to determine what exception to reflect, and updates ; the trap frame with the new CS, EIP, SS, and SP values ; ; Arguments: ; ; ebp -> Trap frame ; ss:esp + 4 = trap number ; ; Returns ; ; Nothing ; ; Notes: ; Interrupts are enabled upon entry, Irql is at APC level ; This routine may not preserve all of the non-volatile registers if ; a fault occurs. ; cPublicProc _Ki386VdmReflectException,1 RI equ [ebp - REGINFOSIZE] ; ; First make sure this is for us to handle ; mov eax,PCR[PcPrcbData+PbCurrentThread] mov eax,[eax]+ThApcState+AsProcess cmp dword ptr [eax]+PrVdmObjects,0 ; is this a vdm process? jne short @f xor eax, eax ; not handled ret @@: push ebp mov ebp,esp sub esp,REGINFOSIZE pushad lea esi,ds:FIXED_NTVDMSTATE_LINEAR ; ; Look to see if the debugger wants exceptions ; stdCall _VdmFetchULONG, test eax,VDM_BREAK_EXCEPTIONS jz vredbg ; no, check for debug events mov ebx,DBG_STACKFAULT cmp word ptr [ebp + 8],0ch ; stack fault? jz @f ; yes, check dbg flag mov ebx,DBG_GPFAULT cmp word ptr [ebp + 8],0dh ; gp fault? jne vredbg ; no, continue @@: test eax,VDM_USE_DBG_VDMEVENT jnz vrexc_event jmp vrexcd ; reflect the exception to 32 ; ; Look to see if the debugger wants debug events ; vredbg: test eax,VDM_BREAK_DEBUGGER jz vrevdm ; no debug events, reflect to vdm mov ebx,DBG_SINGLESTEP cmp word ptr [ebp + 8],1 jnz @f test eax,VDM_USE_DBG_VDMEVENT jnz vrexc_event jmp vrexc1 @@: mov ebx,DBG_BREAK cmp word ptr [ebp + 8],3 jnz vrevdm test eax,VDM_USE_DBG_VDMEVENT jnz vrexc_event jmp vrexc3 ; ; Reflect the exception to the VDM ; vrevdm: mov esi,[ebp] cmp word ptr [esi].TsSegCs, KGDT_R3_CODE OR RPL_MASK ; int sim after fault? je vre28 if DEVL cmp word ptr [ebp + 8],11 jne @f inc _ExVdmSegmentNotPresent @@: endif 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].TsEip mov RI.RiEip,eax mov eax,[esi].TsSegCs mov RI.RiSegCs,eax lea esi,RI call CsToLinear ; uses eax as selector test al,0FFh jz vrerr mov eax,[esi].RiSegSs call SsToLinear test al,0FFh jz vrerr mov ecx,[ebp + 8] call PushException test al,0FFh jz vrerr mov esi,RI.RiTrapFrame mov eax,RI.RiEsp mov [esi].TsHardwareEsp,eax xor bl, bl ; R3 mask. 0 on V86 mode test dword ptr [esi].TsEFlags, EFLAGS_V86_MASK ; jnz @F ; mov bl, 7 ; protected mode, R3 LDT selectors only @@: mov eax,RI.RiSegSs or al, bl mov [esi].TsHardwareSegSs,eax mov eax,RI.RiEFlags mov [esi].TsEFlags,eax mov eax,RI.RiSegCs or al, bl mov [esi].TsSegCs,eax mov eax,RI.RiEip mov [esi].TsEip,eax cmp word ptr [ebp + 8],1 jne vre28 and dword ptr [esi].TsEFlags, NOT EFLAGS_TF_MASK vre28: popad mov eax,1 ; handled vre30: mov esp,ebp pop ebp stdRET _Ki386VdmReflectException vrerr: popad xor eax,eax jmp vre30 vrexc1: mov eax, [ebp] and dword ptr [eax]+TsEflags, not EFLAGS_TF_MASK mov eax, [ebp]+TsEip ; (eax)-> faulting instruction stdCall _VdmDispatchException <[ebp],STATUS_SINGLE_STEP,eax,0,0,0,0> jmp vre28 vrexc3: mov eax,BREAKPOINT_BREAK mov ebx, [ebp] mov ebx, [ebx]+TsEip dec ebx ; (eax)-> int3 instruction stdCall _VdmDispatchException <[ebp],STATUS_BREAKPOINT,ebx,3,eax,ecx,edx> jmp vre28 vrexcd: mov eax, [ebp] mov eax, [eax]+TsEip stdCall _VdmDispatchException <[ebp],STATUS_ACCESS_VIOLATION,eax,2,0,-1,0> jmp vre28 vrexc_event: mov eax, [ebp] cmp ebx, DBG_SINGLESTEP jnz vrexc_event2 and dword ptr [eax]+TsEflags, not EFLAGS_TF_MASK vrexc_event2: mov eax, [eax]+TsEip stdCall _VdmDispatchException <[ebp],STATUS_VDM_EVENT,eax,1,ebx,0,0> jmp vre28 stdENDP _Ki386VdmReflectException page ,132 subttl "Reflect Segment Not Present Exception to a Vdm" ;++ ; ; Routine Description: ; ; This routine reflects an TRAP B to a VDM. It uses the information ; in the trap frame to determine what exception to reflect, and updates ; the trap frame with the new CS, EIP, SS, and SP values ; ; Arguments: ; ; ebp -> Trap frame ; ; Returns ; ; 0 is returned if the reflection fails. ; cPublicProc _Ki386VdmSegmentNotPresent,0 mov edi,PCR[PcTeb] mov ecx,VDM_FAULT_HANDLER_SIZE * 0Bh ; ; Set up an exception handler in case we fault ; during the user-space accesses below. ; push ebp push offset FLAT:VdmSegNotPres_ExceptionHandler ; set up exception registration record push PCR[PcExceptionList] mov PCR[PcExceptionList], esp mov edi,[edi].TeVdm xor ebx, ebx cmp edi, _MmUserProbeAddress ; probe the TeVdm jae reflect lea esi,[edi].VtDpmiInfo ; (edi)->dpmi info struct mov edi, [edi].VtFaultTable ; lea edi,[edi+ecx] ; (edi)->FaultHandler cmp edi, _MmUserProbeAddress ; probe the table address jae reflect cmp word ptr [esi].VpLockCount, 0 ; switching stacks? jz short seg_not_pres ; yes, we can handle it ; no, let normal code check ; for stack faults reflect: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. ; pop PCR[PcExceptionList] add esp, 4 ; pop out except handler pop ebp ; ; Reflect the failure (or exception) back to the usermode ntvdm ; to handle. ; pop eax ; (eax) = return addr push 0bh push eax jmp _Ki386VdmReflectException seg_not_pres: if DEVL inc _ExVdmSegmentNotPresent endif inc word ptr [esi].VpLockCount ; save stuff just like SwitchToHandlerStack does mov eax, [ebp].TsEip mov [esi].VpSaveEip, eax mov eax, [ebp].TsHardwareEsp mov [esi].VpSaveEsp, eax mov ax, [ebp].TsHardwareSegSs mov [esi].VpSaveSsSelector, ax mov bx,word ptr [esi].VpSsSelector mov eax, PCR[PcPrcbData+PbCurrentThread] mov eax, [eax+ThApcState+AsProcess] lea eax, [eax+PrLdtDescriptor] mov ch, [eax+KgdtBaseHi] mov cl, [eax+KgdtBaseMid] shl ecx, 16 and ebx, 0fffffff8h mov cx, [eax+KgdtBaseLow] lea eax, [ecx+ebx] mov bh, [eax+KgdtBaseHi] mov bl, [eax+KgdtBaseMid] shl ebx, 16 mov bx, [eax+KgdtBaseLow] ; (ebx) = Base of SS mov eax, [ebp].TsEFlags call GetVirtualBits ; (eax) = app's eflags push esi mov edx, 0fe0h ; dpmistack offset (per win31) test word ptr [esi].VpFlags, 1 ; 32-bit frame? jz short @f sub edx, 8 * 4 add edx, ebx mov esi, [ebp].TsHardwareEsp mov ecx, [ebp].TsHardwareSegSs mov [edx + 20], eax ; push flags mov [edx + 24], esi ; put esp on new stack mov [edx + 28], ecx ; put ss on new stack mov ecx, [ebp].TsSegCs mov eax, [ebp].TsEip mov esi, [ebp].TsErrCode mov [edx + 16], ecx ; push cs mov [edx + 12], eax ; push ip mov [edx + 8], esi ; push error code pop esi mov ecx, [esi].VpDosxFaultIretD mov eax, ecx shr eax, 16 and ecx, 0ffffh mov [edx + 4], eax ; push fault iret seg mov [edx], ecx ; push fault iret offset jmp short vsnp_update @@: sub edx, 8 * 2 add edx, ebx mov esi, [ebp].TsHardwareEsp mov ecx, [ebp].TsHardwareSegSs mov [edx + 10], ax ; push flags mov [edx + 12], si ; put esp on new stack mov [edx + 14], cx ; put ss on new stack mov ecx, [ebp].TsSegCs mov eax, [ebp].TsEip mov esi, [ebp].TsErrCode mov [edx + 8], cx ; push cs mov [edx + 6], ax ; push ip mov [edx + 4], si ; push error code pop esi mov ecx, [esi].VpDosxFaultIret mov eax, ecx shr eax, 16 mov [edx + 2], ax ; push fault iret seg mov [edx], cx ; push fault iret offset vsnp_update: mov eax,[edi].VfEip sub edx, ebx mov cx, word ptr [edi].VfCsSelector mov bx, word ptr [esi].VpSsSelector test dword ptr [edi].VfFlags, VDM_INT_INT_GATE jz short @f lea esi,ds:FIXED_NTVDMSTATE_LINEAR MPLOCK and [esi],NOT VDM_VIRTUAL_INTERRUPTS and dword ptr [ebp].TsEflags, 0FFF7FFFFH ; clear VIF @@: or cx, 7 ; R3 LDT selectors only or bx, 7 ; R3 LDT selectors only mov [ebp].TsSegCs, cx mov [ebp].TsEip, eax mov [ebp].TsHardwareEsp,edx mov [ebp].TsHardwareSegSs,bx ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. ; pop PCR[PcExceptionList] add esp, 4 ; pop out except handler pop ebp mov eax, 1 stdRET _Ki386VdmSegmentNotPresent stdENDP _Ki386VdmSegmentNotPresent ; ; Error and exception blocks for Ki386VdmSegmentNoPresent ; VdmSegNotPres_ExceptionHandler: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. ; mov esp, [esp+8] ; (esp)-> ExceptionList jmp reflect page ,132 subttl "Dispatch UserMode Exception to a Vdm" ;++ ; ; Routine Description: ; ; Dispatches exception for vdm from in the kernel, by invoking ; CommonDispatchException. ; ; Arguments: See CommonDispatchException for parameter description ; ; VOID ; VdmDispatchException( ; PKTRAP_FRAME TrapFrame, ; NTSTATUS ExcepCode, ; PVOID ExcepAddr, ; ULONG NumParms, ; ULONG Parm1, ; ULONG Parm2, ; ULONG Parm3 ; ) ; ; Returns ; ; Nothing ; ; Notes: ; ; This routine may not preserve all of the non-volatile registers if ; a fault occurs. ; cPublicProc _VdmDispatchException,7 TrapFrame equ [ebp+8] ExcepCode equ [ebp+12] ExcepAddr equ [ebp+16] NumParms equ [ebp+20] Parm1 equ [ebp+24] Parm2 equ [ebp+28] Parm3 equ [ebp+32] push ebp mov ebp,esp pushad xor ecx, ecx ; lower irql to 0 fstCall KfLowerIrql ; allow APCs and debuggers in! mov eax, ExcepCode mov ebx, ExcepAddr mov ecx, NumParms mov edx, Parm1 mov esi, Parm2 mov edi, Parm3 mov ebp, TrapFrame call CommonDispatchException popad pop ebp stdRET _VdmDispatchException stdENDP _VdmDispatchException page ,132 subttl "Push Interrupt frame on user stack" ;++ ; ; Routine Description: ; ; This routine pushes an interrupt frame on the user stack ; ; Arguments: ; ; ecx = interrupt # ; esi = address of reg info ; Returns: ; ; interrupt frame pushed on stack ; reg info updated ; public PushInt PushInt proc push ebx push edi ; ; Handle dispatching interrupts directly to the handler, rather than ; to the dos extender ; ; ; Get the information on the interrupt handler ; .errnz (VDM_INTERRUPT_HANDLER_SIZE - 8) mov eax,PCR[PcTeb] ; ; Set up an exception handler in case we fault ; during the user-space accesses below. ; push ebp push offset FLAT:PushIntExceptionHandler ; set up exception registration record push PCR[PcExceptionList] mov PCR[PcExceptionList], esp mov eax,[eax].TbVdm cmp eax, _MmUserProbeAddress jae pierr mov eax, [eax].VtInterruptTable lea eax,[eax + ecx*8] cmp eax, _MmUserProbeAddress jae pierr ; ; Get SP ; mov edi,[ebp].TsHardwareEsp test [esi].RiSsFlags,SEL_TYPE_BIG jnz @f movzx edi,di ; zero high bits for 64k stack ; ; Update SP ; @@: test [eax].ViFlags,dword ptr VDM_INT_32 jz @f ; ; 32 bit iret frame ; cmp edi,12 ; enough space on stack? jb pierr ; no, go fault sub edi,12 mov [esi].RiEsp,edi jmp pi130 ; ; 16 bit iret frame ; @@: cmp edi,6 ; enough space on stack? jb pierr ; no, go fault sub edi,6 mov [esi].RiEsp,edi ; ; Check limit ; pi130: test [esi].RiSsFlags,SEL_TYPE_ED jz pi140 ; ; Expand down, Sp must be above limit ; cmp edi,[esi].RiSsLimit jna pierr jmp pi150 ; ; Normal, Sp must be below limit ; pi140: cmp edi,[esi].RiSsLimit jnb pierr ; ; Get base of ss ; pi150: mov ebx,[esi].RiSsBase test [eax].ViFlags,dword ptr VDM_INT_32 jz pi160 ; ; "push" 32 bit iret frame ; mov edx,[esi].RiEip mov [edi + ebx],edx mov dx,word ptr [ebp].TsSegCs mov [edi + ebx] + 4,edx push eax mov eax,[esi].RiEFlags call GetVirtualBits mov [edi + ebx] + 8,eax pop eax jmp pi170 ; ; push 16 bit iret frame ; pi160: mov dx,word ptr [esi].RiEip mov [edi + ebx],dx mov dx,word ptr [ebp].TsSegCs mov [edi + ebx] + 2,dx push eax mov eax,[esi].RiEFlags call GetVirtualBits mov [edi + ebx] + 4,ax pop eax ; ; Update CS and IP ; pi170: mov ebx,eax ; save int info mov dx,[eax].ViCsSelector mov word ptr [esi].RiSegCs,dx mov edx,[eax].ViEip mov [esi].RiEip,edx movzx eax, word ptr [esi].RiSegCs call CsToLinear ; uses eax as selector test al,0ffh jnz pi175 ; ; Check for destination not present ; test [esi].RiCsFlags,SEL_TYPE_NP jz pierr mov al,0ffh ; succeeded jmp pi180 ; ; Check handler address ; pi175: mov edx,[esi].RiEip cmp edx,[esi].RiCsLimit jnb pierr ; ; Turn off the trap flag ; pi180: and [esi].RiEFlags,NOT EFLAGS_TF_MASK ; ; Turn off virtual interrupts if necessary ; test [ebx].ViFlags,dword ptr VDM_INT_INT_GATE ; n.b. We know al is non-zero, because we succeeded in cstolinear jz pi80 test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS jz pi75 and [esi].RiEFlags, NOT (EFLAGS_VIF) pi75: lea ebx,ds:FIXED_NTVDMSTATE_LINEAR MPLOCK and [ebx], NOT EFLAGS_INTERRUPT_MASK pi80: and [esi].RiEFlags,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK) or [esi].RiEFlags,EFLAGS_INTERRUPT_MASK pi90: ; ; WARNING: Here we directly unlink the exception handler from the ; exception registration chain. NO unwind is performed. ; pop PCR[PcExceptionList] add esp, 4 ; pop out except handler pop ebp pop edi pop ebx ret pierr: xor eax,eax jmp pi90 PushInt endp PushIntExceptionHandler: mov esp, [esp+8] ; (esp)-> ExceptionList xor eax,eax jmp pi90 page ,132 subttl "Convert CS Segment or selector to linear address" ;++ ; ; Routine Description: ; ; Convert CS segment or selector to linear address as appropriate ; for the current user mode processor mode. ; ; Arguments: ; ; esi = reg info ; ; Returns: ; ; reg info updated ; public CsToLinear CsToLinear proc test [esi].RiEFlags,EFLAGS_V86_MASK jz ctl10 shl eax,4 mov [esi].RiCsBase,eax mov [esi].RiCsLimit,0FFFFh mov [esi].RiCsFlags,0 mov eax,1 ret ctl10: push edx ; WARNING volatile regs!!! lea edx,[esi].RiCsLimit push edx lea edx,[esi].RiCsBase push edx lea edx,[esi].RiCsFlags push edx push eax ; push selector IFDEF STD_CALL call _Ki386GetSelectorParameters@16 ELSE call _Ki386GetSelectorParameters add esp,10h ENDIF pop edx or al,al jz ctlerr test [esi].RiCsFlags,SEL_TYPE_EXECUTE jz ctlerr test [esi].RiCsFlags,SEL_TYPE_2GIG jz ctl30 ; Correct limit value for granularity shl [esi].RiCsLimit,12 or [esi].RiCsLimit,0FFFh ctl30: mov eax,1 ret ctlerr: xor eax,eax ret CsToLinear endp page ,132 subttl "Verify that EIP is still valid" ;++ ; ; Routine Description: ; ; Verify that Eip is still valid and put it into the trap frame ; ; Arguments: ; ; esi = address of reg info ; ; Returns: ; ; public CheckEip CheckEip proc mov eax,[esi].RiEip test [esi].RiEFlags,EFLAGS_V86_MASK jz ce20 and eax,[esi].RiCsLimit mov [esi].RiEip,eax jmp ce40 ce20: cmp eax,[esi].RiCsLimit ja ceerr ce40: mov eax,1 ce50: ret ceerr: xor eax,eax jmp ce50 CheckEip endp page ,132 subttl "Convert Ss Segment or selector to linear address" ;++ ; ; Routine Description: ; ; Convert Ss segment or selector to linear address as appropriate ; for the current user mode processor mode. ; ; Arguments: ; ; eax = selector to convert ; esi = address of reg info ; ; Returns: ; ; reg info updated ; public SsToLinear SsToLinear proc test [esi].RiEFlags,EFLAGS_V86_MASK jz stl10 shl eax,4 mov [esi].RiSsBase,eax mov [esi].RiSsLimit,0FFFFh mov [esi].RiSsFlags,0 mov eax,1 ret stl10: push ecx lea ecx,[esi].RiSsLimit push ecx lea ecx,[esi].RiSsBase push ecx lea ecx,[esi].RiSsFlags push ecx push eax ;selector IFDEF STD_CALL call _Ki386GetSelectorParameters@16 ELSE call _Ki386GetSelectorParameters add esp,10h ENDIF pop ecx or al,al jz stlerr test [esi].RiSsFlags,SEL_TYPE_WRITE jz stlerr test [esi].RiSsFlags,SEL_TYPE_2GIG jz stl30 ; Correct limit value for granularity mov eax,[esi].RiSsLimit shl eax,12 or eax,0FFFh mov [esi].RiSsLimit,eax stl30: mov eax,1 stl40: ret stlerr: xor eax,eax jmp stl40 SsToLinear endp page ,132 subttl "Verify that Esp is still valid" ;++ ; ; Routine Description: ; ; Verify that Esp is still valid ; ; Arguments: ; ; ecx = # of bytes needed for stack frame ; esi = address of reg info ; ; Returns: ; ; ; public CheckEsp CheckEsp proc mov eax,[esi].RiEsp test [esi].RiEFlags,EFLAGS_V86_MASK jz cs20 and eax,[esi].RiSsLimit mov [esi].RiEsp,eax jmp cs40 cs20: test [esi].RiSsFlags,SEL_TYPE_BIG jnz cs25 and eax,0FFFFh ; only use 16 bit for 16 bit cs25: cmp ecx, eax ; StackOffset > SP? ja cserr ; yes error dec eax ; make limit checks work test [esi].RiSsFlags,SEL_TYPE_ED ; Expand down? jz cs30 ; jif no ; ; Expand Down ; sub eax, ecx ; New SP cmp eax,[esi].RiSsLimit ; NewSp < Limit? jb cserr jmp cs40 ; ; Not Expand Down ; cs30: cmp eax,[esi].RiSsLimit ja cserr cs40: mov eax,1 cs50: ret cserr: xor eax,eax jmp cs50 CheckEsp endp page ,132 subttl "Switch to protected mode interrupt stack" ;++ ; ; Routine Description: ; ; Switch to protected mode interrupt handler stack ; ; Arguments: ; ; ecx = interrupt number ; esi = address of reg info ; edi = address of PM Stack info ; ; Returns: ; ; reg info updated ; public SwitchToHandlerStack SwitchToHandlerStack proc ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset SwitchToHandlerStack_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on cmp word ptr [edi].VpLockCount, 0 ; already switched? jnz short @f ; yes mov eax, [esi].RiEip mov [edi].VpSaveEip, eax mov eax, [esi].RiEsp mov [edi].VpSaveEsp, eax mov eax, [esi].RiSegSs mov [edi].VpSaveSsSelector, ax movzx eax,word ptr [edi].VpSsSelector pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp mov [esi].RiSegSs,eax mov dword ptr [esi].RiEsp,1000h ; dpmi stack offset movzx eax, word ptr [esi].RiSegSs push ecx call SsToLinear ; compute new base pop ecx test al,0FFh jz shserr push ebp push esp ; Pass current Esp to handler push offset SwitchToHandlerStack_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on @@: inc word ptr [edi].VpLockCount ; maintain lock count pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp mov eax,1 ret shserr: xor eax,eax ret SwitchToHandlerStack_fault: mov esp, [esp+8] pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp xor eax,eax ret SwitchToHandlerStack endp page ,132 subttl "Get protected mode interrupt handler address" ;++ ; ; Routine Description: ; ; Get the address of the interrupt handler for the specified interrupt ; ; Arguments: ; ; ecx = interrupt number ; esi = address of reg info ; ; Returns: ; ; reg info updated ; public GetHandlerAddress GetHandlerAddress proc push ebp push ecx push edx push esp ; Pass current Esp to handler push offset GetHandlerAddress_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov eax,VDM_FAULT_HANDLER_SIZE mul ecx mov edi,PCR[PcTeb] mov edi,[edi].TeVdm cmp edi, _MmUserProbeAddress ; Probe the VMD structure jae @f mov edi,[edi].VtFaultTable cmp edi, _MmUserProbeAddress jae @f movzx ecx,word ptr [edi + eax].VfCsSelector mov [esi].RiSegCs,ecx mov ecx,[edi + eax].VfEip mov [esi].RiEip,ecx mov eax,1 pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop edx pop ecx pop ebp ret @@: GetHandlerAddress_fault_resume: xor eax, eax pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop edx pop ecx pop ebp ret GetHandlerAddress_fault: mov esp, [esp+8] jmp GetHandlerAddress_fault_resume GetHandlerAddress endp page ,132 subttl "Push processor exception" ;++ ; ; Routine Description: ; ; Update the stack and registers to emulate the specified exception ; ; Arguments: ; ; ecx = interrupt number ; esi = address of reg info ; ; Returns: ; ; reg info updated ; public PushException PushException Proc push ebx push edi test [esi].RiEflags,EFLAGS_V86_MASK jz pe40 ; ; Push V86 mode exception ; cmp ecx, 7 ; device not available fault ja peerr ; per win3.1, no exceptions ; above 7 for v86 mode mov edx,[esi].RiEsp mov ebx,[esi].RiSsBase and edx,0FFFFh ; only use a 16 bit sp sub dx,2 mov eax,[esi].RiEFlags push ecx call GetVirtualBits pop ecx ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset perr_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov [ebx+edx],ax ; push flags sub dx,2 mov ax,word ptr [esi].RiSegCs mov [ebx+edx],ax ; push cs sub dx,2 mov ax,word ptr [esi].RiEip mov [ebx+edx],ax ; push ip mov eax,[ecx*4] ; get new cs:ip value pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp push eax movzx eax,ax mov [esi].RiEip,eax pop eax shr eax,16 mov [esi].RiSegCs,eax mov word ptr [esi].RiEsp,dx jmp pe60 perr1_fault: mov esp, [esp+8] ; (esp)-> ExceptionList pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp jmp peerr1 perr_fault: mov esp, [esp+8] ; (esp)-> ExceptionList pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp jmp peerr ; ; Push PM exception ; pe40: push [esi].RiEsp ; save for stack frame push [esi].RiSegSs ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset perr1_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov edi,PCR[PcTeb] mov edi, [edi].TeVdm pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp cmp edi, _MmUserProbeAddress jae peerr1 lea edi,[edi].VtDpmiInfo call SwitchToHandlerStack test al,0FFh jz peerr1 ; pop off stack and exit sub [esi].RiEsp, 20h ; win31 undocumented feature mov ebx,[esi].RiSsBase mov edx,[esi].RiEsp test [esi].RiSsFlags,SEL_TYPE_BIG jnz short @f movzx edx,dx ; zero high bits for 64k stack @@: ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset perr1_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on test word ptr [edi].VpFlags, 1 ; 32 bit app? pop PCR[PcExceptionList] ; Remove our exception handle lea esp, [esp+8] ; clear stack pop ebp jnz pe45 ; yes ; ; push 16-bit frame ; push ecx mov ecx, 8*2 ; room for 8 words? call CheckEsp pop ecx test al,0FFh jz peerr1 ; pop off stack and exit sub edx,8*2 mov [esi].RiEsp,edx ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset perr1_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov eax, [esp+4*4] mov [ebx+edx+14], ax mov eax, [esp+4*5] mov [ebx+edx+12], ax pop PCR[PcExceptionList] ; Remove our exception handle lea esp, [esp+8] ; clear stack pop ebp lea esp, [esp+8] ; clear stack mov eax,[esi].RiEFlags push ecx call GetVirtualBits pop ecx ; ; Install exception handler ; push ebp push esp ; Pass current Esp to handler push offset perr_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov [ebx+edx+10],ax ; push flags movzx eax,word ptr [esi].RiSegCs mov [ebx+edx+8],ax ; push cs mov eax,[esi].RiEip mov [ebx+edx+6],ax ; push ip mov eax,RI.RiTrapFrame mov eax,[eax].TsErrCode mov [ebx+edx+4],ax ; push error code mov eax,[edi].VpDosxFaultIret mov [ebx+edx],eax ; push iret address pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp jmp pe50 pe45: ; ; push 32-bit frame ; push ecx mov ecx, 8*4 ; room for 8 dwords? call CheckEsp pop ecx test al,0FFh jz peerr1 ; pop off stack and exit sub edx,8*4 mov [esi].RiEsp,edx push ebp push esp ; Pass current Esp to handler push offset perr1_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov eax, [esp+4*4] mov [ebx+edx+28], eax mov eax, [esp+4*5] mov [ebx+edx+24], eax pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp lea esp, [esp+8] ; drop ss etc mov eax,[esi].RiEFlags push ecx call GetVirtualBits pop ecx push ebp push esp ; Pass current Esp to handler push offset perr_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov [ebx+edx+20],eax ; push flags movzx eax,word ptr [esi].RiSegCs mov [ebx+edx+16],eax ; push cs mov eax,[esi].RiEip mov [ebx+edx+12],eax ; push ip mov eax,RI.RiTrapFrame mov eax,[eax].TsErrCode mov [ebx+edx+8],eax ; push error code mov eax,[edi].VpDosxFaultIretD shr eax, 16 mov [ebx+edx+4],eax ; push iret seg mov eax,[edi].VpDosxFaultIretD and eax, 0ffffh mov [ebx+edx],eax ; push iret offset pop PCR[PcExceptionList] ; Remove our exception handle add esp, 8 ; clear stack pop ebp pe50: call GetHandlerAddress test al,0FFh jz peerr pe60: push ecx movzx eax,word ptr [esi].RiSegCs call CsToLinear ; uses eax as selector pop ecx test al,0FFh jz peerr mov eax,[esi].RiEip cmp eax,[esi].RiCsLimit ja peerr mov eax,VDM_FAULT_HANDLER_SIZE push edx mul ecx pop edx push ebp push esp ; Pass current Esp to handler push offset perr_fault ; Set Handler address push PCR[PcExceptionList] ; Set next pointer mov PCR[PcExceptionList],esp ; Link us on mov edi,PCR[PcTeb] mov edi,[edi].TbVdm cmp edi, _MmUserProbeAddress jb @f mov edi, _MmUserProbeAddress @@: mov edi,[edi].VtFaultTable add edi,eax cmp edi, _MmUserProbeAddress jb @f mov edi, _MmUserProbeAddress @@: mov eax,[esi].RiEFlags ;WARNING 16 vs 32 test dword ptr [edi].VfFlags,VDM_INT_INT_GATE pop PCR[PcExceptionList] ; Remove our exception handle lea esp, [esp+8] ; clear stack pop ebp jz pe70 and eax,NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_TF_MASK) push eax xor ebx, ebx ; clear prefix flags call SetVirtualBits pop eax pe70: push ecx mov ecx,eax call CheckVdmFlags and ecx,NOT EFLAGS_TF_MASK mov [esi].RiEFlags,ecx pop ecx mov eax,1 ; success pe80: pop edi pop ebx ret peerr1: add esp, 8 ;throw away esp, ss peerr: xor eax,eax jmp pe80 PushException endp _PAGE ends _TEXT$00 SEGMENT DWORD PUBLIC 'CODE' ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING ; ; Non-pagable code ; page ,132 subttl "Ipi worker for enabling Pentium extensions" ;++ ; ; Routine Description: ; ; This routine sets or resets the VME bit in CR4 for each processor ; ; Arguments: ; ; [esp + 4] -- 1 if VME is to be set, 0 if it is to be reset ; Returns: ; ; 0 ; cPublicProc _Ki386VdmEnablePentiumExtentions, 1 Enable equ [ebp + 8] push ebp mov ebp,esp ; ; Insure we do not get an interrupt in here. We may ; be called at IPI_LEVEL - 1 by KiIpiGenericCall. ; pushf cli ; mov eax,cr4 db 0fh, 020h,0e0h test Enable,1 je vepe20 or eax,CR4_VME jmp vepe30 vepe20: and eax,NOT CR4_VME ; mov cr4,eax vepe30: db 0fh,022h,0e0h popf xor eax,eax mov esp,ebp pop ebp stdRET _Ki386VdmEnablePentiumExtentions stdENDP _Ki386VdmEnablePentiumExtentions _TEXT$00 ends end