PATMAll.cpp revision d103596a5eedc4a4d3460f6b5e456d5177534ed9
/* $Id$ */
/** @file
* PATM - The Patch Manager, all contexts.
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PATM
#include "PATMInternal.h"
#include "PATMA.h"
#include <VBox/disopcode.h>
/**
* Load virtualized flags.
*
* This function is called from CPUMRawEnter(). It doesn't have to update the
* IF and IOPL eflags bits, the caller will enforce those to set and 0 respectively.
*
* @param pVM Pointer to the VM.
* @param pCtxCore The cpu context core.
* @see pg_raw
*/
{
/*
* Currently we don't bother to check whether PATM is enabled or not.
* For all cases where it isn't, IOPL will be safe and IF will be set.
*/
AssertMsg((efl & X86_EFL_IF) || PATMShouldUseRawMode(pVM, (RTRCPTR)pCtxCore->eip), ("X86_EFL_IF is clear and PATM is disabled! (eip=%RRv eflags=%08x fPATM=%d pPATMGC=%RRv-%RRv\n", pCtxCore->eip, pCtxCore->eflags.u32, PATMIsEnabled(pVM), pVM->patm.s.pPatchMemGC, pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem));
AssertReleaseMsg(CTXSUFF(pVM->patm.s.pGCState)->fPIF || fPatchCode, ("fPIF=%d eip=%RRv\n", CTXSUFF(pVM->patm.s.pGCState)->fPIF, pCtxCore->eip));
efl |= X86_EFL_IF;
#ifdef IN_RING3
#ifdef PATM_EMULATE_SYSENTER
/* Check if the sysenter handler has changed. */
)
{
{
{
if (rc == VINF_SUCCESS)
{
}
}
else
}
}
else
{
}
#endif
#endif
}
/**
* Restores virtualized flags.
*
* This function is called from CPUMRawLeave(). It will update the eflags register.
*
** @note Only here we are allowed to switch back to guest code (without a special reason such as a trap in patch code)!!
*
* @param pVM Pointer to the VM.
* @param pCtxCore The cpu context core.
* @param rawRC Raw mode return code
* @see @ref pg_raw
*/
{
/*
* We will only be called if PATMRawEnter was previously called.
*/
efl = (efl & ~PATM_VIRTUAL_FLAGS_MASK) | (CTXSUFF(pVM->patm.s.pGCState)->uVMFlags & PATM_VIRTUAL_FLAGS_MASK);
AssertReleaseMsg((efl & X86_EFL_IF) || fPatchCode || rawRC == VINF_PATM_PENDING_IRQ_AFTER_IRET || RT_FAILURE(rawRC), ("Inconsistent state at %RRv rc=%Rrc\n", pCtxCore->eip, rawRC));
AssertReleaseMsg(CTXSUFF(pVM->patm.s.pGCState)->fPIF || fPatchCode || RT_FAILURE(rawRC), ("fPIF=%d eip=%RRv rc=%Rrc\n", CTXSUFF(pVM->patm.s.pGCState)->fPIF, pCtxCore->eip, rawRC));
#ifdef IN_RING3
if ( (efl & X86_EFL_IF)
&& fPatchCode
)
{
if ( rawRC < VINF_PATM_LEAVE_RC_FIRST
|| rawRC > VINF_PATM_LEAVE_RC_LAST)
{
/*
* Golden rules:
* - Don't interrupt special patch streams that replace special instructions
* - Don't break instruction fusing (sti, pop ss, mov ss)
* - Don't go back to an instruction that has been overwritten by a patch jump
* - Don't interrupt an idt handler on entry (1st instruction); technically incorrect
*
*/
{
if (enmState == PATMTRANS_SAFE)
{
Log(("Switchback from %RRv to %RRv (Psp=%x)\n", pCtxCore->eip, pOrgInstrGC, CTXSUFF(pVM->patm.s.pGCState)->Psp));
fPatchCode = false; /* to reset the stack ptr */
CTXSUFF(pVM->patm.s.pGCState)->GCPtrInhibitInterrupts = 0; /* reset this pointer; safe otherwise the state would be PATMTRANS_INHIBITIRQ */
}
else
{
}
}
else
{
LogFlow(("Patch address %RRv can't be interrupted (fPIF=%d)!\n", pCtxCore->eip, CTXSUFF(pVM->patm.s.pGCState)->fPIF));
}
}
}
#else /* !IN_RING3 */
AssertMsgFailed(("!IN_RING3"));
#endif /* !IN_RING3 */
if (!fPatchCode)
{
{
}
/* Reset the stack pointer to the top of the stack. */
#ifdef DEBUG
{
}
#endif
}
}
/**
* Get the EFLAGS.
* This is a worker for CPUMRawGetEFlags().
*
* @returns The eflags.
* @param pVM Pointer to the VM.
* @param pCtxCore The context core.
*/
{
return efl;
}
/**
* Updates the EFLAGS.
* This is a worker for CPUMRawSetEFlags().
*
* @param pVM Pointer to the VM.
* @param pCtxCore The context core.
* @param efl The new EFLAGS value.
*/
{
efl |= X86_EFL_IF;
}
/**
* Check if we must use raw mode (patch code being executed)
*
* @param pVM Pointer to the VM.
* @param pAddrGC Guest context address
*/
{
return ( PATMIsEnabled(pVM)
&& ((pAddrGC >= (RTRCPTR)pVM->patm.s.pPatchMemGC && pAddrGC < (RTRCPTR)((RTRCUINTPTR)pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem)))) ? true : false;
}
/**
* Returns the guest context pointer and size of the GC context structure
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
}
/**
* Checks whether the GC address is part of our patch region
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pAddrGC Guest context address
* @internal
*/
{
return (PATMIsEnabled(pVM) && pAddrGC - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem) ? true : false;
}
/**
* Set parameters for pending MMIO patch operation
*
* @returns VBox status code.
* @param pDevIns Device instance.
* @param GCPhys MMIO physical address
* @param pCachedData GC pointer to cached data
*/
{
if (!HMIsEnabled(pVM))
{
}
return VINF_SUCCESS;
}
/**
* Checks if the interrupt flag is enabled or not.
*
* @returns true if it's enabled.
* @returns false if it's disabled.
*
* @param pVM Pointer to the VM.
* @todo CPUM should wrap this, EM.cpp shouldn't call us.
*/
{
}
/**
* Checks if the interrupt flag is enabled or not.
*
* @returns true if it's enabled.
* @returns false if it's disabled.
*
* @param pVM Pointer to the VM.
* @param pCtxCore CPU context
* @todo CPUM should wrap this, EM.cpp shouldn't call us.
*/
{
if (PATMIsEnabled(pVM))
{
return false;
}
}
/**
* Check if the instruction is patched as a duplicated function
*
* @returns patch record
* @param pVM Pointer to the VM.
* @param pInstrGC Guest context point to the instruction
*
*/
{
pRec = (PPATMPATCHREC)RTAvloU32Get(&CTXSUFF(pVM->patm.s.PatchLookupTree)->PatchTree, (AVLOU32KEY)pInstrGC);
if ( pRec
)
return pRec;
return 0;
}
/**
* Checks if the int 3 was caused by a patched instruction
*
* @returns VBox status
*
* @param pVM Pointer to the VM.
* @param pInstrGC Instruction pointer
* @param pOpcode Original instruction opcode (out, optional)
* @param pSize Original instruction size (out, optional)
*/
{
pRec = (PPATMPATCHREC)RTAvloU32Get(&CTXSUFF(pVM->patm.s.PatchLookupTree)->PatchTree, (AVLOU32KEY)pInstrGC);
if ( pRec
)
{
return true;
}
return false;
}
/**
* Emulate sysenter, sysexit and syscall instructions
*
* @returns VBox status
*
* @param pVM Pointer to the VM.
* @param pCtxCore The relevant core context.
* @param pCpu Disassembly context
*/
{
{
goto end;
Log2(("PATMSysCall: sysenter from %RRv to %RRv\n", pRegFrame->eip, pVM->patm.s.pfnSysEnterPatchGC));
/** @todo the base and limit are forced to 0 & 4G-1 resp. We assume the selector is wide open here. */
/** @note The Intel manual suggests that the OS is responsible for this. */
/* Turn off interrupts. */
return VINF_SUCCESS;
}
{
goto end;
return VINF_SUCCESS;
}
{
/** @todo implement syscall */
}
else
{
/** @todo implement sysret */
}
end:
return VINF_EM_RAW_RING_SWITCH;
}
/**
* Adds branch pair to the lookup cache of the particular branch instruction
*
* @returns VBox status
* @param pVM Pointer to the VM.
* @param pJumpTableGC Pointer to branch instruction lookup cache
* @param pBranchTarget Original branch target
* @param pRelBranchPatch Relative duplicated function address
*/
int patmAddBranchToLookupCache(PVM pVM, RTRCPTR pJumpTableGC, RTRCPTR pBranchTarget, RTRCUINTPTR pRelBranchPatch)
{
Log(("PATMAddBranchToLookupCache: Adding (%RRv->%RRv (%RRv)) to table %RRv\n", pBranchTarget, pRelBranchPatch + pVM->patm.s.pPatchMemGC, pRelBranchPatch, pJumpTableGC));
#ifdef IN_RC
#else
#endif
{
uint32_t i;
for (i=0;i<pJumpTable->nrSlots;i++)
{
{
/* Relative address - eases relocation */
pJumpTable->cAddresses++;
break;
}
}
#ifdef VBOX_WITH_STATISTICS
#endif
}
else
{
/* Replace an old entry. */
/** @todo replacement strategy isn't really bright. change to something better if required. */
/* Relative address - eases relocation */
}
return VINF_SUCCESS;
}
#if defined(VBOX_WITH_STATISTICS) || defined(LOG_ENABLED)
/**
* Return the name of the patched instruction
*
* @returns instruction name
*
* @param opcode DIS instruction opcode
* @param fPatchFlags Patch flags
*/
{
switch (opcode)
{
case OP_CLI:
pszInstr = "cli";
break;
case OP_PUSHF:
pszInstr = "pushf";
break;
case OP_POPF:
pszInstr = "popf";
break;
case OP_STR:
pszInstr = "str";
break;
case OP_LSL:
pszInstr = "lsl";
break;
case OP_LAR:
pszInstr = "lar";
break;
case OP_SGDT:
pszInstr = "sgdt";
break;
case OP_SLDT:
pszInstr = "sldt";
break;
case OP_SIDT:
pszInstr = "sidt";
break;
case OP_SMSW:
pszInstr = "smsw";
break;
case OP_VERW:
pszInstr = "verw";
break;
case OP_VERR:
pszInstr = "verr";
break;
case OP_CPUID:
pszInstr = "cpuid";
break;
case OP_JMP:
pszInstr = "jmp";
break;
case OP_JO:
pszInstr = "jo";
break;
case OP_JNO:
pszInstr = "jno";
break;
case OP_JC:
pszInstr = "jc";
break;
case OP_JNC:
pszInstr = "jnc";
break;
case OP_JE:
pszInstr = "je";
break;
case OP_JNE:
pszInstr = "jne";
break;
case OP_JBE:
pszInstr = "jbe";
break;
case OP_JNBE:
pszInstr = "jnbe";
break;
case OP_JS:
pszInstr = "js";
break;
case OP_JNS:
pszInstr = "jns";
break;
case OP_JP:
pszInstr = "jp";
break;
case OP_JNP:
pszInstr = "jnp";
break;
case OP_JL:
pszInstr = "jl";
break;
case OP_JNL:
pszInstr = "jnl";
break;
case OP_JLE:
pszInstr = "jle";
break;
case OP_JNLE:
pszInstr = "jnle";
break;
case OP_JECXZ:
pszInstr = "jecxz";
break;
case OP_LOOP:
pszInstr = "loop";
break;
case OP_LOOPNE:
pszInstr = "loopne";
break;
case OP_LOOPE:
pszInstr = "loope";
break;
case OP_MOV:
if (fPatchFlags & PATMFL_IDTHANDLER)
else
pszInstr = "mov (cs)";
break;
case OP_SYSENTER:
pszInstr = "sysenter";
break;
case OP_PUSH:
pszInstr = "push (cs)";
break;
case OP_CALL:
pszInstr = "call";
break;
case OP_IRET:
pszInstr = "iret";
break;
}
return pszInstr;
}
#endif