;*******************************************************************************
;*******************************************************************************
%ifdef VBOX_WITH_STATISTICS
mov dword [ss:PATM_INTERRUPTFLAG], 0
inc dword [ss:PATM_ALLPATCHCALLS]
inc dword [ss:PATM_PERPATCHCALLS]
mov dword [ss:PATM_INTERRUPTFLAG], 1
; Patch record for statistics
GLOBALNAME PATMStatsRecord
DD PATMStats_End - PATMStats_Start
mov dword [ss:PATM_INTERRUPTFLAG], 1
; Patch record for setting PATM_INTERRUPTFLAG
GLOBALNAME PATMSetPIFRecord
DD PATMSetPIF_End - PATMSetPIF_Start
; Clear PATM_INTERRUPTFLAG
; probe stack here as we can't recover from page faults later on
mov dword [ss:PATM_INTERRUPTFLAG], 0
; Patch record for clearing PATM_INTERRUPTFLAG
GLOBALNAME PATMClearPIFRecord
DD PATMClearPIF_End - PATMClearPIF_Start
; Clear PATM_INHIBITIRQADDR and fault if IF=0
BEGINPROC PATMClearInhibitIRQFaultIF0
PATMClearInhibitIRQFaultIF0_Start:
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INHIBITIRQADDR], 0
test dword [ss:PATM_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_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
jz PATMClearInhibitIRQFaultIF0_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_TEMP_EAX], eax
mov dword [ss:PATM_TEMP_ECX], ecx
mov dword [ss:PATM_TEMP_EDI], edi
mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
PATMClearInhibitIRQFaultIF0_Fault:
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMClearInhibitIRQFaultIF0_Continue:
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMClearInhibitIRQFaultIF0_End:
ENDPROC PATMClearInhibitIRQFaultIF0
; Patch record for clearing PATM_INHIBITIRQADDR
GLOBALNAME PATMClearInhibitIRQFaultIF0Record
DD PATMClearInhibitIRQFaultIF0_Start
DD PATMClearInhibitIRQFaultIF0_End - PATMClearInhibitIRQFaultIF0_Start
DD PATM_TEMP_RESTORE_FLAGS
; Clear PATM_INHIBITIRQADDR and continue if IF=0 (duplicated function only; never jump back to guest code afterwards!!)
BEGINPROC PATMClearInhibitIRQContIF0
PATMClearInhibitIRQContIF0_Start:
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INHIBITIRQADDR], 0
test dword [ss:PATM_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_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
jz PATMClearInhibitIRQContIF0_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_TEMP_EAX], eax
mov dword [ss:PATM_TEMP_ECX], ecx
mov dword [ss:PATM_TEMP_EDI], edi
mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
PATMClearInhibitIRQContIF0_Continue:
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMClearInhibitIRQContIF0_End:
ENDPROC PATMClearInhibitIRQContIF0
; Patch record for clearing PATM_INHIBITIRQADDR
GLOBALNAME PATMClearInhibitIRQContIF0Record
DD PATMClearInhibitIRQContIF0_Start
DD PATMClearInhibitIRQContIF0_End - PATMClearInhibitIRQContIF0_Start
DD PATM_TEMP_RESTORE_FLAGS
BEGINPROC PATMCliReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
%ifdef PATM_LOG_IF_CHANGES
mov eax, PATM_ACTION_LOG_CLI
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
and dword [ss:PATM_VMFLAGS], ~X86_EFL_IF
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMCliReplacement
DD PATMCliJump - PATMCliStart
DD PATMCliEnd - PATMCliStart
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
BEGINPROC PATMStiReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INHIBITIRQADDR], PATM_NEXTINSTRADDR
%ifdef PATM_LOG_IF_CHANGES
mov eax, PATM_ACTION_LOG_STI
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
or dword [ss:PATM_VMFLAGS], X86_EFL_IF
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMStiReplacement
DD PATMStiEnd - PATMStiStart
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
; Trampoline code for trap entry (without error code on the stack)
; esp + 16 - SS (if transfer to inner ring)
; esp + 12 - ESP (if transfer to inner ring)
mov dword [ss:PATM_INTERRUPTFLAG], 0
; make sure the saved CS selector for ring 1 is made 0
and dword [esp+8], dword ~1 ; yasm / nasm dword
; correct EFLAGS on the stack to include the current IOPL
mov eax, dword [ss:PATM_VMFLAGS]
and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
mov dword [ss:PATM_INTERRUPTFLAG], 1
; Patch record for trap gate entrypoint
GLOBALNAME PATMTrapEntryRecord
DD PATMTrapEntryJump - PATMTrapEntryStart
DD PATMTrapEntryEnd - PATMTrapEntryStart
; Trampoline code for trap entry (with error code on the stack)
; esp + 20 - SS (if transfer to inner ring)
; esp + 16 - ESP (if transfer to inner ring)
BEGINPROC PATMTrapEntryErrorCode
PATMTrapErrorCodeEntryStart:
mov dword [ss:PATM_INTERRUPTFLAG], 0
; make sure the saved CS selector for ring 1 is made 0
jnz PATMTrapErrorCodeNoRing1
jz PATMTrapErrorCodeNoRing1
and dword [esp+12], dword ~1 ; yasm / nasm dword
PATMTrapErrorCodeNoRing1:
; correct EFLAGS on the stack to include the current IOPL
mov eax, dword [ss:PATM_VMFLAGS]
and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(error code)+4(eax)
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMTrapErrorCodeEntryJump:
PATMTrapErrorCodeEntryEnd:
ENDPROC PATMTrapEntryErrorCode
; Patch record for trap gate entrypoint
GLOBALNAME PATMTrapEntryRecordErrorCode
DD PATMTrapErrorCodeEntryStart
DD PATMTrapErrorCodeEntryJump - PATMTrapErrorCodeEntryStart
DD PATMTrapErrorCodeEntryEnd - PATMTrapErrorCodeEntryStart
; Trampoline code for interrupt gate entry (without error code on the stack)
; esp + 16 - SS (if transfer to inner ring)
; esp + 12 - ESP (if transfer to inner ring)
mov dword [ss:PATM_INTERRUPTFLAG], 0
; make sure the saved CS selector for ring 1 is made 0
and dword [esp+8], dword ~1 ; yasm / nasm dword
; correct EFLAGS on the stack to include the current IOPL
mov eax, dword [ss:PATM_VMFLAGS]
and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
mov dword [ss:PATM_INTERRUPTFLAG], 1
; Patch record for interrupt gate entrypoint
GLOBALNAME PATMIntEntryRecord
DD PATMIntEntryEnd - PATMIntEntryStart
; Trampoline code for interrupt gate entry (*with* error code on the stack)
; esp + 20 - SS (if transfer to inner ring)
; esp + 16 - ESP (if transfer to inner ring)
BEGINPROC PATMIntEntryErrorCode
PATMIntEntryErrorCodeStart:
mov dword [ss:PATM_INTERRUPTFLAG], 0
; make sure the saved CS selector for ring 1 is made 0
jnz PATMIntNoRing1_ErrorCode
jz PATMIntNoRing1_ErrorCode
and dword [esp+12], dword ~1 ; yasm / nasm dword
PATMIntNoRing1_ErrorCode:
; correct EFLAGS on the stack to include the current IOPL
mov eax, dword [ss:PATM_VMFLAGS]
and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(eax)+4(error code)
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMIntEntryErrorCodeEnd:
ENDPROC PATMIntEntryErrorCode
; Patch record for interrupt gate entrypoint
GLOBALNAME PATMIntEntryRecordErrorCode
DD PATMIntEntryErrorCodeStart
DD PATMIntEntryErrorCodeEnd - PATMIntEntryErrorCodeStart
; 32 bits Popf replacement that faults when IF remains 0
BEGINPROC PATMPopf32Replacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
test dword [esp], X86_EFL_IF
mov dword [ss:PATM_INTERRUPTFLAG], 1
pop dword [ss:PATM_VMFLAGS]
push dword [ss:PATM_VMFLAGS]
; if interrupts are pending, then we must go back to the host context to handle them!
test dword [ss:PATM_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_TEMP_EAX], eax
mov dword [ss:PATM_TEMP_ECX], ecx
mov dword [ss:PATM_TEMP_EDI], edi
mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_NEXTINSTRADDR
popfd ; restore flags we pushed above (the or instruction changes the flags as well)
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
popfd ; restore flags we pushed above
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPopf32Replacement
; Patch record for 'popfd'
GLOBALNAME PATMPopf32Record
DD PATMPopf32Jump - PATMPopf32Start
DD PATMPopf32End - PATMPopf32Start
DD PATM_TEMP_RESTORE_FLAGS
; no need to check the IF flag when popf isn't an exit point of a patch (
e.g. function duplication)
BEGINPROC PATMPopf32Replacement_NoExit
mov dword [ss:PATM_INTERRUPTFLAG], 0
%ifdef PATM_LOG_IF_CHANGES
mov eax, PATM_ACTION_LOG_POPF_IF1
test dword [esp+8], X86_EFL_IF
mov eax, PATM_ACTION_LOG_POPF_IF0
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
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_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
jz PATMPopf32_NoExit_Continue
; Go to our hypervisor trap handler to dispatch the pending irq
mov dword [ss:PATM_TEMP_EAX], eax
mov dword [ss:PATM_TEMP_ECX], ecx
mov dword [ss:PATM_TEMP_EDI], edi
mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
mov edi, PATM_NEXTINSTRADDR
pop dword [ss:PATM_VMFLAGS] ; restore flags now (the or instruction changes the flags as well)
push dword [ss:PATM_VMFLAGS]
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
PATMPopf32_NoExit_Continue:
pop dword [ss:PATM_VMFLAGS]
push dword [ss:PATM_VMFLAGS]
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPopf32Replacement_NoExit
; Patch record for 'popfd'
GLOBALNAME PATMPopf32Record_NoExit
DD PATMPopf32_NoExitStart
DD PATMPopf32_NoExitEnd - PATMPopf32_NoExitStart
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
DD PATM_TEMP_RESTORE_FLAGS
; 16 bits Popf replacement that faults when IF remains 0
BEGINPROC PATMPopf16Replacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
test word [esp], X86_EFL_IF
mov dword [ss:PATM_INTERRUPTFLAG], 1
; 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_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
mov dword [ss:PATM_INTERRUPTFLAG], 1
pop word [ss:PATM_VMFLAGS]
push word [ss:PATM_VMFLAGS]
and dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
or dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
popf ;after the and and or operations!! (flags must be preserved)
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPopf16Replacement
; Patch record for 'popf'
GLOBALNAME PATMPopf16Record
DD PATMPopf16Jump - PATMPopf16Start
DD PATMPopf16End - PATMPopf16Start
; 16 bits Popf replacement that faults when IF remains 0
; @todo not necessary to fault in that case (see 32 bits version)
BEGINPROC PATMPopf16Replacement_NoExit
mov dword [ss:PATM_INTERRUPTFLAG], 0
test word [esp], X86_EFL_IF
mov dword [ss:PATM_INTERRUPTFLAG], 1
; 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_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
jz PATMPopf16_Continue_NoExit
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMPopf16_Continue_NoExit:
pop word [ss:PATM_VMFLAGS]
push word [ss:PATM_VMFLAGS]
and dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
or dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
popf ;after the and and or operations!! (flags must be preserved)
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPopf16Replacement_NoExit
; Patch record for 'popf'
GLOBALNAME PATMPopf16Record_NoExit
DD PATMPopf16Start_NoExit
DD PATMPopf16End_NoExit - PATMPopf16Start_NoExit
BEGINPROC PATMPushf32Replacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
%ifdef PATM_LOG_IF_CHANGES
mov eax, PATM_ACTION_LOG_PUSHF
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
or eax, dword [ss:PATM_VMFLAGS]
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPushf32Replacement
; Patch record for 'pushfd'
GLOBALNAME PATMPushf32Record
DD PATMPushf32End - PATMPushf32Start
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
BEGINPROC PATMPushf16Replacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
or eax, dword [ss:PATM_VMFLAGS]
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPushf16Replacement
; Patch record for 'pushf'
GLOBALNAME PATMPushf16Record
DD PATMPushf16End - PATMPushf16Start
BEGINPROC PATMPushCSReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
and dword [esp+4], dword ~1 ; yasm / nasm dword
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMPushCSReplacement
; Patch record for 'push cs'
GLOBALNAME PATMPushCSRecord
DD PATMPushCSJump - PATMPushCSStart
DD PATMPushCSEnd - PATMPushCSStart
;;****************************************************
;; uVMFlags &= ~X86_EFL_IF
;;****************************************************
BEGINPROC PATMIretReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
test dword [esp], X86_EFL_NT
test dword [esp+12], X86_EFL_VM
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;;@todo: not correct for iret back to ring 2!!!!!
;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
test dword [esp+12], X86_EFL_IF
; if interrupts are pending, then we must go back to the host context to handle them!
; @@todo fix this properly, so we can dispatch pending interrupts in GC
test dword [ss:PATM_VM_FORCEDACTIONS], VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST
; This section must *always* be executed (!!)
; Extract the IOPL from the return flags, save them to our virtual flags and
and dword [ss:PATM_VMFLAGS], ~X86_EFL_IOPL
or dword [ss:PATM_VMFLAGS], eax
and dword [esp+12], ~X86_EFL_IOPL
; Set IF again; below we make sure this won't cause problems.
or dword [ss:PATM_VMFLAGS], X86_EFL_IF
%ifdef PATM_LOG_IF_CHANGES
mov eax, PATM_ACTION_LOG_IRET
lock or dword [ss:PATM_PENDINGACTION], eax
mov ecx, PATM_ACTION_MAGIC
db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
; make sure iret is executed fully (including the iret below; cli ... iret can otherwise be interrupted)
mov dword [ss:PATM_INHIBITIRQADDR], PATM_CURINSTRADDR
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMIretReplacement
; Patch record for 'iretd'
GLOBALNAME PATMIretRecord
DD PATMIretEnd- PATMIretStart
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
align 16 ; yasm / nasm diff - remove me!
BEGINPROC PATMCpuidReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
cmp eax, PATM_CPUID_STD_MAX
cmp eax, PATM_CPUID_EXT_MAX
mov eax, PATM_CPUID_DEF_PTR
mov edx, PATM_CPUID_STD_PTR
and eax, 0ffh ; strictly speaking not necessary.
mov edx, PATM_CPUID_EXT_PTR
lea eax, [ss:eax * 4] ; 4 entries...
lea eax, [ss:eax * 4] ; 4 bytes each
mov edx, [ss:eax + 12] ; CPUMCPUID layout assumptions!
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMCpuidReplacement
; Patch record for 'cpuid'
GLOBALNAME PATMCpuidRecord
DD PATMCpuidEnd- PATMCpuidStart
BEGINPROC PATMJEcxReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
cmp ecx, dword 0 ; yasm / nasm dword
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMJEcxReplacement
; Patch record for 'JEcx'
GLOBALNAME PATMJEcxRecord
DD PATMJEcxJump - PATMJEcxStart
DD PATMJEcxSizeOverride - PATMJEcxStart
DD PATMJEcxEnd- PATMJEcxStart
align 16; yasm / nasm diffing. remove me!
BEGINPROC PATMLoopReplacement
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMLoopReplacement
; Patch record for 'Loop'
GLOBALNAME PATMLoopRecord
DD PATMLoopJump - PATMLoopStart
DD PATMLoopSizeOverride - PATMLoopStart
DD PATMLoopEnd- PATMLoopStart
BEGINPROC PATMLoopZReplacement
; jump if ZF=1 AND (E)CX != 0
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMLoopZReplacement
; Patch record for 'Loopz'
GLOBALNAME PATMLoopZRecord
DD PATMLoopZJump - PATMLoopZStart
DD PATMLoopZSizeOverride - PATMLoopZStart
DD PATMLoopZEnd- PATMLoopZStart
BEGINPROC PATMLoopNZReplacement
; jump if ZF=0 AND (E)CX != 0
mov dword [ss:PATM_INTERRUPTFLAG], 0
mov dword [ss:PATM_INTERRUPTFLAG], 1
mov dword [ss:PATM_INTERRUPTFLAG], 1
ENDPROC PATMLoopNZReplacement
; Patch record for 'LoopNZ'
GLOBALNAME PATMLoopNZRecord
DD PATMLoopNZJump - PATMLoopNZStart
DD PATMLoopNZSizeOverride - PATMLoopNZStart
DD PATMLoopNZEnd- PATMLoopNZStart
; Global patch function for indirect calls
; Caller is responsible for clearing PATM_INTERRUPTFLAG and doing:
; + 12 push [JumpTableAddress]
; + 8 push [PATMRelReturnAddress]
; + 4 push [GuestReturnAddress]
; @note NEVER change this without bumping the SSM version
BEGINPROC PATMLookupAndCall
mov edx, dword [esp+16+20] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+16+12] ; jump table
je near PATMLookupAndCall_QueryPATM
PATMLookupAndCall_SearchStart:
je near PATMLookupAndCall_SearchHit
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_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).
PATMLookupAndCall_SearchHit:
;@note can be zero, so the next check is required!!
PATMLookupAndCall_SearchEnd:
je near PATMLookupAndCall_Failure
mov ecx, eax ; ECX = target address (relative!)
add ecx, PATM_PATCHBASE ; Make it absolute
mov edx, dword PATM_STACKPTR
cmp dword [ss:edx], PATM_STACK_SIZE
ja near PATMLookupAndCall_Failure ; should never happen actually!!!
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_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_STACKBASE_GUEST
add edi, dword [ss:edx] ; stack base (guest) + stack position
mov eax, dword [esp+16+4] ; guest return address
push ecx ; temporarily store the target address on the stack
add esp, 24 ; parameters + return address pushed by caller (changes the flags, but that shouldn't matter)
push dword [esp - 20] ; push original guest return address
; the called function will set PATM_INTERRUPTFLAG (!!)
jmp dword [esp-40] ; call duplicated patch function
; returning here -> do not add code here or after the jmp!!!!!
ENDPROC PATMLookupAndCall
; Patch record for indirect calls and jumps
GLOBALNAME PATMLookupAndCallRecord
DD PATMLookupAndCallStart
DD PATMLookupAndCallEnd - PATMLookupAndCallStart
; Global patch function for indirect jumps
; Caller is responsible for clearing PATM_INTERRUPTFLAG and doing:
; + 4 push [JumpTableAddress]
; And saving eflags in PATM_TEMP_EFLAGS
; @note NEVER change this without bumping the SSM version
BEGINPROC PATMLookupAndJump
mov edx, dword [esp+16+8] ; pushed target address
xor eax, eax ; default result -> nothing found
mov edi, dword [esp+16+4] ; jump table
je near PATMLookupAndJump_QueryPATM
PATMLookupAndJump_SearchStart:
je near PATMLookupAndJump_SearchHit
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_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).
PATMLookupAndJump_SearchHit:
;@note can be zero, so the next check is required!!
PATMLookupAndJump_SearchEnd:
je near PATMLookupAndJump_Failure
mov ecx, eax ; ECX = target address (relative!)
add ecx, PATM_PATCHBASE ; Make it absolute
mov dword [ss:PATM_TEMP_EAX], ecx
add esp, 12 ; parameters + return address pushed by caller
; restore flags (just to be sure)
push dword [ss:PATM_TEMP_EFLAGS]
; the jump destination will set PATM_INTERRUPTFLAG (!!)
jmp dword [ss:PATM_TEMP_EAX] ; call duplicated patch destination address
ENDPROC PATMLookupAndJump
; Patch record for indirect calls and jumps
GLOBALNAME PATMLookupAndJumpRecord
DD PATMLookupAndJumpStart
DD PATMLookupAndJumpEnd - PATMLookupAndJumpStart
; Patch function for static calls
; @note static calls have only one lookup slot!
; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
push PATM_FIXUP ; fixup for jump table below
DD PATM_LOOKUP_AND_CALL_FUNCTION
; we only return in case of a failure
add esp, 12 ; pushed address of jump table
add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
mov dword [ss:PATM_INTERRUPTFLAG], 1
RESB PATCHDIRECTJUMPTABLE_SIZE ; only one lookup slot
; returning here -> do not add code here or after the jmp!!!!!
; Patch record for direct calls
GLOBALNAME PATMCallRecord
DD PATMCallEnd - PATMCallStart
DD PATMCallTable - PATMCallStart
DD PATM_LOOKUP_AND_CALL_FUNCTION
; Patch function for indirect calls
; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
BEGINPROC PATMCallIndirect
push PATM_FIXUP ; fixup for jump table below
DD PATM_LOOKUP_AND_CALL_FUNCTION
; we only return in case of a failure
add esp, 12 ; pushed address of jump table
add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
mov dword [ss:PATM_INTERRUPTFLAG], 1
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
RESB PATCHJUMPTABLE_SIZE ; lookup slots
; returning here -> do not add code here or after the jmp!!!!!
; Patch record for indirect calls
GLOBALNAME PATMCallIndirectRecord
DD PATMCallIndirectEnd - PATMCallIndirectStart
DD PATMCallIndirectTable - PATMCallIndirectStart
DD PATM_LOOKUP_AND_CALL_FUNCTION
; Patch function for indirect jumps
; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
BEGINPROC PATMJumpIndirect
; save flags (just to be sure)
pop dword [ss:PATM_TEMP_EFLAGS]
push PATM_FIXUP ; fixup for jump table below
DD PATM_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_TEMP_EFLAGS]
mov dword [ss:PATM_INTERRUPTFLAG], 1
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
RESB PATCHJUMPTABLE_SIZE ; lookup slots
; returning here -> do not add code here or after the jmp!!!!!
; Patch record for indirect jumps
GLOBALNAME PATMJumpIndirectRecord
DD PATMJumpIndirectEnd - PATMJumpIndirectStart
DD PATMJumpIndirectTable - PATMJumpIndirectStart
DD PATM_LOOKUP_AND_JUMP_FUNCTION
; return from duplicated function
; probe stack here as we can't recover from page faults later on
mov dword [ss:PATM_INTERRUPTFLAG], 0
add esp, 4 ; pushed address of jump table
mov dword [ss:PATM_INTERRUPTFLAG], 1
DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
RESB PATCHJUMPTABLE_SIZE ; lookup slots
mov dword [esp+8], eax ; overwrite the saved return address
; caller will duplicate the ret or ret n instruction
; the patched call will set PATM_INTERRUPTFLAG after the return!
DD PATMRet_End - PATMRet_Start
DD PATMRetTable - PATMRet_Start
; global function for implementing 'retn'
; Caller is responsible for right stack layout
; + 16 original return address
; @note assumes PATM_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
BEGINPROC PATMRetFunction
; (@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_STACKPTR], PATM_STACK_SIZE
jae near PATMRetFunction_FindReturnAddress
mov edx, dword PATM_STACKPTR
; check if the return address is what we expect it to be
mov eax, dword PATM_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_STACKBASE
add eax, dword [ss:edx] ; stack base + stack position
mov eax, dword [ss:eax] ; relative patm return address
add dword [ss:edx], 4 ; pop return address from the PATM stack (sizeof(RTGCPTR); @note hardcoded assumption!)
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
je near PATMRetFunction_AskHypervisor
PATMRetFunction_SearchStart:
je near PATMRetFunction_SearchHit
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_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:
;@note can be zero, so the next check is required!!
PATMRetFunction_SearchEnd:
jz PATMRetFunction_Failure
GLOBALNAME PATMRetFunctionRecord
DD PATMRetFunction_End - PATMRetFunction_Start
; Jump to original instruction if IF=1
mov dword [ss:PATM_INTERRUPTFLAG], 0
test dword [ss:PATM_VMFLAGS], X86_EFL_IF
; IF=0 -> unsafe, so we must call the duplicated function (which we don't do here)
mov dword [ss:PATM_INTERRUPTFLAG], 1
; invalidate the PATM stack as we'll jump back to guest code
mov dword [ss:PATM_STACKPTR], PATM_STACK_SIZE
%ifdef PATM_LOG_IF_CHANGES
lock or dword [ss:PATM_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)
mov dword [ss:PATM_INTERRUPTFLAG], 1
; IF=1 -> we can safely jump back to the original instruction
; Patch record for call instructions
GLOBALNAME PATMCheckIFRecord
DD PATMCheckIF_Jump - PATMCheckIF_Start
DD PATMCheckIF_End - PATMCheckIF_Start
%ifdef PATM_LOG_IF_CHANGES
%ifdef PATM_LOG_IF_CHANGES
; Jump back to guest if IF=1, else fault
BEGINPROC PATMJumpToGuest_IF1
PATMJumpToGuest_IF1_Start:
mov dword [ss:PATM_INTERRUPTFLAG], 0
test dword [ss:PATM_VMFLAGS], X86_EFL_IF
jnz PATMJumpToGuest_IF1_Safe
; IF=0 -> unsafe, so fault
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMJumpToGuest_IF1_Safe:
; IF=1 -> we can safely jump back to the original instruction
mov dword [ss:PATM_INTERRUPTFLAG], 1
PATMJumpToGuest_IF1_Jump:
ENDPROC PATMJumpToGuest_IF1
; Patch record for call instructions
GLOBALNAME PATMJumpToGuest_IF1Record
DD PATMJumpToGuest_IF1_Start
DD PATMJumpToGuest_IF1_Jump - PATMJumpToGuest_IF1_Start
DD PATMJumpToGuest_IF1_End - PATMJumpToGuest_IF1_Start
; For assertion during init (to make absolutely sure the flags are in sync in
vm.mac &
vm.h)
GLOBALNAME PATMInterruptFlag
DD VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST