windows-nt/Source/XPSP1/NT/base/ntos/ke/i386/emv86.asm
2020-09-26 16:20:57 +08:00

1888 lines
50 KiB
NASM

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, <ES, CS, SS, DS, FS, GS, OPER32, ADDR32, LOCK, REPNE, REP>
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, <esi>
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, <VDMTR_KERNEL_OP_V86,ecx,0,ebp>
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, <ebp>
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, <ecx, 1, TRUE, edi, ebp>
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, <ecx, 2, TRUE, edi, ebp>
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, <ecx, 1, FALSE, edi, ebp>
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, <ecx, 2, FALSE, edi, ebp>
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, <ebx, 1, TRUE, edi, ebp>
ret
oib_prt1:
; call printer status routine with port number, size, trap frame
stdCall _VdmPrinterStatus, <ebx, edi, ebp>
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, <ebx, 2, TRUE, edi, ebp>
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, <ebx, 1, FALSE, edi, ebp>
ret
oob_prt1:
; call printer write data routine with port number, size, trap frame
stdCall _VdmPrinterWriteData, <ebx, edi, ebp>
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, <ebx, 2, FALSE, edi, ebp>
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, <ebp, eax> ; 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, <ebp, eax> ; 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, <ebp, ebx, esi, edi>
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