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

637 lines
15 KiB
NASM

title "Opcode 0f Instruction Emulation"
;++
;
;Copyright (c) 1991 Microsoft Corporation
;
;Module Name:
;
; vdmop0f.asm
;
;Abstract:
;
; This module contains the support for the 0f opcodes that are emulated
; (such as LMSW, mov to/from CR0, mov to/from DR?).
;
;
;Author:
;
; Dave Hastings (daveh) 23-Feb-1992
;
;Notes:
;
; This file needs to be modified for Kenr's NPX changes, i.e. what to
; do with CR0 values
;
;
;Revision History:
;
;--
.386p
.xlist
include ks386.inc
include callconv.inc ; calling convention macros
include mi386.inc
include vdm.inc
page ,132
; Force assume into place
_PAGE SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
_PAGE ENDS
_PAGE SEGMENT DWORD USE32 PUBLIC 'CODE'
ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
extrn OpcodeInvalid:proc
VdmOpcodeInvalid equ OpcodeInvalid
extrn CheckEip:proc
extrn VdmDecodeOperand:proc
extrn RegTab:dword
EXTRNP ___KeGetCurrentThread,0
extrn KiVdmSetUserCR0:proc
extrn _VdmUserCr0MapOut:byte
;
; This table is used to dispatch the special register moves
;
; WARNING Trx not implemented.
SpecialRegTab dd offset VdmOpcodeGetCrx
dd offset VdmOpcodeGetDrx
dd offset VdmOpcodeSetCrx
dd offset VdmOpcodeSetDrx
; dd offset VdmOpcodeGetTrx
dd offset VdmOpcodeInvalid
dd offset VdmOpcodeInvalid
; dd offset VdmOpcodeSetTrx
dd offset VdmOpcodeInvalid
;
; This table is used to locate the debug register contents in the trap frame
; The values for DR4, DR5 should never be used, since these registers are
; reserved by intel
;
DrTab dd TsDr0
dd TsDr1
dd TsDr2
dd TsDr3
dd 0BADF00Dh ; hopefully this will fault if used (dr4)
dd 0BADF00Dh ; hopefully this will fault if used (dr5)
dd TsDr6
dd TsDr7
_PAGE ENDS
_PAGE SEGMENT DWORD USE32 PUBLIC 'CODE'
ASSUME DS:FLAT, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
page ,132
subttl "Emulate 0f opcodes"
;++
;
; Routine Description:
;
; This routine dispatches for the opcode 0f emulation. Currently,
; only LMSW, MOV to/from CR0, and MOV to /from DRx are emulated.
; The others cause access violations in user mode.
;
; Interrupts are disabled upon entry, and enabled upon exit
;
; Arguments:
;
; esi = address of reg info
;
; Returns:
;
; EAX = true if the opcode was emulated.
; reginfo updated
; EAX = false if the opcode was not emulated
;
;
public VdmOpcode0f
VdmOpcode0f proc
push ebp
mov ebp,esp
push edi
;
; Construct address of next opcode byte, and check for gp fault
;
inc [esi].RiEip
call CheckEip
test al,0fh
jz of40 ; Gp fault, report error
mov edi,[esi].RiCsBase
add edi,[esi].RiEip
movzx edx,byte ptr [edi]
cmp edx,MI_LMSW_OPCODE
jne of10
call VdmOpcodeLmsw
jmp of30 ; return
of10: cmp edx,MI_CLTS_OPCODE
jne of20
call VdmOpcodeClts
jmp of30 ; return
of20: cmp edx,MI_GET_CRx_OPCODE
jb of40 ; return error
cmp edx,MI_SET_TRx_OPCODE
ja of40 ; return error
sub edx,MI_GET_CRx_OPCODE
call SpecialRegTab[edx * 4]
of30:
pop edi
mov esp,ebp
pop ebp
ret
of40: xor eax,eax
jmp of30
VdmOpcode0f endp
page ,132
subttl "Emulate LMSW"
;++
;
; Routine Desription:
;
; This routine emulates the LMSW instruction. It allows the dos
; application to set or clear EM and MP. It forces MP to be set
; when EM is clear. It ignores to change any of the other bits.
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; EAX = true if the opcode was emulated.
; reginfo updated
; EAX = false if the opcode was not emulated.
;
public VdmOpcodeLmsw
VdmOpcodeLmsw proc
push ebp
mov ebp,esp
push edi
push ebx
;
; Check reg field of mod r/m byte to see if this is really an Lmsw
;
inc [esi].RiEip
call CheckEip
test al,0fh
jz ol30 ; gp fault
mov edi,[esi].RiEip
add edi,[esi].RiCsBase
movzx edx,byte ptr [edi]
and edx,MI_REGMASK
cmp edx,MI_REGLMSW ; check for the additional opcode bits
jne ol40 ; not lmsw, return error.
;
; Turn off the 32 bit operand prefix (lmsw always uses 16 bit operands)
;
and [esi].RiPrefixFlags,NOT PREFIX_OPER32
;
; Get the linear address of the value to put into the MSW.
;
mov eax,1 ; read operand
call VdmDecodeOperand
test al,0fh
jz ol30 ; error reading operand
mov edi,[esi].RiOperand
mov eax,[edi]
call KiVdmSetUserCR0
mov eax,1 ; indicate success
ol30: pop ebx
pop edi
mov esp,ebp
pop ebp
ret
ol40: xor eax,eax
jmp ol30
VdmOpcodeLmsw endp
page ,132
subttl "Emulate CLTS"
;++
;
; Routine Description:
;
; This routine emulates CLTS by ignoring it.
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; eax = 1
;
public VdmOpcodeClts
VdmOpcodeClts proc
inc [esi].RiEip ; skip second byte
mov eax,1
ret
VdmOpcodeClts endp
page ,132
subttl "Emulate GetCRx"
;++
;
; Routine Description:
;
; This routine emulates the GetCRx opcodes. For now, it returns the
; contents of CR0 unmodified, and 0 for all other CR?
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; eax = true if the opcode was emulated
; reginfo updated.
; eax = false if the opcode was not emulated.
;
public VdmOpcodeGetCrx
VdmOpcodeGetCrx proc
push ebp
mov ebp,esp
push edi
inc [esi].RiEip
call CheckEip
test al,0fh
jz ogc40 ; Gp fault, report error
mov edi,[esi].RiEip
add edi,[esi].RiCsBase
inc [esi].RiEip
movzx edx,byte ptr [edi]
;
; Verify the Mod field for mov special
;
and edx,MI_MODMASK
cmp edx,MI_MODMOVSPEC ; require for mov to/from special
jne short ogc50 ; return error
movzx edx,byte ptr [edi]
test edx,MI_REGMASK ; mov from CR0?
mov eax,0 ; assume 0
jne short ogc30
mov edi,PCR[PcInitialStack] ; (edi) = fp save area
mov edi,[edi].FpCr0NpxState ; get users bits
and edi, CR0_MP+CR0_EM+CR0_PE ; mask
movzx edi,_VdmUserCr0MapOut[edi] ; map to real settings
mov eax, cr0 ; read CR0
and eax, not (CR0_MP+CR0_EM+CR0_TS) ; clear npx bits
or eax, edi ; set npx bits
;
; Pull the destination register from the R/M field
;
ogc30: and edx,MI_RMMASK
mov edi,[esi].RiTrapFrame
mov edx,RegTab[edx * 4] ; get register offset
mov [edx + edi],eax ; store CR? contents into register
mov eax,1
ogc40: pop edi
mov esp,ebp
pop ebp
ret
ogc50: xor eax,eax
jmp ogc40
VdmOpcodeGetCrx endp
page ,132
subttl "Emulate SetCRx"
;++
;
; Routine Description:
;
; This routine emulates the SetCRx opcodes. For now, it only emulates
; set CR0, and only for the same conditions as LMSW. It causes a fault
; for all other CR?
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; eax = true if the opcode was emulated
; reginfo updated.
; eax = false if the opcode was not emulated.
;
public VdmOpcodeSetCrx
VdmOpcodeSetCrx proc
push ebp
mov ebp,esp
push edi
push ebx
inc [esi].RiEip
call CheckEip
test al,0fh
jz osc40 ; Gp fault, report error
mov edi,[esi].RiEip
add edi,[esi].RiCsBase
inc [esi].RiEip
movzx edx,byte ptr [edi]
;
; Verify the Mod field for mov special
;
and edx,MI_MODMASK
cmp edx,MI_MODMOVSPEC ; require for mov to/from special
jne osc50 ; return error
movzx edx,byte ptr [edi]
test edx,MI_REGMASK ; mov to CR0?
jne osc50 ; no, return error
;
; Get the source register from the R/M field
;
and edx,MI_RMMASK
mov edi,[esi].RiTrapFrame
mov edx,RegTab[edx * 4] ; get register offset
mov eax,[edx + edi] ; get CR? contents from register
call KiVdmSetUserCR0
mov eax,1
osc40: pop edi
mov esp,ebp
pop ebp
ret
osc50: xor eax,eax
jmp osc40
VdmOpcodeSetCrx endp
page ,132
subttl "Emulate GetDRx"
;++
;
; Routine Description:
;
; This routine emulates the GetDRx opcodes. For DR0-DR3, it returns the
; values from the user mode trap frame. For DR4-DR5, it returns 0. For
; DR6 and DR7, it returns the bits from the user mode trap frame that are
; settable from user mode.
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; eax = true if the opcode was emulated
; reginfo updated.
; eax = false if the opcode was not emulated.
;
public VdmOpcodeGetDrx
VdmOpcodeGetDrx proc
ModRm equ [ebp - 4]
push ebp
mov ebp,esp
sub esp,4
push edi
push ecx
inc [esi].RiEip
call CheckEip
test al,0fh
jz ogd50 ; Gp fault, report error
mov edi,[esi].RiEip
add edi,[esi].RiCsBase
inc [esi].RiEip
movzx edx,byte ptr [edi]
mov ModRm,edx
;
; Verify the Mod field
;
and edx,MI_MODMASK
cmp edx,MI_MODMOVSPEC ; require for mov special
jne ogd60 ; return error
mov edi,[esi].RiTrapFrame
;
; Get the Dr number
;
mov edx,ModRm
and edx,MI_REGMASK
shr edx,MI_REGSHIFT
;
; If it is DR4 or DR5, set the destination register to zero
;
cmp edx,4
jb ogd20
cmp edx,5
ja ogd20
mov eax,0 ; set the destination to zero
jmp ogd40
;
; Otherwise, use the actual user mode register contents
;
ogd20: mov ecx,DrTab[edx * 4]
mov eax,[edi + ecx] ; get register value
cmp edx,6 ; do we need to sanitize?
jb ogd40 ; no.
cmp edx,7
je ogd30
and eax,DR6_LEGAL
jmp ogd40
ogd30: and eax,DR7_LEGAL
;
; Get destination register from opcode, and store Dr contents
;
ogd40: mov edx,ModRm
and edx,MI_RMMASK
mov ecx,RegTab[edx * 4]
mov [ecx + edi],eax ; set user mode register
mov eax,1 ; indicate success
ogd50: pop ecx
pop edi
mov esp,ebp
pop ebp
ret
ogd60: xor eax,eax ; indicate error
jmp ogd50
VdmOpcodeGetDrx endp
page ,132
subttl "Emulate SetDRx"
;++
;
; Routine Description:
;
; This routine emulates the SetDRx opcodes. For DR0-DR3, it will set
; the user mode register to any linear address up to 16MB. For DR4-DR5,
; it will ignore the instruction. For DR6 and DR7, it will set the bits
; that are user mode setable. For DR7, it also translates the global
; bits to local bits.
;
; Arguments:
;
; esi = pointer to reg info
;
; Returns:
;
; eax = true if the opcode was emulated
; reginfo updated.
; eax = false if the opcode was not emulated.
;
public VdmOpcodeSetDrx
VdmOpcodeSetDrx proc
ModRm equ [ebp - 4]
push ebp
mov ebp,esp
sub esp,4
push edi
push ecx
inc [esi].RiEip
call CheckEip
test al,0fh
jz osd80 ; Gp fault, report error
mov edi,[esi].RiEip
add edi,[esi].RiCsBase
inc [esi].RiEip
movzx edx,byte ptr [edi]
mov ModRm,edx
;
; Verify the Mod field
;
and edx,MI_MODMASK
cmp edx,MI_MODMOVSPEC ; require for mov special
jne osd90 ; return error
;
; Get value to put into debug register
;
mov edi,[esi].RiTrapFrame
mov edx,ModRm
and edx,MI_RMMASK
mov edx,RegTab[edx * 4]
mov eax,[edi + edx]
;
; Determine which debug register it goes into, and sanitize appropriately
;
mov edx,ModRm
and edx,MI_REGMASK
shr edx,MI_REGSHIFT
cmp edx,4
jnb osd20
;
; DR0-DR4, make linear address < 16MB
;
and eax,MAX_VDM_ADDR
jmp osd60
osd20: cmp edx,6
jnb osd30
;
; DR4-DR5, just return
;
jmp osd70 ; return success
osd30: cmp edx,6
jne osd40
;
; DR6, mask for legal bits
;
and eax,DR6_LEGAL
jmp osd60
;
; DR7, mask for legal bits, and translate g to l, and activate debug
;
osd40: mov ecx,eax
and ecx,DR7_GLOBAL
shr ecx,1 ; translate the G to L by shifting
or eax,ecx ; put in any new bits
and eax,DR7_LEGAL ; sanitize the value
;
; Put the value into the user mode register
;
osd60: mov ecx,DrTab[edx * 4]
mov [edi + ecx],eax
cmp edx,7
jne osd70
mov ecx,eax
stdCall ___KeGetCurrentThread
test ecx,DR7_ACTIVE
jz osd65
mov byte ptr [eax].ThDebugActive,1 ; set debugging active for this thread
jmp osd70
osd65: mov byte ptr [eax].ThDebugActive,0
osd70: mov eax,1
osd80: pop ecx
pop edi
mov esp,ebp
pop ebp
ret
osd90: xor eax,eax
jmp osd80
VdmOpcodeSetDrx endp
_PAGE ends
end