You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1919 lines
53 KiB
1919 lines
53 KiB
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
|
|
include irqli386.inc
|
|
.list
|
|
|
|
extrn VdmOpcode0f:proc
|
|
extrn _DbgPrint:proc
|
|
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
|
|
EXTRNP _Ki386AdjustEsp0,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
|
|
call _Ki386VdmDispatchStringIo@32 ; use retval
|
|
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
|
|
call _Ki386VdmDispatchStringIo@32 ; use retval
|
|
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
|
|
call _Ki386VdmDispatchStringIo@32 ; use retval
|
|
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
|
|
call _Ki386VdmDispatchStringIo@32 ; use retval
|
|
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
|
|
|
|
if DBG
|
|
; Mods to the trap frame need to be protected from thread context changes
|
|
CurrentIrql
|
|
cmp al, APC_LEVEL
|
|
jge @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
|
|
|
|
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
|
|
push eax
|
|
mov eax, [ebp].TsEFlags
|
|
push eax
|
|
and eax, EFLAGS_VIP
|
|
or eax, ebx
|
|
mov [ebp].TsEFlags, eax
|
|
jmp short pof20
|
|
|
|
pof15:
|
|
push eax
|
|
or ebx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK)
|
|
push [ebp].TsEFlags
|
|
mov [ebp].TsEFlags, ebx
|
|
pof20: xor ebx, [esp]
|
|
test ebx, EFLAGS_V86_MASK
|
|
lea esp, [esp+4]
|
|
je @f
|
|
stdCall _Ki386AdjustEsp0, <ebp>
|
|
@@:
|
|
pop eax
|
|
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
|
|
|
|
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 short 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 short 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 short 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
|
|
cmp ax, 8
|
|
jae @f
|
|
test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
|
|
jnz @f
|
|
mov ax, KGDT_R3_CODE OR RPL_MASK
|
|
@@:
|
|
mov [ebp].TsSegCs,ax ; cs:ip on trap frame is updated
|
|
|
|
mov eax,1
|
|
ret
|
|
|
|
OpcodeINTnnV86 endp
|
|
|
|
oinnvuserrefs proc
|
|
push ebp
|
|
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 short oinnvuserrefs_fault_resume
|
|
|
|
mov ebx,[eax].VtInterruptTable ;
|
|
cmp ebx, 0 ; there is no interrupt table
|
|
je short oinnvuserrefs_fault_resume ; so, don't reflect it.
|
|
|
|
lea ebx,[ebx + ecx*8]
|
|
cmp ebx, _MmUserProbeAddress ; Probe the TeVdm
|
|
jae short oinnvuserrefs_fault_resume
|
|
|
|
test [ebx].ViFlags, VDM_INT_HOOKED ; need to reflect to PM?
|
|
jz short oinnvuserrefs_fault_resume
|
|
|
|
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, 4 ; clear stack
|
|
pop ebp
|
|
ret
|
|
|
|
oinnvuserrefs_fault_resume:
|
|
pop PCR[PcExceptionList] ; Remove our exception handle
|
|
add esp, 4 ; clear stack
|
|
pop ebp
|
|
xor eax, eax
|
|
ret
|
|
oinnvuserrefs endp
|
|
|
|
oinnvuserrefs_fault proc
|
|
;
|
|
; 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_fault 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
|
|
|
|
if DBG
|
|
; Mods to the trap frame need to be protected from thread context changes
|
|
CurrentIrql
|
|
cmp al, APC_LEVEL
|
|
jge @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
|
|
|
|
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
|
|
add edx,6
|
|
movzx ebx,word ptr [ecx+4] ; get flag value
|
|
mov [ebp].TsHardwareEsp,edx ; update sp on trap frame
|
|
mov [ebp].TsSegCs,esi
|
|
|
|
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:
|
|
push eax
|
|
or ebx, (EFLAGS_V86_MASK OR EFLAGS_INTERRUPT_MASK)
|
|
mov eax, dword ptr [ebp].TsEFlags
|
|
push eax
|
|
and eax, EFLAGS_VIP
|
|
or eax, ebx ; update flags in trap frame
|
|
mov dword ptr [ebp].TsEFlags, eax
|
|
pop ebx
|
|
test ebx, EFLAGS_V86_MASK
|
|
jne @f
|
|
push ecx
|
|
push edx
|
|
stdCall _Ki386AdjustEsp0, <ebp>
|
|
pop edx
|
|
pop ecx
|
|
@@:
|
|
pop eax
|
|
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
|
|
add edx,12
|
|
mov ebx, [ecx+8] ; get flag value
|
|
mov [ebp].TsSegCs,esi
|
|
mov [ebp].TsHardwareEsp,edx ; update sp on trap frame
|
|
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 oc40
|
|
|
|
test eax,VDM_INTERRUPT_PENDING
|
|
jz short oc40
|
|
|
|
call VdmDispatchIntAck
|
|
mov eax,1
|
|
ret
|
|
|
|
oc40: lea eax,ds:FIXED_NTVDMSTATE_LINEAR
|
|
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 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, 4 ; 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, 4 ; 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, 4 ; clear stack
|
|
pop ebp
|
|
|
|
stdCall _VdmEndExecution, <ebp, eax> ; TrapFrame, VdmTib
|
|
ret
|
|
VdmDispatchIntAck endp
|
|
|
|
|
|
diafault proc
|
|
|
|
;
|
|
; 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, 4 ; clear stack
|
|
pop ebp
|
|
ret
|
|
diafault 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[PcPrcbData+PbCurrentThread] ; get current thread
|
|
mov edx, [edx].ThInitialStack
|
|
mov edx, [edx].FpCr0NpxState-NPX_FRAME_LENGTH
|
|
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 ebp ; Pass current Ebp 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 short @f
|
|
|
|
mov edx, _MmUserProbeAddress ; make us AV
|
|
@@: mov [edx].VtVdmContext.CsFloatSave.FpCtxtCr0NpxState, eax
|
|
|
|
scr10: pop PCR[PcExceptionList] ; Remove our exception handle
|
|
|
|
mov ebx, PCR[PcPrcbData + PbCurrentThread] ; (ebx) = current thread
|
|
add esp, 4 ; Remove Exception Handler
|
|
pop ebp ; restore ebp.
|
|
|
|
mov edx, [ebx].ThInitialStack ; Get fp save area
|
|
sub edx, NPX_FRAME_LENGTH
|
|
|
|
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
|
|
KiVdmSetUserCR0 endp
|
|
|
|
scr_fault proc
|
|
;
|
|
; 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
|
|
scr_fault endp
|
|
|
|
|
|
|
|
_TEXT$00 ENDS
|
|
|
|
end
|