; $Id: PATMA.asm 62478 2016-07-22 18:29:06Z vboxsync $ ;; @file ; PATM Assembly Routines. ; ; Copyright (C) 2006-2016 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