PATMA.asm revision 854d6eabce2bf852729f7fecd7a8bc24fd0a7f8b
; $Id$
;; @file
; PATM Assembly Routines.
;
; Copyright (C) 2006-2015 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
; you can redistribute it and/or modify it under the terms of the GNU
; General Public License (GPL) as published by the Free Software
; Foundation, in version 2 as it comes in the "COPYING" file of the
; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
;
;;
; @note This method has problems in theory. If we fault for any reason, then we won't be able to restore
; the guest's context properly!!
; E.g if one of the push instructions causes a fault or SS isn't wide open and our patch GC state accesses aren't valid.
; @assumptions
; - Enough stack for a few pushes
; - The SS selector has base 0 and limit 0xffffffff
;
; @todo stack probing is currently hardcoded and not present everywhere (search for 'probe stack')
;*******************************************************************************
;* Header Files *
;*******************************************************************************
%include "VBox/asmdefs.mac"
%include "VBox/err.mac"
%include "iprt/x86.mac"
%include "VBox/vmm/cpum.mac"
%include "VBox/vmm/vm.mac"
%include "PATMA.mac"
;*******************************************************************************
;* Defined Constants And Macros *
;*******************************************************************************
%ifdef DEBUG
; Noisy, but useful for debugging certain problems
;;;%define PATM_LOG_PATCHINSTR
;;%define PATM_LOG_PATCHIRET
%endif
;;
; Simple PATCHASMRECORD initializer
; @param %1 The patch function name.
; @param %2 The number of fixups.
;
%macro PATCHASMRECORD_INIT 2
istruc PATCHASMRECORD
at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
at PATCHASMRECORD.offJump, DD 0
at PATCHASMRECORD.offRelJump, DD 0
at PATCHASMRECORD.offSizeOverride,DD 0
at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
at PATCHASMRECORD.cRelocs, DD %2
iend
%endmacro
;;
; Simple PATCHASMRECORD initializer
; @param %1 The patch function name.
; @param %2 Jump lable.
; @param %3 The number of fixups.
;
%macro PATCHASMRECORD_INIT_JUMP 3
istruc PATCHASMRECORD
at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
at PATCHASMRECORD.offJump, DD %2 - NAME(%1)
at PATCHASMRECORD.offRelJump, DD 0
at PATCHASMRECORD.offSizeOverride,DD 0
at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
at PATCHASMRECORD.cRelocs, DD %3
iend
%endmacro
;;
; Simple PATCHASMRECORD initializer
; @param %1 The patch function name.
; @param %2 Jump lable (or nothing).
; @param %3 Relative jump label (or nothing).
; @param %4 Size override label (or nothing).
; @param %5 The number of fixups.
;
%macro PATCHASMRECORD_INIT_EX 5
istruc PATCHASMRECORD
at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
%ifid %2
at PATCHASMRECORD.offJump, DD %2 - NAME(%1)
%else
at PATCHASMRECORD.offJump, DD 0
%endif
%ifid %3
at PATCHASMRECORD.offRelJump, DD %3 - NAME(%1)
%else
at PATCHASMRECORD.offRelJump, DD 0
%endif
%ifid %4
at PATCHASMRECORD.offSizeOverride,DD %4 - NAME(%1)
%else
at PATCHASMRECORD.offSizeOverride,DD 0
%endif
at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
at PATCHASMRECORD.cRelocs, DD %5
iend
%endmacro
;;
; Switches to the code section and aligns the function.
;
; @remarks This section must be different from the patch readonly data section!
;
%macro BEGIN_PATCH_CODE_SECTION 0
BEGINCODE
align 32
%endmacro
%macro BEGIN_PATCH_CODE_SECTION_NO_ALIGN 0
BEGINCODE
%endmacro
;;
; Switches to the data section for the read-only patch descriptor data and
; aligns it appropriately.
;
; @remarks This section must be different from the patch code section!
;
%macro BEGIN_PATCH_RODATA_SECTION 0
BEGINDATA
align 16
%endmacro
%macro BEGIN_PATCH_RODATA_SECTION_NO_ALIGN 0
BEGINDATA
%endmacro
;;
; Starts a patch.
;
; @param %1 The patch record name (externally visible).
; @param %2 The patch function name (considered internal).
;
%macro BEGIN_PATCH 2
; The patch record.
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME %1
PATCHASMRECORD_INIT PATMCpuidReplacement, (RT_CONCAT(%1,_FixupEnd) - RT_CONCAT(%1,_FixupStart)) / 8
RT_CONCAT(%1,_FixupStart):
; The patch code.
BEGIN_PATCH_CODE_SECTION
BEGINPROC %2
%endmacro
;;
; Emit a fixup.
; @param %1 The fixup type.
%macro PATCH_FIXUP 1
BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
dd %1, 0
BEGIN_PATCH_CODE_SECTION_NO_ALIGN
%endmacro
;;
; Emit a fixup with extra info.
; @param %1 The fixup type.
; @param %2 The extra fixup info.
%macro PATCH_FIXUP_2 2
BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
dd %1, %2
BEGIN_PATCH_CODE_SECTION_NO_ALIGN
%endmacro
;;
; Ends a patch.
;
; This terminates the function and fixup array.
;
; @param %1 The patch record name (externally visible).
; @param %2 The patch function name (considered internal).
;
%macro END_PATCH 2
ENDPROC %2
; Terminate the fixup array.
BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
RT_CONCAT(%1,_FixupEnd):
dd 0ffffffffh, 0ffffffffh
BEGIN_PATCH_CODE_SECTION_NO_ALIGN
%endmacro
;
; Switch to 32-bit mode (x86).
;
%ifdef RT_ARCH_AMD64
BITS 32
%endif
%ifdef VBOX_WITH_STATISTICS
;
; Patch call statistics
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMStats
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
inc dword [ss:PATM_ASMFIX_ALLPATCHCALLS]
inc dword [ss:PATM_ASMFIX_PERPATCHCALLS]
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMStats
; Patch record for statistics
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmStatsRecord
PATCHASMRECORD_INIT PATMStats, 4
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_ALLPATCHCALLS, 0
DD PATM_ASMFIX_PERPATCHCALLS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
%endif ; VBOX_WITH_STATISTICS
;
; Set PATM_ASMFIX_INTERRUPTFLAG
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMSetPIF
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMSetPIF
; Patch record for setting PATM_ASMFIX_INTERRUPTFLAG
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmSetPIFRecord
PATCHASMRECORD_INIT PATMSetPIF, 1
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Clear PATM_ASMFIX_INTERRUPTFLAG
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMClearPIF
; probe stack here as we can't recover from page faults later on
not dword [esp-64]
not dword [esp-64]
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
ENDPROC PATMClearPIF
; Patch record for clearing PATM_ASMFIX_INTERRUPTFLAG
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmClearPIFRecord
PATCHASMRECORD_INIT PATMClearPIF, 1
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Clear PATM_ASMFIX_INHIBITIRQADDR and fault if IF=0
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMClearInhibitIRQFaultIF0
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
mov dword [ss:PATM_ASMFIX_INHIBITIRQADDR], 0
pushf
test dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
jz PATMClearInhibitIRQFaultIF0_Fault
; if interrupts are pending, then we must go back to the host context to handle them!
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMClearInhibitIRQFaultIF0_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
PATMClearInhibitIRQFaultIF0_Fault:
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMClearInhibitIRQFaultIF0_Continue:
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMClearInhibitIRQFaultIF0
; Patch record for clearing PATM_ASMFIX_INHIBITIRQADDR
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmClearInhibitIRQFaultIF0Record
PATCHASMRECORD_INIT PATMClearInhibitIRQFaultIF0, 12
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INHIBITIRQADDR, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_NEXTINSTRADDR, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Clear PATM_ASMFIX_INHIBITIRQADDR and continue if IF=0 (duplicated function only; never jump back to guest code afterwards!!)
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMClearInhibitIRQContIF0
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
mov dword [ss:PATM_ASMFIX_INHIBITIRQADDR], 0
pushf
test dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
jz PATMClearInhibitIRQContIF0_Continue
; if interrupts are pending, then we must go back to the host context to handle them!
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMClearInhibitIRQContIF0_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
PATMClearInhibitIRQContIF0_Continue:
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMClearInhibitIRQContIF0
; Patch record for clearing PATM_ASMFIX_INHIBITIRQADDR
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmClearInhibitIRQContIF0Record
PATCHASMRECORD_INIT PATMClearInhibitIRQContIF0, 11
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INHIBITIRQADDR, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_NEXTINSTRADDR, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMCliReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
mov eax, PATM_ACTION_LOG_CLI
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IF
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMCliJump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMCliReplacement
; Patch record for 'cli'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmCliRecord
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT_JUMP PATMCliReplacement, PATMCliJump, 4
%else
PATCHASMRECORD_INIT_JUMP PATMCliReplacement, PATMCliJump, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMStiReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
mov dword [ss:PATM_ASMFIX_INHIBITIRQADDR], PATM_ASMFIX_NEXTINSTRADDR
pushf
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
mov eax, PATM_ACTION_LOG_STI
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
or dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMStiReplacement
; Patch record for 'sti'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmStiRecord
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT PATMStiReplacement, 6
%else
PATCHASMRECORD_INIT PATMStiReplacement, 5
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INHIBITIRQADDR, 0
DD PATM_ASMFIX_NEXTINSTRADDR, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Trampoline code for trap entry (without error code on the stack)
;
; esp + 32 - GS (V86 only)
; esp + 28 - FS (V86 only)
; esp + 24 - DS (V86 only)
; esp + 20 - ES (V86 only)
; esp + 16 - SS (if transfer to inner ring)
; esp + 12 - ESP (if transfer to inner ring)
; esp + 8 - EFLAGS
; esp + 4 - CS
; esp - EIP
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMTrapEntry
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
mov eax, PATM_ACTION_LOG_GATE_ENTRY
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp+12], X86_EFL_VM
jnz PATMTrapNoRing1
; make sure the saved CS selector for ring 1 is made 0
test dword [esp+8], 2
jnz PATMTrapNoRing1
test dword [esp+8], 1
jz PATMTrapNoRing1
and dword [esp+8], dword ~1 ; yasm / nasm dword
PATMTrapNoRing1:
; correct EFLAGS on the stack to include the current IOPL
push eax
mov eax, dword [ss:PATM_ASMFIX_VMFLAGS]
and eax, X86_EFL_IOPL
and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
or dword [esp+16], eax
pop eax
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMTrapEntryJump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMTrapEntry
; Patch record for trap gate entrypoint
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmTrapEntryRecord
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT_JUMP PATMTrapEntry, PATMTrapEntryJump, 4
%else
PATCHASMRECORD_INIT_JUMP PATMTrapEntry, PATMTrapEntryJump, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Trampoline code for trap entry (with error code on the stack)
;
; esp + 36 - GS (V86 only)
; esp + 32 - FS (V86 only)
; esp + 28 - DS (V86 only)
; esp + 24 - ES (V86 only)
; esp + 20 - SS (if transfer to inner ring)
; esp + 16 - ESP (if transfer to inner ring)
; esp + 12 - EFLAGS
; esp + 8 - CS
; esp + 4 - EIP
; esp - error code
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMTrapEntryErrorCode
PATMTrapErrorCodeEntryStart:
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4+4] ;3 dwords + pushed flags + error code -> iret eip
mov eax, PATM_ACTION_LOG_GATE_ENTRY
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp+16], X86_EFL_VM
jnz PATMTrapErrorCodeNoRing1
; make sure the saved CS selector for ring 1 is made 0
test dword [esp+12], 2
jnz PATMTrapErrorCodeNoRing1
test dword [esp+12], 1
jz PATMTrapErrorCodeNoRing1
and dword [esp+12], dword ~1 ; yasm / nasm dword
PATMTrapErrorCodeNoRing1:
; correct EFLAGS on the stack to include the current IOPL
push eax
mov eax, dword [ss:PATM_ASMFIX_VMFLAGS]
and eax, X86_EFL_IOPL
and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(error code)+4(eax)
or dword [esp+20], eax
pop eax
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMTrapErrorCodeEntryJump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMTrapEntryErrorCode
; Patch record for trap gate entrypoint
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmTrapEntryRecordErrorCode
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT_JUMP PATMTrapEntryErrorCode, PATMTrapErrorCodeEntryJump, 4
%else
PATCHASMRECORD_INIT_JUMP PATMTrapEntryErrorCode, PATMTrapErrorCodeEntryJump, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Trampoline code for interrupt gate entry (without error code on the stack)
;
; esp + 32 - GS (V86 only)
; esp + 28 - FS (V86 only)
; esp + 24 - DS (V86 only)
; esp + 20 - ES (V86 only)
; esp + 16 - SS (if transfer to inner ring)
; esp + 12 - ESP (if transfer to inner ring)
; esp + 8 - EFLAGS
; esp + 4 - CS
; esp - EIP
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMIntEntry
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
mov eax, PATM_ACTION_LOG_GATE_ENTRY
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp+12], X86_EFL_VM
jnz PATMIntNoRing1
; make sure the saved CS selector for ring 1 is made 0
test dword [esp+8], 2
jnz PATMIntNoRing1
test dword [esp+8], 1
jz PATMIntNoRing1
and dword [esp+8], dword ~1 ; yasm / nasm dword
PATMIntNoRing1:
; correct EFLAGS on the stack to include the current IOPL
push eax
mov eax, dword [ss:PATM_ASMFIX_VMFLAGS]
and eax, X86_EFL_IOPL
and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
or dword [esp+16], eax
pop eax
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMIntEntry
; Patch record for interrupt gate entrypoint
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmIntEntryRecord
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT PATMIntEntry, 4
%else
PATCHASMRECORD_INIT PATMIntEntry, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Trampoline code for interrupt gate entry (*with* error code on the stack)
;
; esp + 36 - GS (V86 only)
; esp + 32 - FS (V86 only)
; esp + 28 - DS (V86 only)
; esp + 24 - ES (V86 only)
; esp + 20 - SS (if transfer to inner ring)
; esp + 16 - ESP (if transfer to inner ring)
; esp + 12 - EFLAGS
; esp + 8 - CS
; esp + 4 - EIP
; esp - error code
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMIntEntryErrorCode
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4+4] ;3 dwords + pushed flags + error code -> iret eip
mov eax, PATM_ACTION_LOG_GATE_ENTRY
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp+16], X86_EFL_VM
jnz PATMIntNoRing1_ErrorCode
; make sure the saved CS selector for ring 1 is made 0
test dword [esp+12], 2
jnz PATMIntNoRing1_ErrorCode
test dword [esp+12], 1
jz PATMIntNoRing1_ErrorCode
and dword [esp+12], dword ~1 ; yasm / nasm dword
PATMIntNoRing1_ErrorCode:
; correct EFLAGS on the stack to include the current IOPL
push eax
mov eax, dword [ss:PATM_ASMFIX_VMFLAGS]
and eax, X86_EFL_IOPL
and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(eax)+4(error code)
or dword [esp+20], eax
pop eax
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMIntEntryErrorCode
; Patch record for interrupt gate entrypoint
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmIntEntryRecordErrorCode
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT PATMIntEntryErrorCode, 4
%else
PATCHASMRECORD_INIT PATMIntEntryErrorCode, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; 32 bits Popf replacement that faults when IF remains 0
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPopf32Replacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
mov eax, PATM_ACTION_LOG_POPF_IF1
test dword [esp+8], X86_EFL_IF
jnz PATMPopf32_Log
mov eax, PATM_ACTION_LOG_POPF_IF0
PATMPopf32_Log:
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
test dword [esp], X86_EFL_IF
jnz PATMPopf32_Ok
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMPopf32_Ok:
; Note: we don't allow popf instructions to change the current IOPL; we simply ignore such changes (!!!)
; In this particular patch it's rather unlikely the pushf was included, so we have no way to check if the flags on the stack were correctly synced
; PATMPopf32Replacement_NoExit is different, because it's only used in IDT and function patches
or dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
; if interrupts are pending, then we must go back to the host context to handle them!
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMPopf32_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
PATMPopf32_Continue:
popfd ; restore flags we pushed above
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMPopf32Jump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMPopf32Replacement
; Patch record for 'popfd'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPopf32Record
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT_JUMP PATMPopf32Replacement, PATMPopf32Jump, 12
%else
PATCHASMRECORD_INIT_JUMP PATMPopf32Replacement, PATMPopf32Jump, 11
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_NEXTINSTRADDR, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; no need to check the IF flag when popf isn't an exit point of a patch (e.g. function duplication)
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPopf32Replacement_NoExit
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
mov eax, PATM_ACTION_LOG_POPF_IF1
test dword [esp+8], X86_EFL_IF
jnz PATMPopf32_NoExitLog
mov eax, PATM_ACTION_LOG_POPF_IF0
PATMPopf32_NoExitLog:
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
test dword [esp], X86_EFL_IF
jz PATMPopf32_NoExit_Continue
; if interrupts are pending, then we must go back to the host context to handle them!
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMPopf32_NoExit_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_NEXTINSTRADDR
pop dword [ss:PATM_ASMFIX_VMFLAGS] ; restore flags now (the or instruction changes the flags as well)
push dword [ss:PATM_ASMFIX_VMFLAGS]
popfd
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
PATMPopf32_NoExit_Continue:
pop dword [ss:PATM_ASMFIX_VMFLAGS]
push dword [ss:PATM_ASMFIX_VMFLAGS]
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMPopf32Replacement_NoExit
; Patch record for 'popfd'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPopf32Record_NoExit
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT PATMPopf32Replacement_NoExit, 14
%else
PATCHASMRECORD_INIT PATMPopf32Replacement_NoExit, 13
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_NEXTINSTRADDR, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; 16 bits Popf replacement that faults when IF remains 0
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPopf16Replacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
test word [esp], X86_EFL_IF
jnz PATMPopf16_Ok
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMPopf16_Ok:
; if interrupts are pending, then we must go back to the host context to handle them!
; @note we destroy the flags here, but that should really not matter (PATM_INT3 case)
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMPopf16_Continue
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMPopf16_Continue:
pop word [ss:PATM_ASMFIX_VMFLAGS]
push word [ss:PATM_ASMFIX_VMFLAGS]
and dword [ss:PATM_ASMFIX_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
or dword [ss:PATM_ASMFIX_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
DB 0x66 ; size override
popf ;after the and and or operations!! (flags must be preserved)
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMPopf16Jump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMPopf16Replacement
; Patch record for 'popf'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPopf16Record
PATCHASMRECORD_INIT_JUMP PATMPopf16Replacement, PATMPopf16Jump, 9
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; 16 bits Popf replacement that faults when IF remains 0
; @todo not necessary to fault in that case (see 32 bits version)
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPopf16Replacement_NoExit
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
test word [esp], X86_EFL_IF
jnz PATMPopf16_Ok_NoExit
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMPopf16_Ok_NoExit:
; if interrupts are pending, then we must go back to the host context to handle them!
; @note we destroy the flags here, but that should really not matter (PATM_INT3 case)
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
jz PATMPopf16_Continue_NoExit
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMPopf16_Continue_NoExit:
pop word [ss:PATM_ASMFIX_VMFLAGS]
push word [ss:PATM_ASMFIX_VMFLAGS]
and dword [ss:PATM_ASMFIX_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
or dword [ss:PATM_ASMFIX_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
DB 0x66 ; size override
popf ;after the and and or operations!! (flags must be preserved)
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMPopf16Replacement_NoExit
; Patch record for 'popf'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPopf16Record_NoExit
PATCHASMRECORD_INIT PATMPopf16Replacement_NoExit, 9
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPushf32Replacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
mov eax, PATM_ACTION_LOG_PUSHF
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
pushfd
push eax
mov eax, dword [esp+8]
and eax, PATM_FLAGS_MASK
or eax, dword [ss:PATM_ASMFIX_VMFLAGS]
mov dword [esp+8], eax
pop eax
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMPushf32Replacement
; Patch record for 'pushfd'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPushf32Record
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT PATMPushf32Replacement, 4
%else
PATCHASMRECORD_INIT PATMPushf32Replacement, 3
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPushf16Replacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
DB 0x66 ; size override
pushf
DB 0x66 ; size override
pushf
push eax
xor eax, eax
mov ax, word [esp+6]
and eax, PATM_FLAGS_MASK
or eax, dword [ss:PATM_ASMFIX_VMFLAGS]
mov word [esp+6], ax
pop eax
DB 0x66 ; size override
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMPushf16Replacement
; Patch record for 'pushf'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPushf16Record
PATCHASMRECORD_INIT PATMPushf16Replacement, 3
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMPushCSReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
push cs
pushfd
test dword [esp+4], 2
jnz pushcs_notring1
; change dpl from 1 to 0
and dword [esp+4], dword ~1 ; yasm / nasm dword
pushcs_notring1:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMPushCSJump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMPushCSReplacement
; Patch record for 'push cs'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmPushCSRecord
PATCHASMRECORD_INIT_JUMP PATMPushCSReplacement, PATMPushCSJump, 2
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;****************************************************
; Abstract:
;
; if eflags.NT==0 && iretstack.eflags.VM==0 && iretstack.eflags.IOPL==0
; then
; if return to ring 0 (iretstack.new_cs & 3 == 0)
; then
; if iretstack.new_eflags.IF == 1 && iretstack.new_eflags.IOPL == 0
; then
; iretstack.new_cs |= 1
; else
; int 3
; endif
; uVMFlags &= ~X86_EFL_IF
; iret
; else
; int 3
;****************************************************
;
; Stack:
;
; esp + 32 - GS (V86 only)
; esp + 28 - FS (V86 only)
; esp + 24 - DS (V86 only)
; esp + 20 - ES (V86 only)
; esp + 16 - SS (if transfer to outer ring)
; esp + 12 - ESP (if transfer to outer ring)
; esp + 8 - EFLAGS
; esp + 4 - CS
; esp - EIP
;;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMIretReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
mov eax, PATM_ACTION_LOG_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp], X86_EFL_NT
jnz near iret_fault1
; we can't do an iret to v86 code, as we run with CPL=1. The iret would attempt a protected mode iret and (most likely) fault.
test dword [esp+12], X86_EFL_VM
jnz near iret_return_to_v86
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;;@todo: not correct for iret back to ring 2!!!!!
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
test dword [esp+8], 2
jnz iret_notring0
test dword [esp+12], X86_EFL_IF
jz near iret_clearIF
; force ring 1 CS RPL
or dword [esp+8], 1 ;-> @todo we leave traces or raw mode if we jump back to the host context to handle pending interrupts! (below)
iret_notring0:
; if interrupts are pending, then we must go back to the host context to handle them!
; Note: This is very important as pending pic interrupts can be overridden by apic interrupts if we don't check early enough (Fedora 5 boot)
; @@todo fix this properly, so we can dispatch pending interrupts in GC
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
jz iret_continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_PENDING_IRQ_AFTER_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_CURINSTRADDR
popfd
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
iret_continue :
; This section must *always* be executed (!!)
; Extract the IOPL from the return flags, save them to our virtual flags and
; put them back to zero
; @note we assume iretd doesn't fault!!!
push eax
mov eax, dword [esp+16]
and eax, X86_EFL_IOPL
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IOPL
or dword [ss:PATM_ASMFIX_VMFLAGS], eax
pop eax
and dword [esp+12], ~X86_EFL_IOPL
; Set IF again; below we make sure this won't cause problems.
or dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
; make sure iret is executed fully (including the iret below; cli ... iret can otherwise be interrupted)
mov dword [ss:PATM_ASMFIX_INHIBITIRQADDR], PATM_ASMFIX_CURINSTRADDR
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
iretd
PATM_INT3
iret_fault:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
iret_fault1:
nop
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
iret_clearIF:
push dword [esp+4] ; eip to return to
pushfd
push eax
push PATM_ASMFIX_FIXUP
DB 0E8h ; call
DD PATM_ASMFIX_IRET_FUNCTION
add esp, 4 ; pushed address of jump table
cmp eax, 0
je near iret_fault3
mov dword [esp+12+4], eax ; stored eip in iret frame
pop eax
popfd
add esp, 4 ; pushed eip
; always ring 0 return -> change to ring 1 (CS in iret frame)
or dword [esp+8], 1
; This section must *always* be executed (!!)
; Extract the IOPL from the return flags, save them to our virtual flags and
; put them back to zero
push eax
mov eax, dword [esp+16]
and eax, X86_EFL_IOPL
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IOPL
or dword [ss:PATM_ASMFIX_VMFLAGS], eax
pop eax
and dword [esp+12], ~X86_EFL_IOPL
; Clear IF
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IF
popfd
; the patched destination code will set PATM_ASMFIX_INTERRUPTFLAG after the return!
iretd
iret_return_to_v86:
test dword [esp+12], X86_EFL_IF
jz iret_fault
; Go to our hypervisor trap handler to perform the iret to v86 code
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX
mov eax, PATM_ACTION_DO_V86_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
popfd
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
iret_fault3:
pop eax
popfd
add esp, 4 ; pushed eip
jmp iret_fault
align 4
PATMIretTable:
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
ENDPROC PATMIretReplacement
; Patch record for 'iretd'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmIretRecord
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT PATMIretReplacement, 26
%else
PATCHASMRECORD_INIT PATMIretReplacement, 25
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_CURINSTRADDR, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INHIBITIRQADDR, 0
DD PATM_ASMFIX_CURINSTRADDR, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_FIXUP, PATMIretTable - NAME(PATMIretReplacement)
DD PATM_ASMFIX_IRET_FUNCTION, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD 0ffffffffh, 0ffffffffh
;
;
;****************************************************
; Abstract:
;
; if eflags.NT==0 && iretstack.eflags.VM==0 && iretstack.eflags.IOPL==0
; then
; if return to ring 0 (iretstack.new_cs & 3 == 0)
; then
; if iretstack.new_eflags.IF == 1 && iretstack.new_eflags.IOPL == 0
; then
; iretstack.new_cs |= 1
; else
; int 3
; endif
; uVMFlags &= ~X86_EFL_IF
; iret
; else
; int 3
;****************************************************
;
; Stack:
;
; esp + 32 - GS (V86 only)
; esp + 28 - FS (V86 only)
; esp + 24 - DS (V86 only)
; esp + 20 - ES (V86 only)
; esp + 16 - SS (if transfer to outer ring)
; esp + 12 - ESP (if transfer to outer ring)
; esp + 8 - EFLAGS
; esp + 4 - CS
; esp - EIP
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMIretRing1Replacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
%ifdef PATM_LOG_PATCHIRET
push eax
push ecx
push edx
lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
mov eax, PATM_ACTION_LOG_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
test dword [esp], X86_EFL_NT
jnz near iretring1_fault1
; we can't do an iret to v86 code, as we run with CPL=1. The iret would attempt a protected mode iret and (most likely) fault.
test dword [esp+12], X86_EFL_VM
jnz near iretring1_return_to_v86
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;;@todo: not correct for iret back to ring 2!!!!!
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
test dword [esp+8], 2
jnz iretring1_checkpendingirq
test dword [esp+12], X86_EFL_IF
jz near iretring1_clearIF
iretring1_checkpendingirq:
; if interrupts are pending, then we must go back to the host context to handle them!
; Note: This is very important as pending pic interrupts can be overridden by apic interrupts if we don't check early enough (Fedora 5 boot)
; @@todo fix this properly, so we can dispatch pending interrupts in GC
test dword [ss:PATM_ASMFIX_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
jz iretring1_continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_EDI], edi
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_PENDING_IRQ_AFTER_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_ASMFIX_CURINSTRADDR
popfd
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
iretring1_continue:
test dword [esp+8], 2
jnz iretring1_notring01
test dword [esp+8], 1
jz iretring1_ring0
; ring 1 return change CS & SS RPL to 2 from 1
and dword [esp+8], ~1 ; CS
or dword [esp+8], 2
and dword [esp+20], ~1 ; SS
or dword [esp+20], 2
jmp short iretring1_notring01
iretring1_ring0:
; force ring 1 CS RPL
or dword [esp+8], 1
iretring1_notring01:
; This section must *always* be executed (!!)
; Extract the IOPL from the return flags, save them to our virtual flags and
; put them back to zero
; @note we assume iretd doesn't fault!!!
push eax
mov eax, dword [esp+16]
and eax, X86_EFL_IOPL
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IOPL
or dword [ss:PATM_ASMFIX_VMFLAGS], eax
pop eax
and dword [esp+12], ~X86_EFL_IOPL
; Set IF again; below we make sure this won't cause problems.
or dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
; make sure iret is executed fully (including the iret below; cli ... iret can otherwise be interrupted)
mov dword [ss:PATM_ASMFIX_INHIBITIRQADDR], PATM_ASMFIX_CURINSTRADDR
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
iretd
PATM_INT3
iretring1_fault:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
iretring1_fault1:
nop
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
iretring1_clearIF:
push dword [esp+4] ; eip to return to
pushfd
push eax
push PATM_ASMFIX_FIXUP
DB 0E8h ; call
DD PATM_ASMFIX_IRET_FUNCTION
add esp, 4 ; pushed address of jump table
cmp eax, 0
je near iretring1_fault3
mov dword [esp+12+4], eax ; stored eip in iret frame
pop eax
popfd
add esp, 4 ; pushed eip
; This section must *always* be executed (!!)
; Extract the IOPL from the return flags, save them to our virtual flags and
; put them back to zero
push eax
mov eax, dword [esp+16]
and eax, X86_EFL_IOPL
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IOPL
or dword [ss:PATM_ASMFIX_VMFLAGS], eax
pop eax
and dword [esp+12], ~X86_EFL_IOPL
; Clear IF
and dword [ss:PATM_ASMFIX_VMFLAGS], ~X86_EFL_IF
popfd
test dword [esp+8], 1
jz iretring1_clearIF_ring0
; ring 1 return change CS & SS RPL to 2 from 1
and dword [esp+8], ~1 ; CS
or dword [esp+8], 2
and dword [esp+20], ~1 ; SS
or dword [esp+20], 2
; the patched destination code will set PATM_ASMFIX_INTERRUPTFLAG after the return!
iretd
iretring1_clearIF_ring0:
; force ring 1 CS RPL
or dword [esp+8], 1
; the patched destination code will set PATM_ASMFIX_INTERRUPTFLAG after the return!
iretd
iretring1_return_to_v86:
test dword [esp+12], X86_EFL_IF
jz iretring1_fault
; Go to our hypervisor trap handler to perform the iret to v86 code
mov dword [ss:PATM_ASMFIX_TEMP_EAX], eax
mov dword [ss:PATM_ASMFIX_TEMP_ECX], ecx
mov dword [ss:PATM_ASMFIX_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX
mov eax, PATM_ACTION_DO_V86_IRET
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
popfd
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; does not return
iretring1_fault3:
pop eax
popfd
add esp, 4 ; pushed eip
jmp iretring1_fault
align 4
PATMIretRing1Table:
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
ENDPROC PATMIretRing1Replacement
; Patch record for 'iretd'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmIretRing1Record
%ifdef PATM_LOG_PATCHIRET
PATCHASMRECORD_INIT PATMIretRing1Replacement, 26
%else
PATCHASMRECORD_INIT PATMIretRing1Replacement, 25
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
%ifdef PATM_LOG_PATCHIRET
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_VM_FORCEDACTIONS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_EDI, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_CURINSTRADDR, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INHIBITIRQADDR, 0
DD PATM_ASMFIX_CURINSTRADDR, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_FIXUP, PATMIretRing1Table - NAME(PATMIretRing1Replacement)
DD PATM_ASMFIX_IRET_FUNCTION, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_ECX, 0
DD PATM_ASMFIX_TEMP_RESTORE_FLAGS, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD 0ffffffffh, 0ffffffffh
;
; global function for implementing 'iret' to code with IF cleared
;
; Caller is responsible for right stack layout
; + 16 original return address
; + 12 eflags
; + 8 eax
; + 4 Jump table address
;( + 0 return address )
;
; @note assumes PATM_ASMFIX_INTERRUPTFLAG is zero
; @note assumes it can trash eax and eflags
;
; @returns eax=0 on failure
; otherwise return address in eax
;
; @note NEVER change this without bumping the SSM version
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMIretFunction
push ecx
push edx
push edi
; Event order:
; 1) Check if the return patch address can be found in the lookup table
; 2) Query return patch address from the hypervisor
; 1) Check if the return patch address can be found in the lookup table
mov edx, dword [esp+12+16] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+12+4] ; jump table
mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
cmp ecx, 0
je near PATMIretFunction_AskHypervisor
PATMIretFunction_SearchStart:
cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
je near PATMIretFunction_SearchHit
inc eax
cmp eax, ecx
jl near PATMIretFunction_SearchStart
PATMIretFunction_AskHypervisor:
; 2) Query return patch address from the hypervisor
; @todo private ugly interface, since we have nothing generic at the moment
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
mov eax, PATM_ACTION_LOOKUP_ADDRESS
mov ecx, PATM_ACTION_MAGIC
mov edi, dword [esp+12+4] ; jump table address
mov edx, dword [esp+12+16] ; original return address
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
jmp near PATMIretFunction_SearchEnd
PATMIretFunction_SearchHit:
mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
;@note can be zero, so the next check is required!!
PATMIretFunction_SearchEnd:
cmp eax, 0
jz PATMIretFunction_Failure
add eax, PATM_ASMFIX_PATCHBASE
pop edi
pop edx
pop ecx
ret
PATMIretFunction_Failure:
;signal error
xor eax, eax
pop edi
pop edx
pop ecx
ret
ENDPROC PATMIretFunction
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmIretFunctionRecord
PATCHASMRECORD_INIT PATMIretFunction, 2
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_PATCHBASE, 0
DD 0ffffffffh, 0ffffffffh
;
; PATMCpuidReplacement
;
; Calls a helper function that does the job.
;
; This way we can change the CPUID structures and how we organize them without
; breaking patches. It also saves a bit of memory for patch code and fixups.
;
BEGIN_PATCH g_patmCpuidRecord, PATMCpuidReplacement
not dword [esp-32] ; probe stack before starting
not dword [esp-32]
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
PATCH_FIXUP PATM_ASMFIX_INTERRUPTFLAG
pushf
db 0e8h ; call
dd PATM_ASMFIX_PATCH_HLP_CPUM_CPUID
PATCH_FIXUP PATM_ASMFIX_PATCH_HLP_CPUM_CPUID
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATCH_FIXUP PATM_ASMFIX_INTERRUPTFLAG
END_PATCH g_patmCpuidRecord, PATMCpuidReplacement
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMJEcxReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
PATMJEcxSizeOverride:
DB 0x90 ; nop
cmp ecx, dword 0 ; yasm / nasm dword
jnz PATMJEcxContinue
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMJEcxJump:
DD PATM_ASMFIX_JUMPDELTA
PATMJEcxContinue:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMJEcxReplacement
; Patch record for 'JEcx'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmJEcxRecord
PATCHASMRECORD_INIT_EX PATMJEcxReplacement, , PATMJEcxJump, PATMJEcxSizeOverride, 3
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
;
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMLoopReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
PATMLoopSizeOverride:
DB 0x90 ; nop
dec ecx
jz PATMLoopContinue
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMLoopJump:
DD PATM_ASMFIX_JUMPDELTA
PATMLoopContinue:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMLoopReplacement
; Patch record for 'Loop'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmLoopRecord
PATCHASMRECORD_INIT_EX PATMLoopReplacement, , PATMLoopJump, PATMLoopSizeOverride, 3
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; jump if ZF=1 AND (E)CX != 0
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMLoopZReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
jnz NAME(PATMLoopZReplacement_EndProc)
pushfd
PATMLoopZSizeOverride:
DB 0x90 ; nop
dec ecx
jz PATMLoopZContinue
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMLoopZJump:
DD PATM_ASMFIX_JUMPDELTA
PATMLoopZContinue:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMLoopZReplacement
; Patch record for 'Loopz'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmLoopZRecord
PATCHASMRECORD_INIT_EX PATMLoopZReplacement, , PATMLoopZJump, PATMLoopZSizeOverride, 3
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; jump if ZF=0 AND (E)CX != 0
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMLoopNZReplacement
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
jz NAME(PATMLoopNZReplacement_EndProc)
pushfd
PATMLoopNZSizeOverride:
DB 0x90 ; nop
dec ecx
jz PATMLoopNZContinue
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMLoopNZJump:
DD PATM_ASMFIX_JUMPDELTA
PATMLoopNZContinue:
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
ENDPROC PATMLoopNZReplacement
; Patch record for 'LoopNZ'
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmLoopNZRecord
PATCHASMRECORD_INIT_EX PATMLoopNZReplacement, , PATMLoopNZJump, PATMLoopNZSizeOverride, 3
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Global patch function for indirect calls
; Caller is responsible for clearing PATM_ASMFIX_INTERRUPTFLAG and doing:
; + 20 push [pTargetGC]
; + 16 pushfd
; + 12 push [JumpTableAddress]
; + 8 push [PATMRelReturnAddress]
; + 4 push [GuestReturnAddress]
;( + 0 return address )
;
; @note NEVER change this without bumping the SSM version
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMLookupAndCall
push eax
push edx
push edi
push ecx
mov eax, dword [esp+16+4] ; guest return address
mov dword [ss:PATM_ASMFIX_CALL_RETURN_ADDR], eax ; temporary storage
mov edx, dword [esp+16+20] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+16+12] ; jump table
mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
cmp ecx, 0
je near PATMLookupAndCall_QueryPATM
PATMLookupAndCall_SearchStart:
cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
je near PATMLookupAndCall_SearchHit
inc eax
cmp eax, ecx
jl near PATMLookupAndCall_SearchStart
PATMLookupAndCall_QueryPATM:
; nothing found -> let our trap handler try to find it
; @todo private ugly interface, since we have nothing generic at the moment
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
mov eax, PATM_ACTION_LOOKUP_ADDRESS
mov ecx, PATM_ACTION_MAGIC
; edx = GC address to find
; edi = jump table address
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
jmp near PATMLookupAndCall_SearchEnd
PATMLookupAndCall_Failure:
; return to caller; it must raise an error, due to patch to guest address translation (remember that there's only one copy of this code block).
pop ecx
pop edi
pop edx
pop eax
ret
PATMLookupAndCall_SearchHit:
mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
;@note can be zero, so the next check is required!!
PATMLookupAndCall_SearchEnd:
cmp eax, 0
je near PATMLookupAndCall_Failure
mov ecx, eax ; ECX = target address (relative!)
add ecx, PATM_ASMFIX_PATCHBASE ; Make it absolute
mov edx, dword PATM_ASMFIX_STACKPTR
cmp dword [ss:edx], PATM_STACK_SIZE
ja near PATMLookupAndCall_Failure ; should never happen actually!!!
cmp dword [ss:edx], 0
je near PATMLookupAndCall_Failure ; no more room
; save the patch return address on our private stack
sub dword [ss:edx], 4 ; sizeof(RTGCPTR)
mov eax, dword PATM_ASMFIX_STACKBASE
add eax, dword [ss:edx] ; stack base + stack position
mov edi, dword [esp+16+8] ; PATM return address
mov dword [ss:eax], edi ; relative address of patch return (instruction following this block)
; save the original return address as well (checked by ret to make sure the guest hasn't messed around with the stack)
mov edi, dword PATM_ASMFIX_STACKBASE_GUEST
add edi, dword [ss:edx] ; stack base (guest) + stack position
mov eax, dword [esp+16+4] ; guest return address
mov dword [ss:edi], eax
mov dword [ss:PATM_ASMFIX_CALL_PATCH_TARGET_ADDR], ecx ; temporarily store the target address
pop ecx
pop edi
pop edx
pop eax
add esp, 24 ; parameters + return address pushed by caller (changes the flags, but that shouldn't matter)
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
push edx
lea edx, [esp + 12 - 4] ; stack address to store return address
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOG_CALL
mov eax, PATM_ACTION_LOG_CALL
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop eax
%endif
push dword [ss:PATM_ASMFIX_CALL_RETURN_ADDR] ; push original guest return address
; the called function will set PATM_ASMFIX_INTERRUPTFLAG (!!)
jmp dword [ss:PATM_ASMFIX_CALL_PATCH_TARGET_ADDR]
; returning here -> do not add code here or after the jmp!!!!!
ENDPROC PATMLookupAndCall
; Patch record for indirect calls and jumps
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmLookupAndCallRecord
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT PATMLookupAndCall, 10
%else
PATCHASMRECORD_INIT PATMLookupAndCall, 9
%endif
DD PATM_ASMFIX_CALL_RETURN_ADDR, 0
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_PATCHBASE, 0
DD PATM_ASMFIX_STACKPTR, 0
DD PATM_ASMFIX_STACKBASE, 0
DD PATM_ASMFIX_STACKBASE_GUEST, 0
DD PATM_ASMFIX_CALL_PATCH_TARGET_ADDR, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_CALL_RETURN_ADDR, 0
DD PATM_ASMFIX_CALL_PATCH_TARGET_ADDR, 0
DD 0ffffffffh, 0ffffffffh
;
; Global patch function for indirect jumps
; Caller is responsible for clearing PATM_ASMFIX_INTERRUPTFLAG and doing:
; + 8 push [pTargetGC]
; + 4 push [JumpTableAddress]
;( + 0 return address )
; And saving eflags in PATM_ASMFIX_TEMP_EFLAGS
;
; @note NEVER change this without bumping the SSM version
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMLookupAndJump
push eax
push edx
push edi
push ecx
mov edx, dword [esp+16+8] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+16+4] ; jump table
mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
cmp ecx, 0
je near PATMLookupAndJump_QueryPATM
PATMLookupAndJump_SearchStart:
cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
je near PATMLookupAndJump_SearchHit
inc eax
cmp eax, ecx
jl near PATMLookupAndJump_SearchStart
PATMLookupAndJump_QueryPATM:
; nothing found -> let our trap handler try to find it
; @todo private ugly interface, since we have nothing generic at the moment
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
mov eax, PATM_ACTION_LOOKUP_ADDRESS
mov ecx, PATM_ACTION_MAGIC
; edx = GC address to find
; edi = jump table address
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
jmp near PATMLookupAndJump_SearchEnd
PATMLookupAndJump_Failure:
; return to caller; it must raise an error, due to patch to guest address translation (remember that there's only one copy of this code block).
pop ecx
pop edi
pop edx
pop eax
ret
PATMLookupAndJump_SearchHit:
mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
;@note can be zero, so the next check is required!!
PATMLookupAndJump_SearchEnd:
cmp eax, 0
je near PATMLookupAndJump_Failure
mov ecx, eax ; ECX = target address (relative!)
add ecx, PATM_ASMFIX_PATCHBASE ; Make it absolute
; save jump patch target
mov dword [ss:PATM_ASMFIX_TEMP_EAX], ecx
pop ecx
pop edi
pop edx
pop eax
add esp, 12 ; parameters + return address pushed by caller
; restore flags (just to be sure)
push dword [ss:PATM_ASMFIX_TEMP_EFLAGS]
popfd
; the jump destination will set PATM_ASMFIX_INTERRUPTFLAG (!!)
jmp dword [ss:PATM_ASMFIX_TEMP_EAX] ; call duplicated patch destination address
ENDPROC PATMLookupAndJump
; Patch record for indirect calls and jumps
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmLookupAndJumpRecord
PATCHASMRECORD_INIT PATMLookupAndJump, 5
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_PATCHBASE, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD PATM_ASMFIX_TEMP_EFLAGS, 0
DD PATM_ASMFIX_TEMP_EAX, 0
DD 0ffffffffh, 0ffffffffh
; Patch function for static calls
; @note static calls have only one lookup slot!
; Caller is responsible for clearing PATM_ASMFIX_INTERRUPTFLAG and adding:
; push [pTargetGC]
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMCall
pushfd
push PATM_ASMFIX_FIXUP ; fixup for jump table below
push PATM_ASMFIX_PATCHNEXTBLOCK
push PATM_ASMFIX_RETURNADDR
DB 0E8h ; call
DD PATM_ASMFIX_LOOKUP_AND_CALL_FUNCTION
; we only return in case of a failure
add esp, 12 ; pushed address of jump table
popfd
add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
%ifdef DEBUG
; for disassembly
jmp NAME(PATMCall_EndProc)
%endif
align 4
PATMCallTable:
DW 1 ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHDIRECTJUMPTABLE_SIZE DB 0 ; only one lookup slot
; returning here -> do not add code here or after the jmp!!!!!
ENDPROC PATMCall
; Patch record for direct calls
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmCallRecord
PATCHASMRECORD_INIT PATMCall, 5
DD PATM_ASMFIX_FIXUP, PATMCallTable - NAME(PATMCall)
DD PATM_ASMFIX_PATCHNEXTBLOCK, 0
DD PATM_ASMFIX_RETURNADDR, 0
DD PATM_ASMFIX_LOOKUP_AND_CALL_FUNCTION, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
; Patch function for indirect calls
; Caller is responsible for clearing PATM_ASMFIX_INTERRUPTFLAG and adding:
; push [pTargetGC]
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMCallIndirect
pushfd
push PATM_ASMFIX_FIXUP ; fixup for jump table below
push PATM_ASMFIX_PATCHNEXTBLOCK
push PATM_ASMFIX_RETURNADDR
DB 0E8h ; call
DD PATM_ASMFIX_LOOKUP_AND_CALL_FUNCTION
; we only return in case of a failure
add esp, 12 ; pushed address of jump table
popfd
add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
%ifdef DEBUG
; for disassembly
jmp NAME(PATMCallIndirect_EndProc)
%endif
align 4
PATMCallIndirectTable:
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
; returning here -> do not add code here or after the jmp!!!!!
ENDPROC PATMCallIndirect
; Patch record for indirect calls
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmCallIndirectRecord
PATCHASMRECORD_INIT PATMCallIndirect, 5
DD PATM_ASMFIX_FIXUP, PATMCallIndirectTable - NAME(PATMCallIndirect)
DD PATM_ASMFIX_PATCHNEXTBLOCK, 0
DD PATM_ASMFIX_RETURNADDR, 0
DD PATM_ASMFIX_LOOKUP_AND_CALL_FUNCTION, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Patch function for indirect jumps
; Caller is responsible for clearing PATM_ASMFIX_INTERRUPTFLAG and adding:
; push [pTargetGC]
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMJumpIndirect
; save flags (just to be sure)
pushfd
pop dword [ss:PATM_ASMFIX_TEMP_EFLAGS]
push PATM_ASMFIX_FIXUP ; fixup for jump table below
DB 0E8h ; call
DD PATM_ASMFIX_LOOKUP_AND_JUMP_FUNCTION
; we only return in case of a failure
add esp, 8 ; pushed address of jump table + pushed target address
; restore flags (just to be sure)
push dword [ss:PATM_ASMFIX_TEMP_EFLAGS]
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
%ifdef DEBUG
; for disassembly
jmp NAME(PATMJumpIndirect_EndProc)
%endif
align 4
PATMJumpIndirectTable:
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
; returning here -> do not add code here or after the jmp!!!!!
ENDPROC PATMJumpIndirect
; Patch record for indirect jumps
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmJumpIndirectRecord
PATCHASMRECORD_INIT PATMJumpIndirect, 5
DD PATM_ASMFIX_TEMP_EFLAGS, 0
DD PATM_ASMFIX_FIXUP, PATMJumpIndirectTable - NAME(PATMJumpIndirect)
DD PATM_ASMFIX_LOOKUP_AND_JUMP_FUNCTION, 0
DD PATM_ASMFIX_TEMP_EFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; return from duplicated function
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMRet
; probe stack here as we can't recover from page faults later on
not dword [esp-32]
not dword [esp-32]
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushfd
push eax
push PATM_ASMFIX_FIXUP
DB 0E8h ; call
DD PATM_ASMFIX_RETURN_FUNCTION
add esp, 4 ; pushed address of jump table
cmp eax, 0
jne near PATMRet_Success
pop eax
popfd
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
%ifdef DEBUG
; for disassembly
jmp PATMRet_Success
%endif
align 4
PATMRetTable:
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
DW 0 ; ulInsertPos
DD 0 ; cAddresses
TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
PATMRet_Success:
mov dword [esp+8], eax ; overwrite the saved return address
pop eax
popf
; caller will duplicate the ret or ret n instruction
; the patched call will set PATM_ASMFIX_INTERRUPTFLAG after the return!
ENDPROC PATMRet
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmRetRecord
PATCHASMRECORD_INIT PATMRet, 4
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_FIXUP, PATMRetTable - NAME(PATMRet)
DD PATM_ASMFIX_RETURN_FUNCTION, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; global function for implementing 'retn'
;
; Caller is responsible for right stack layout
; + 16 original return address
; + 12 eflags
; + 8 eax
; + 4 Jump table address
;( + 0 return address )
;
; @note assumes PATM_ASMFIX_INTERRUPTFLAG is zero
; @note assumes it can trash eax and eflags
;
; @returns eax=0 on failure
; otherwise return address in eax
;
; @note NEVER change this without bumping the SSM version
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMRetFunction
push ecx
push edx
push edi
; Event order:
; (@todo figure out which path is taken most often (1 or 2))
; 1) Check if the return patch address was pushed onto the PATM stack
; 2) Check if the return patch address can be found in the lookup table
; 3) Query return patch address from the hypervisor
; 1) Check if the return patch address was pushed on the PATM stack
cmp dword [ss:PATM_ASMFIX_STACKPTR], PATM_STACK_SIZE
jae near PATMRetFunction_FindReturnAddress
mov edx, dword PATM_ASMFIX_STACKPTR
; check if the return address is what we expect it to be
mov eax, dword PATM_ASMFIX_STACKBASE_GUEST
add eax, dword [ss:edx] ; stack base + stack position
mov eax, dword [ss:eax] ; original return address
cmp eax, dword [esp+12+16] ; pushed return address
; the return address was changed -> let our trap handler try to find it
; (can happen when the guest messes with the stack (seen it) or when we didn't call this function ourselves)
jne near PATMRetFunction_FindReturnAddress
; found it, convert relative to absolute patch address and return the result to the caller
mov eax, dword PATM_ASMFIX_STACKBASE
add eax, dword [ss:edx] ; stack base + stack position
mov eax, dword [ss:eax] ; relative patm return address
add eax, PATM_ASMFIX_PATCHBASE
%ifdef PATM_LOG_PATCHINSTR
push eax
push ebx
push ecx
push edx
mov edx, eax ; return address
lea ebx, [esp+16+12+16] ; stack address containing the return address
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOG_RET
mov eax, PATM_ACTION_LOG_RET
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop ebx
pop eax
%endif
add dword [ss:edx], 4 ; pop return address from the PATM stack (sizeof(RTGCPTR); @note hardcoded assumption!)
pop edi
pop edx
pop ecx
ret
PATMRetFunction_FindReturnAddress:
; 2) Check if the return patch address can be found in the lookup table
mov edx, dword [esp+12+16] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+12+4] ; jump table
mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
cmp ecx, 0
je near PATMRetFunction_AskHypervisor
PATMRetFunction_SearchStart:
cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
je near PATMRetFunction_SearchHit
inc eax
cmp eax, ecx
jl near PATMRetFunction_SearchStart
PATMRetFunction_AskHypervisor:
; 3) Query return patch address from the hypervisor
; @todo private ugly interface, since we have nothing generic at the moment
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
mov eax, PATM_ACTION_LOOKUP_ADDRESS
mov ecx, PATM_ACTION_MAGIC
mov edi, dword [esp+12+4] ; jump table address
mov edx, dword [esp+12+16] ; original return address
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
jmp near PATMRetFunction_SearchEnd
PATMRetFunction_SearchHit:
mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
;@note can be zero, so the next check is required!!
PATMRetFunction_SearchEnd:
cmp eax, 0
jz PATMRetFunction_Failure
add eax, PATM_ASMFIX_PATCHBASE
%ifdef PATM_LOG_PATCHINSTR
push eax
push ebx
push ecx
push edx
mov edx, eax ; return address
lea ebx, [esp+16+12+16] ; stack address containing the return address
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOG_RET
mov eax, PATM_ACTION_LOG_RET
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop edx
pop ecx
pop ebx
pop eax
%endif
pop edi
pop edx
pop ecx
ret
PATMRetFunction_Failure:
;signal error
xor eax, eax
pop edi
pop edx
pop ecx
ret
ENDPROC PATMRetFunction
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmRetFunctionRecord
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT PATMRetFunction, 9
%else
PATCHASMRECORD_INIT PATMRetFunction, 7
%endif
DD PATM_ASMFIX_STACKPTR, 0
DD PATM_ASMFIX_STACKPTR, 0
DD PATM_ASMFIX_STACKBASE_GUEST, 0
DD PATM_ASMFIX_STACKBASE, 0
DD PATM_ASMFIX_PATCHBASE, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_PENDINGACTION, 0
DD PATM_ASMFIX_PATCHBASE, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD 0ffffffffh, 0ffffffffh
;
; Jump to original instruction if IF=1
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMCheckIF
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
test dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
jnz PATMCheckIF_Safe
nop
; IF=0 -> unsafe, so we must call the duplicated function (which we don't do here)
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
jmp NAME(PATMCheckIF_EndProc)
PATMCheckIF_Safe:
; invalidate the PATM stack as we'll jump back to guest code
mov dword [ss:PATM_ASMFIX_STACKPTR], PATM_STACK_SIZE
%ifdef PATM_LOG_PATCHINSTR
push eax
push ecx
lock or dword [ss:PATM_ASMFIX_PENDINGACTION], PATM_ACTION_LOG_IF1
mov eax, PATM_ACTION_LOG_IF1
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
pop ecx
pop eax
%endif
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
; IF=1 -> we can safely jump back to the original instruction
DB 0xE9
PATMCheckIF_Jump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMCheckIF
; Patch record for call instructions
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmCheckIFRecord
%ifdef PATM_LOG_PATCHINSTR
PATCHASMRECORD_INIT_JUMP PATMCheckIF, PATMCheckIF_Jump, 6
%else
PATCHASMRECORD_INIT_JUMP PATMCheckIF, PATMCheckIF_Jump, 5
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_STACKPTR, 0
%ifdef PATM_LOG_PATCHINSTR
DD PATM_ASMFIX_PENDINGACTION, 0
%endif
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Jump back to guest if IF=1, else fault
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMJumpToGuest_IF1
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 0
pushf
test dword [ss:PATM_ASMFIX_VMFLAGS], X86_EFL_IF
jnz PATMJumpToGuest_IF1_Safe
nop
; IF=0 -> unsafe, so fault
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
PATM_INT3
PATMJumpToGuest_IF1_Safe:
; IF=1 -> we can safely jump back to the original instruction
popf
mov dword [ss:PATM_ASMFIX_INTERRUPTFLAG], 1
DB 0xE9
PATMJumpToGuest_IF1_Jump:
DD PATM_ASMFIX_JUMPDELTA
ENDPROC PATMJumpToGuest_IF1
; Patch record for call instructions
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME PATMJumpToGuest_IF1Record
PATCHASMRECORD_INIT_JUMP PATMJumpToGuest_IF1, PATMJumpToGuest_IF1_Jump, 4
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_VMFLAGS, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD PATM_ASMFIX_INTERRUPTFLAG, 0
DD 0ffffffffh, 0ffffffffh
;
; Check and correct RPL of pushed ss.
;
BEGIN_PATCH_CODE_SECTION
BEGINPROC PATMMovFromSS
push eax
pushfd
mov ax, ss
and ax, 3
cmp ax, 1
jne near PATMMovFromSS_Continue
and dword [esp+8], ~3 ; clear RPL 1
PATMMovFromSS_Continue:
popfd
pop eax
ENDPROC PATMMovFromSS
BEGIN_PATCH_RODATA_SECTION
GLOBALNAME g_patmMovFromSSRecord
PATCHASMRECORD_INIT PATMMovFromSS, 0
DD 0ffffffffh, 0ffffffffh
;; For assertion during init (to make absolutely sure the flags are in sync in vm.mac & vm.h)
BEGINCONST
GLOBALNAME g_fPatmInterruptFlag
DD VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST