IEMAll.cpp revision e41f0459369a6d814aa36bf4def225482fc56026
/* $Id$ */
/** @file
* IEM - Interpreted Execution Manager - All Contexts.
*/
/*
* Copyright (C) 2011 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.
*/
/** @page pg_iem IEM - Interpreted Execution Manager
*
* The interpreted exeuction manager (IEM) is for executing short guest code
* sequences that are causing too many exits / virtualization traps. It will
* also be used to interpret single instructions, thus replacing the selective
* interpreters in EM and IOM.
*
* Design goals:
* - Relatively small footprint, although we favour speed and correctness
* over size.
* - Reasonably fast.
* - Correctly handle lock prefixed instructions.
* - Complete instruction set - eventually.
* - Refactorable into a recompiler, maybe.
* - Replace EMInterpret*.
*
* Using the existing disassembler has been considered, however this is thought
* to conflict with speed as the disassembler chews things a bit too much while
* leaving us with a somewhat complicated state to interpret afterwards.
*
*
* The current code is very much work in progress. You've been warned!
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifdef IEM_VERIFICATION_MODE
#endif
#include "IEMInternal.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** @typedef PFNIEMOP
* Pointer to an opcode decoder function.
*/
/** @def FNIEMOP_DEF
* Define an opcode decoder function.
*
* We're using macors for this so that adding and removing parameters as well as
* tweaking compiler specific attributes becomes easier. See FNIEMOP_CALL
*
* @param a_Name The function name.
*/
#if defined(__GNUC__) && defined(RT_ARCH_X86)
# define FNIEMOP_DEF(a_Name) \
static VBOXSTRICTRC __attribute__((__fastcall__, __nothrow__)) a_Name(PIEMCPU pIemCpu, a_Type0 a_Name0) RT_NO_THROW
static VBOXSTRICTRC __attribute__((__fastcall__, __nothrow__)) a_Name(PIEMCPU pIemCpu, a_Type0 a_Name0, a_Type1 a_Name1) RT_NO_THROW
# define FNIEMOP_DEF(a_Name) \
static /*__declspec(naked)*/ VBOXSTRICTRC __fastcall a_Name(PIEMCPU pIemCpu, a_Type0 a_Name0) RT_NO_THROW
static /*__declspec(naked)*/ VBOXSTRICTRC __fastcall a_Name(PIEMCPU pIemCpu, a_Type0 a_Name0, a_Type1 a_Name1) RT_NO_THROW
#else
# define FNIEMOP_DEF(a_Name) \
#endif
/**
* Function table for a binary operator providing implementation based on
* operand size.
*/
typedef struct IEMOPBINSIZES
{
/** Pointer to a binary operator function table. */
typedef IEMOPBINSIZES const *PCIEMOPBINSIZES;
/**
* Function table for a unary operator providing implementation based on
* operand size.
*/
typedef struct IEMOPUNARYSIZES
{
/** Pointer to a unary operator function table. */
typedef IEMOPUNARYSIZES const *PCIEMOPUNARYSIZES;
/**
* Function table for a shift operator providing implementation based on
* operand size.
*/
typedef struct IEMOPSHIFTSIZES
{
/** Pointer to a shift operator function table. */
typedef IEMOPSHIFTSIZES const *PCIEMOPSHIFTSIZES;
/**
* Function table for a multiplication or division operation.
*/
typedef struct IEMOPMULDIVSIZES
{
/** Pointer to a multiplication or division operation function table. */
typedef IEMOPMULDIVSIZES const *PCIEMOPMULDIVSIZES;
/**
* Selector descriptor table entry as fetched by iemMemFetchSelDesc.
*/
typedef union IEMSELDESC
{
/** The legacy view. */
/** The long mode view. */
} IEMSELDESC;
/** Pointer to a selector descriptor table entry. */
typedef IEMSELDESC *PIEMSELDESC;
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Temporary hack to disable the double execution. Will be removed in favor
* of a dedicated execution mode in EM. */
//#define IEM_VERIFICATION_MODE_NO_REM
/** Used to shut up GCC warnings about variables that 'may be used uninitialized'
* due to GCC lacking knowledge about the value range of a switch. */
/**
* Call an opcode decoder function.
*
* We're using macors for this so that adding and removing parameters can be
* done as we please. See FNIEMOP_DEF.
*/
/**
* Call a common opcode decoder function taking one extra argument.
*
* We're using macors for this so that adding and removing parameters can be
* done as we please. See FNIEMOP_DEF_1.
*/
/**
* Call a common opcode decoder function taking one extra argument.
*
* We're using macors for this so that adding and removing parameters can be
* done as we please. See FNIEMOP_DEF_1.
*/
/**
* Check if we're currently executing in real or virtual 8086 mode.
*
* @returns @c true if it is, @c false if not.
* @param a_pIemCpu The IEM state of the current CPU.
*/
#define IEM_IS_REAL_OR_V86_MODE(a_pIemCpu) (CPUMIsGuestInRealOrV86ModeEx((a_pIemCpu)->CTX_SUFF(pCtx)))
/**
* Check if we're currently executing in long mode.
*
* @returns @c true if it is, @c false if not.
* @param a_pIemCpu The IEM state of the current CPU.
*/
/**
* Check if we're currently executing in real mode.
*
* @returns @c true if it is, @c false if not.
* @param a_pIemCpu The IEM state of the current CPU.
*/
/**
* Tests if an AMD CPUID feature (extended) is marked present - ECX.
*/
#define IEM_IS_AMD_CPUID_FEATURE_PRESENT_ECX(a_fEcx) iemRegIsAmdCpuIdFeaturePresent(pIemCpu, 0, (a_fEcx))
/**
* Check if the address is canonical.
*/
#define IEM_IS_CANONICAL(a_u64Addr) ((uint64_t)(a_u64Addr) + UINT64_C(0x800000000000) < UINT64_C(0x1000000000000))
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Function table for the ADD instruction. */
static const IEMOPBINSIZES g_iemAImpl_add =
{
};
/** Function table for the ADC instruction. */
static const IEMOPBINSIZES g_iemAImpl_adc =
{
};
/** Function table for the SUB instruction. */
static const IEMOPBINSIZES g_iemAImpl_sub =
{
};
/** Function table for the SBB instruction. */
static const IEMOPBINSIZES g_iemAImpl_sbb =
{
};
/** Function table for the OR instruction. */
static const IEMOPBINSIZES g_iemAImpl_or =
{
};
/** Function table for the XOR instruction. */
static const IEMOPBINSIZES g_iemAImpl_xor =
{
};
/** Function table for the AND instruction. */
static const IEMOPBINSIZES g_iemAImpl_and =
{
};
/** Function table for the CMP instruction.
* @remarks Making operand order ASSUMPTIONS.
*/
static const IEMOPBINSIZES g_iemAImpl_cmp =
{
};
/** Function table for the TEST instruction.
* @remarks Making operand order ASSUMPTIONS.
*/
static const IEMOPBINSIZES g_iemAImpl_test =
{
};
/** Group 1 /r lookup table. */
{
};
/** Function table for the INC instruction. */
static const IEMOPUNARYSIZES g_iemAImpl_inc =
{
};
/** Function table for the DEC instruction. */
static const IEMOPUNARYSIZES g_iemAImpl_dec =
{
};
/** Function table for the NEG instruction. */
static const IEMOPUNARYSIZES g_iemAImpl_neg =
{
};
/** Function table for the NOT instruction. */
static const IEMOPUNARYSIZES g_iemAImpl_not =
{
};
/** Function table for the ROL instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_rol =
{
};
/** Function table for the ROR instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_ror =
{
};
/** Function table for the RCL instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_rcl =
{
};
/** Function table for the RCR instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_rcr =
{
};
/** Function table for the SHL instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_shl =
{
};
/** Function table for the SHR instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_shr =
{
};
/** Function table for the SAR instruction. */
static const IEMOPSHIFTSIZES g_iemAImpl_sar =
{
};
/** Function table for the MUL instruction. */
static const IEMOPMULDIVSIZES g_iemAImpl_mul =
{
};
/** Function table for the IMUL instruction working implicitly on rAX. */
static const IEMOPMULDIVSIZES g_iemAImpl_imul =
{
};
/** Function table for the DIV instruction. */
static const IEMOPMULDIVSIZES g_iemAImpl_div =
{
};
/** Function table for the MUL instruction. */
static const IEMOPMULDIVSIZES g_iemAImpl_idiv =
{
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static VBOXSTRICTRC iemRaiseSelectorInvalidAccess(PIEMCPU pIemCpu, uint32_t iSegReg, uint32_t fAccess);
static VBOXSTRICTRC iemRaiseSelectorNotPresent(PIEMCPU pIemCpu, uint32_t iSegReg, uint32_t fAccess);
static VBOXSTRICTRC iemRaisePageFault(PIEMCPU pIemCpu, RTGCPTR GCPtrWhere, uint32_t fAccess, int rc);
#if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
static VBOXSTRICTRC iemVerifyFakeIOPortRead(PIEMCPU pIemCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue);
static VBOXSTRICTRC iemVerifyFakeIOPortWrite(PIEMCPU pIemCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue);
#endif
/**
* Initializes the decoder state.
*
* @param pIemCpu The per CPU IEM state.
*/
{
pIemCpu->cActiveMappings = 0;
pIemCpu->iNextMapping = 0;
}
/**
* Prefetch opcodes the first time when starting executing.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
*/
{
/*
* What we're doing here is very similar to iemMemMap/iemMemBounceBufferMap.
*
* First translate CS:rIP to a physical address.
*/
{
if (!IEM_IS_CANONICAL(GCPtrPC))
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
else
{
}
if (RT_FAILURE(rc))
/** @todo Check reserved bits and such stuff. PGM is better at doing
* that, so do it when implementing the guest virtual address
* TLB... */
/*
* Read the bytes at this address.
*/
if (cbToTryRead > cbLeftOnPage)
if (!pIemCpu->fByPassHandlers)
else
if (rc != VINF_SUCCESS)
return rc;
return VINF_SUCCESS;
}
/**
* Try fetch at least @a cbMin bytes more opcodes, raise the appropriate
* exception if it fails.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param cbMin Where to return the opcode byte.
*/
{
/*
* What we're doing here is very similar to iemMemMap/iemMemBounceBufferMap.
*
* First translate CS:rIP to a physical address.
*/
{
if (!IEM_IS_CANONICAL(GCPtrNext))
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
else
{
if (cbToTryRead < cbMin)
}
if (RT_FAILURE(rc))
/** @todo Check reserved bits and such stuff. PGM is better at doing
* that, so do it when implementing the guest virtual address
* TLB... */
/*
* Read the bytes at this address.
*/
if (cbToTryRead > cbLeftOnPage)
if (!pIemCpu->fByPassHandlers)
rc = PGMPhysRead(IEMCPU_TO_VM(pIemCpu), GCPhys, &pIemCpu->abOpcode[pIemCpu->cbOpcode], cbToTryRead);
else
rc = PGMPhysSimpleReadGCPhys(IEMCPU_TO_VM(pIemCpu), &pIemCpu->abOpcode[pIemCpu->cbOpcode], GCPhys, cbToTryRead);
if (rc != VINF_SUCCESS)
return rc;
return VINF_SUCCESS;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextByte doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pb Where to return the opcode byte.
*/
{
if (rcStrict == VINF_SUCCESS)
{
}
else
*pb = 0;
return rcStrict;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextS8SxU16 doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu16 Where to return the opcode dword.
*/
{
if (rcStrict == VINF_SUCCESS)
return rcStrict;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextU16 doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu16 Where to return the opcode word.
*/
{
if (rcStrict == VINF_SUCCESS)
{
}
else
*pu16 = 0;
return rcStrict;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextU32 doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu32 Where to return the opcode dword.
*/
{
if (rcStrict == VINF_SUCCESS)
{
}
else
*pu32 = 0;
return rcStrict;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextS32SxU64 doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu64 Where to return the opcode qword.
*/
{
if (rcStrict == VINF_SUCCESS)
{
}
else
*pu64 = 0;
return rcStrict;
}
/**
* Deals with the problematic cases that iemOpcodeGetNextU64 doesn't like.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu64 Where to return the opcode qword.
*/
{
if (rcStrict == VINF_SUCCESS)
{
}
else
*pu64 = 0;
return rcStrict;
}
/**
* Fetches the next opcode byte.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu8 Where to return the opcode byte.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next opcode byte, returns automatically on failure.
*
* @param pIemCpu The IEM state.
* @param a_pu8 Where to return the opcode byte.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next signed byte from the opcode stream.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pi8 Where to return the signed byte.
*/
{
}
/**
* Fetches the next signed byte from the opcode stream, returning automatically
* on failure.
*
* @param pIemCpu The IEM state.
* @param pi8 Where to return the signed byte.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next signed byte from the opcode stream, extending it to
* unsigned 16-bit.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu16 Where to return the unsigned word.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next signed byte from the opcode stream and sign-extending it to
* a word, returning automatically on failure.
*
* @param pIemCpu The IEM state.
* @param pu16 Where to return the word.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next opcode word.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu16 Where to return the opcode word.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next opcode word, returns automatically on failure.
*
* @param pIemCpu The IEM state.
* @param a_pu16 Where to return the opcode word.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next opcode dword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu32 Where to return the opcode double word.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next opcode dword, returns automatically on failure.
*
* @param pIemCpu The IEM state.
* @param a_u32 Where to return the opcode dword.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next opcode dword, sign extending it into a quad word.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu64 Where to return the opcode quad word.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next opcode double word and sign extends it to a quad word,
* returns automatically on failure.
*
* @param pIemCpu The IEM state.
* @param a_pu64 Where to return the opcode quad word.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/**
* Fetches the next opcode qword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM state.
* @param pu64 Where to return the opcode qword.
*/
{
return VINF_SUCCESS;
}
/**
* Fetches the next opcode word, returns automatically on failure.
*
* @param pIemCpu The IEM state.
* @param a_pu64 Where to return the opcode qword.
*/
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
/** @name Raising Exceptions.
*
* @{
*/
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
static VBOXSTRICTRC iemRaiseSelectorInvalidAccess(PIEMCPU pIemCpu, uint32_t iSegReg, uint32_t fAccess)
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
static VBOXSTRICTRC iemRaisePageFault(PIEMCPU pIemCpu, RTGCPTR GCPtrWhere, uint32_t fAccess, int rc)
{
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
/**
* Macro for calling iemCImplRaiseInvalidLockPrefix().
*
* inlining as we wish.
*
* @return Strict VBox status code.
*/
{
AssertFailed();
return VERR_NOT_IMPLEMENTED;
}
/**
* Macro for calling iemCImplRaiseInvalidOpcode().
*
* inlining as we wish.
*
* @return Strict VBox status code.
*/
{
AssertFailed();
return VERR_NOT_IMPLEMENTED;
}
/** @} */
/*
*
* Helpers routines.
* Helpers routines.
* Helpers routines.
*
*/
/**
* Recalculates the effective operand size.
*
* @param pIemCpu The IEM state.
*/
{
switch (pIemCpu->enmCpuMode)
{
case IEMMODE_16BIT:
break;
case IEMMODE_32BIT:
break;
case IEMMODE_64BIT:
{
case 0:
break;
case IEM_OP_PRF_SIZE_OP:
break;
case IEM_OP_PRF_SIZE_REX_W:
break;
}
break;
default:
AssertFailed();
}
}
/**
* Sets the default operand size to 64-bit and recalculates the effective
* operand size.
*
* @param pIemCpu The IEM state.
*/
{
else
}
/*
*
* Common opcode decoders.
* Common opcode decoders.
* Common opcode decoders.
*
*/
/** Stubs an opcode. */
#define FNIEMOP_STUB(a_Name) \
{ \
IEMOP_MNEMONIC(#a_Name); \
return VERR_NOT_IMPLEMENTED; \
} \
typedef int ignore_semicolon
/** @name Register Access.
* @{
*/
/**
* Gets a reference (pointer) to the specified hidden segment register.
*
* @returns Hidden register reference.
* @param pIemCpu The per CPU data.
* @param iSegReg The segment register.
*/
{
switch (iSegReg)
{
}
}
/**
* Gets a reference (pointer) to the specified segment register (the selector
* value).
*
* @returns Pointer to the selector variable.
* @param pIemCpu The per CPU data.
* @param iSegReg The segment register.
*/
{
switch (iSegReg)
{
}
}
/**
* Fetches the selector value of a segment register.
*
* @returns The selector value.
* @param pIemCpu The per CPU data.
* @param iSegReg The segment register.
*/
{
switch (iSegReg)
{
}
AssertFailedReturn(0xffff);
}
/**
* Gets a reference (pointer) to the specified general register.
*
* @returns Register reference.
* @param pIemCpu The per CPU data.
* @param iReg The general register.
*/
{
switch (iReg)
{
}
}
/**
* Gets a reference (pointer) to the specified 8-bit general register.
*
* Because of AH, CH, DH and BH we cannot use iemGRegRef directly here.
*
* @returns Register reference.
* @param pIemCpu The per CPU data.
* @param iReg The register.
*/
{
if (iReg >= 4)
pu8Reg++;
return pu8Reg;
}
/**
* Fetches the value of a 8-bit general register.
*
* @returns The register value.
* @param pIemCpu The per CPU data.
* @param iReg The register.
*/
{
return *pbSrc;
}
/**
* Fetches the value of a 16-bit general register.
*
* @returns The register value.
* @param pIemCpu The per CPU data.
* @param iReg The register.
*/
{
}
/**
* Fetches the value of a 32-bit general register.
*
* @returns The register value.
* @param pIemCpu The per CPU data.
* @param iReg The register.
*/
{
}
/**
* Fetches the value of a 64-bit general register.
*
* @returns The register value.
* @param pIemCpu The per CPU data.
* @param iReg The register.
*/
{
}
/**
*
* May raise a \#GP(0) if the new RIP is non-canonical or outside the code
* segment limit.
*
* @param pIemCpu The per CPU data.
* @param offNextInstr The offset of the next instruction.
*/
{
switch (pIemCpu->enmEffOpSize)
{
case IEMMODE_16BIT:
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
break;
}
case IEMMODE_32BIT:
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
break;
}
case IEMMODE_64BIT:
{
if (!IEM_IS_CANONICAL(uNewRip))
return iemRaiseGeneralProtectionFault0(pIemCpu);
break;
}
}
return VINF_SUCCESS;
}
/**
*
* May raise a \#GP(0) if the new RIP is non-canonical or outside the code
* segment limit.
*
* @returns Strict VBox status code.
* @param pIemCpu The per CPU data.
* @param offNextInstr The offset of the next instruction.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
/** @todo Test 16-bit jump in 64-bit mode. */
return VINF_SUCCESS;
}
/**
*
* May raise a \#GP(0) if the new RIP is non-canonical or outside the code
* segment limit.
*
* @returns Strict VBox status code.
* @param pIemCpu The per CPU data.
* @param offNextInstr The offset of the next instruction.
*/
{
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
else
{
if (!IEM_IS_CANONICAL(uNewRip))
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
return VINF_SUCCESS;
}
/**
* Performs a near jump to the specified address.
*
* May raise a \#GP(0) if the new RIP is non-canonical or outside the code
* segment limit.
*
* @param pIemCpu The per CPU data.
* @param uNewRip The new RIP value.
*/
{
switch (pIemCpu->enmEffOpSize)
{
case IEMMODE_16BIT:
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
/** @todo Test 16-bit jump in 64-bit mode. */
break;
}
case IEMMODE_32BIT:
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
break;
}
case IEMMODE_64BIT:
{
if (!IEM_IS_CANONICAL(uNewRip))
return iemRaiseGeneralProtectionFault0(pIemCpu);
break;
}
}
return VINF_SUCCESS;
}
/**
* Get the address of the top of the stack.
*
* read.
*/
{
}
/**
*
* @param pIemCpu The per CPU data.
* @param cbInstr The number of bytes to add.
*/
{
switch (pIemCpu->enmCpuMode)
{
case IEMMODE_16BIT:
break;
case IEMMODE_32BIT:
break;
case IEMMODE_64BIT:
break;
default: AssertFailed();
}
}
/**
*
* @param pIemCpu The per CPU data.
*/
{
}
/**
* Adds to the stack pointer.
*
* updated.
* @param cbToAdd The number of bytes to add.
*/
{
else
}
/**
* Subtracts from the stack pointer.
*
* updated.
* @param cbToSub The number of bytes to subtract.
*/
{
else
}
/**
* Adds to the temporary stack pointer.
*
* @param cbToAdd The number of bytes to add.
* @param pCtx Where to get the current stack mode.
*/
{
else
}
/**
* Subtracts from the temporary stack pointer.
*
* @param cbToSub The number of bytes to subtract.
* @param pCtx Where to get the current stack mode.
*/
{
else
}
/**
* Calculates the effective stack address for a push of the specified size as
* well as the new RSP value (upper bits may be masked).
*
* @returns Effective stack addressf for the push.
* @param pCtx Where to get the current stack mode.
* @param cbItem The size of the stack item to pop.
* @param puNewRsp Where to return the new RSP value.
*/
{
else
return GCPtrTop;
}
/**
* Gets the current stack pointer and calculates the value after a pop of the
* specified size.
*
* @returns Current stack pointer.
* @param pCtx Where to get the current stack mode.
* @param cbItem The size of the stack item to pop.
* @param puNewRsp Where to return the new RSP value.
*/
{
{
}
{
}
else
{
}
return GCPtrTop;
}
/**
* Calculates the effective stack address for a push of the specified size as
* well as the new temporary RSP value (upper bits may be masked).
*
* @returns Effective stack addressf for the push.
* @param pTmpRsp The temporary stack pointer. This is updated.
* @param cbItem The size of the stack item to pop.
* @param puNewRsp Where to return the new RSP value.
*/
{
else
return GCPtrTop;
}
/**
* Gets the effective stack address for a pop of the specified size and
* calculates and updates the temporary RSP.
*
* @returns Current stack pointer.
* @param pTmpRsp The temporary stack pointer. This is updated.
* @param pCtx Where to get the current stack mode.
* @param cbItem The size of the stack item to pop.
*/
{
{
}
{
}
else
{
}
return GCPtrTop;
}
/**
* Checks if an AMD CPUID feature bit is set.
*
* @returns true / false.
*
* @param pIemCpu The IEM per CPU data.
* @param fEdx The EDX bit to test, or 0 if ECX.
* @param fEcx The ECX bit to test, or 0 if EDX.
* @remarks Used via IEM_IS_AMD_CPUID_FEATURE_PRESENT_ECX.
*/
{
}
/** @} */
/** @name Memory access.
*
* @{
*/
/**
* Checks if the given segment can be written to, raise the appropriate
* exception if not.
*
* @returns VBox strict status code.
*
* @param pIemCpu The IEM per CPU data.
* @param pHid Pointer to the hidden register.
* @param iSegReg The register number.
*/
static VBOXSTRICTRC iemMemSegCheckWriteAccessEx(PIEMCPU pIemCpu, PCCPUMSELREGHID pHid, uint8_t iSegReg)
{
return VINF_SUCCESS;
}
/**
* Checks if the given segment can be read from, raise the appropriate
* exception if not.
*
* @returns VBox strict status code.
*
* @param pIemCpu The IEM per CPU data.
* @param pHid Pointer to the hidden register.
* @param iSegReg The register number.
*/
static VBOXSTRICTRC iemMemSegCheckReadAccessEx(PIEMCPU pIemCpu, PCCPUMSELREGHID pHid, uint8_t iSegReg)
{
return VINF_SUCCESS;
}
/**
* Applies the segment limit, base and attributes.
*
* This may raise a \#GP or \#SS.
*
* @returns VBox strict status code.
*
* @param pIemCpu The IEM per CPU data.
* @param fAccess The kind of access which is being performed.
* @param iSegReg The index of the segment register to apply.
* This is UINT8_MAX if none (for IDT, GDT, LDT,
* TSS, ++).
* @param pGCPtrMem Pointer to the guest memory address to apply
* segmentation to. Input and output parameter.
*/
{
return VINF_SUCCESS;
switch (pIemCpu->enmCpuMode)
{
case IEMMODE_16BIT:
case IEMMODE_32BIT:
{
{
if ( (fAccess & IEM_ACCESS_TYPE_WRITE)
if (!IEM_IS_REAL_OR_V86_MODE(pIemCpu))
{
/** @todo CPL check. */
}
/*
* There are two kinds of data selectors, normal and expand down.
*/
{
}
else
{
/** @todo implement expand down segments. */
AssertFailed(/** @todo implement this */);
return VERR_NOT_IMPLEMENTED;
}
}
else
{
/*
* Code selector and usually be used to read thru, writing is
* only permitted in real and V8086 mode.
*/
if ( ( (fAccess & IEM_ACCESS_TYPE_WRITE)
|| ( (fAccess & IEM_ACCESS_TYPE_READ)
&& !IEM_IS_REAL_OR_V86_MODE(pIemCpu) )
if (!IEM_IS_REAL_OR_V86_MODE(pIemCpu))
{
/** @todo CPL check. */
}
}
return VINF_SUCCESS;
}
case IEMMODE_64BIT:
return VINF_SUCCESS;
default:
}
}
/**
* Translates a virtual address to a physical physical address and checks if we
* can access the page as specified.
*
* @param pIemCpu The IEM per CPU data.
* @param GCPtrMem The virtual address.
* @param fAccess The intended access.
* @param pGCPhysMem Where to return the physical address.
*/
static VBOXSTRICTRC iemMemPageTranslateAndCheckAccess(PIEMCPU pIemCpu, RTGCPTR GCPtrMem, uint32_t fAccess,
{
/** @todo Need a different PGM interface here. We're currently using
* generic / REM interfaces. this won't cut it for R0 & RC. */
if (RT_FAILURE(rc))
{
/** @todo Check unassigned memory in unpaged mode. */
}
&& !(fFlags & X86_PTE_RW)
&& (fFlags & X86_PTE_PAE_NX)
)
)
{
}
*pGCPhysMem = GCPhys;
return VINF_SUCCESS;
}
/**
* Maps a physical page.
*
* @returns VBox status code (see PGMR3PhysTlbGCPhys2Ptr).
* @param pIemCpu The IEM per CPU data.
* @param GCPhysMem The physical address.
* @param fAccess The intended access.
* @param ppvMem Where to return the mapping address.
*/
{
#if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
/* Force the alternative path so we can ignore writes. */
if (fAccess & IEM_ACCESS_TYPE_WRITE)
return VERR_PGM_PHYS_TLB_CATCH_ALL;
#endif
/*
* If we can map the page without trouble, do a block processing
* until the end of the current page.
*/
/** @todo need some better API. */
ppvMem);
}
/**
* Looks up a memory mapping entry.
*
* @returns The mapping index (positive) or VERR_NOT_FOUND (negative).
* @param pIemCpu The IEM per CPU data.
* @param pvMem The memory address.
* @param fAccess The access to.
*/
{
return 0;
return 1;
return 2;
return VERR_NOT_FOUND;
}
/**
* Finds a free memmap entry when using iNextMapping doesn't work.
*
* @returns Memory mapping index, 1024 on failure.
* @param pIemCpu The IEM per CPU data.
*/
{
/*
* The easy case.
*/
if (pIemCpu->cActiveMappings == 0)
{
return 0;
}
/* There should be enough mappings for all instructions. */
AssertFailed(); /** @todo implement me. */
return 1024;
}
/**
* Commits a bounce buffer that needs writing back and unmaps it.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param iMemMap The index of the buffer to commit.
*/
{
/*
* Do the writing.
*/
int rc;
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM) /* No memory changes in verification mode. */
{
if (!pIemCpu->fByPassHandlers)
{
cbFirst);
cbSecond);
}
else
{
cbFirst);
cbSecond);
}
}
else
#endif
rc = VINF_SUCCESS;
#if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
/*
* Record the write(s).
*/
if (pEvtRec)
{
memcpy(pEvtRec->u.RamWrite.ab, &pIemCpu->aBounceBuffers[iMemMap].ab[0], pIemCpu->aMemBbMappings[iMemMap].cbFirst);
}
{
if (pEvtRec)
{
}
}
#endif
/*
* Free the mapping entry.
*/
return rc;
}
/**
* iemMemMap worker that deals with a request crossing pages.
*/
{
/*
* Do the address translations.
*/
VBOXSTRICTRC rcStrict = iemMemPageTranslateAndCheckAccess(pIemCpu, GCPtrFirst, fAccess, &GCPhysFirst);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
rcStrict = iemMemPageTranslateAndCheckAccess(pIemCpu, GCPtrFirst + (cbMem - 1), fAccess, &GCPhysSecond);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/*
* Read in the current memory content if it's a read of execute access.
*/
{
int rc;
if (!pIemCpu->fByPassHandlers)
{
if (rc != VINF_SUCCESS)
return rc;
if (rc != VINF_SUCCESS)
return rc;
}
else
{
if (rc != VINF_SUCCESS)
return rc;
rc = PGMPhysSimpleReadGCPhys(IEMCPU_TO_VM(pIemCpu), pbBuf + cbFirstPage, GCPhysSecond, cbSecondPage);
if (rc != VINF_SUCCESS)
return rc;
}
#if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
/*
* Record the reads.
*/
if (pEvtRec)
{
}
if (pEvtRec)
{
}
#endif
}
#ifdef VBOX_STRICT
else
#endif
#ifdef VBOX_STRICT
#endif
/*
* Commit the bounce buffer entry.
*/
return VINF_SUCCESS;
}
/**
* iemMemMap woker that deals with iemMemPageMap failures.
*/
static VBOXSTRICTRC iemMemBounceBufferMapPhys(PIEMCPU pIemCpu, unsigned iMemMap, void **ppvMem, size_t cbMem,
{
/*
* Filter out conditions we can handle and the ones which shouldn't happen.
*/
if ( rcMap != VINF_PGM_PHYS_TLB_CATCH_WRITE
{
return rcMap;
}
/*
* Read in the current memory content if it's a read of execute access.
*/
{
if (rcMap == VERR_PGM_PHYS_TLB_UNASSIGNED)
else
{
int rc;
if (!pIemCpu->fByPassHandlers)
else
if (rc != VINF_SUCCESS)
return rc;
}
#if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
/*
* Record the read.
*/
if (pEvtRec)
{
}
#endif
}
#ifdef VBOX_STRICT
else
#endif
#ifdef VBOX_STRICT
#endif
/*
* Commit the bounce buffer entry.
*/
return VINF_SUCCESS;
}
/**
* Maps the specified guest memory for the given kind of access.
*
* This may be using bounce buffering of the memory if it's crossing a page
* boundary or if there is an access handler installed for any of it. Because
* of lock prefix guarantees, we're in for some extra clutter when this
* happens.
*
* This may raise a \#GP, \#SS, \#PF or \#AC.
*
* @returns VBox strict status code.
*
* @param pIemCpu The IEM per CPU data.
* @param ppvMem Where to return the pointer to the mapped
* memory.
* @param cbMem The number of bytes to map. This is usually 1,
* 2, 4, 6, 8, 12, 16 or 32. When used by string
* operations it can be up to a page.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* Use UINT8_MAX to indicate that no segmentation
* is required (for IDT, GDT and LDT accesses).
* @param GCPtrMem The address of the guest memory.
* @param a_fAccess How the memory is being accessed. The
* IEM_ACCESS_TYPE_XXX bit is used to figure out
* how to map the memory, while the
* IEM_ACCESS_WHAT_XXX bit is used when raising
* exceptions.
*/
static VBOXSTRICTRC iemMemMap(PIEMCPU pIemCpu, void **ppvMem, size_t cbMem, uint8_t iSegReg, RTGCPTR GCPtrMem, uint32_t fAccess)
{
/*
* Check the input and figure out which mapping entry to use.
*/
{
}
/*
* Map the memory, checking that we can actually access it. If something
* slightly complicated happens, fall back on bounce buffering.
*/
if (rcStrict != VINF_SUCCESS)
return rcStrict;
if (rcStrict != VINF_SUCCESS)
return rcStrict;
void *pvMem;
if (rcStrict != VINF_SUCCESS)
/*
* Fill in the mapping table entry.
*/
return VINF_SUCCESS;
}
/**
* Commits the guest memory if bounce buffered and unmaps it.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pvMem The mapping.
* @param fAccess The kind of access.
*/
{
/*
* If it's bounce buffered, we need to write back the buffer.
*/
if ( (pIemCpu->aMemMappings[iMemMap].fAccess & (IEM_ACCESS_BOUNCE_BUFFERED | IEM_ACCESS_TYPE_WRITE))
/* Free the entry. */
return VINF_SUCCESS;
}
/**
* Fetches a data byte.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu8Dst Where to return the byte.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
*/
static VBOXSTRICTRC iemMemFetchDataU8(PIEMCPU pIemCpu, uint8_t *pu8Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu8Src, sizeof(*pu8Src), iSegReg, GCPtrMem, IEM_ACCESS_DATA_R);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Fetches a data word.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu16Dst Where to return the word.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
*/
static VBOXSTRICTRC iemMemFetchDataU16(PIEMCPU pIemCpu, uint16_t *pu16Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Src, sizeof(*pu16Src), iSegReg, GCPtrMem, IEM_ACCESS_DATA_R);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Fetches a data dword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu32Dst Where to return the dword.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
*/
static VBOXSTRICTRC iemMemFetchDataU32(PIEMCPU pIemCpu, uint32_t *pu32Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Src, sizeof(*pu32Src), iSegReg, GCPtrMem, IEM_ACCESS_DATA_R);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Fetches a data dword and sign extends it to a qword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu64Dst Where to return the sign extended value.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
*/
static VBOXSTRICTRC iemMemFetchDataS32SxU64(PIEMCPU pIemCpu, uint64_t *pu64Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pi32Src, sizeof(*pi32Src), iSegReg, GCPtrMem, IEM_ACCESS_DATA_R);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Fetches a data qword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu64Dst Where to return the qword.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
*/
static VBOXSTRICTRC iemMemFetchDataU64(PIEMCPU pIemCpu, uint64_t *pu64Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Src, sizeof(*pu64Src), iSegReg, GCPtrMem, IEM_ACCESS_DATA_R);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Fetches a descriptor register (lgdt, lidt).
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pcbLimit Where to return the limit.
* @param pGCPTrBase Where to return the base.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
* @param enmOpSize The effective operand size.
*/
{
(void **)&pu8Src,
? 2 + 8
: enmOpSize == IEMMODE_32BIT
? 2 + 4
: 2 + 3,
if (rcStrict == VINF_SUCCESS)
{
switch (enmOpSize)
{
case IEMMODE_16BIT:
break;
case IEMMODE_32BIT:
break;
case IEMMODE_64BIT:
break;
}
}
return rcStrict;
}
/**
* Stores a data byte.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
* @param u8Value The value to store.
*/
static VBOXSTRICTRC iemMemStoreDataU8(PIEMCPU pIemCpu, uint8_t iSegReg, RTGCPTR GCPtrMem, uint8_t u8Value)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu8Dst, sizeof(*pu8Dst), iSegReg, GCPtrMem, IEM_ACCESS_DATA_W);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Stores a data word.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
* @param u16Value The value to store.
*/
static VBOXSTRICTRC iemMemStoreDataU16(PIEMCPU pIemCpu, uint8_t iSegReg, RTGCPTR GCPtrMem, uint16_t u16Value)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Dst, sizeof(*pu16Dst), iSegReg, GCPtrMem, IEM_ACCESS_DATA_W);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Stores a data dword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
* @param u32Value The value to store.
*/
static VBOXSTRICTRC iemMemStoreDataU32(PIEMCPU pIemCpu, uint8_t iSegReg, RTGCPTR GCPtrMem, uint32_t u32Value)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Dst, sizeof(*pu32Dst), iSegReg, GCPtrMem, IEM_ACCESS_DATA_W);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Stores a data qword.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param iSegReg The index of the segment register to use for
* this access. The base and limits are checked.
* @param GCPtrMem The address of the guest memory.
* @param u64Value The value to store.
*/
static VBOXSTRICTRC iemMemStoreDataU64(PIEMCPU pIemCpu, uint8_t iSegReg, RTGCPTR GCPtrMem, uint64_t u64Value)
{
/* The lazy approach for now... */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Dst, sizeof(*pu64Dst), iSegReg, GCPtrMem, IEM_ACCESS_DATA_W);
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* Pushes a word onto the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u16Value The value to push.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Dst, sizeof(*pu16Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pushes a dword onto the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u32Value The value to push.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Dst, sizeof(*pu32Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pushes a qword onto the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u64Value The value to push.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Dst, sizeof(*pu64Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pops a word from the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu16Value Where to store the popped value.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Src, sizeof(*pu16Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rc == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rc == VINF_SUCCESS)
}
return rc;
}
/**
* Pops a dword from the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu32Value Where to store the popped value.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Src, sizeof(*pu32Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rc == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rc == VINF_SUCCESS)
}
return rc;
}
/**
* Pops a qword from the stack.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu64Value Where to store the popped value.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Src, sizeof(*pu64Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rc == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rc == VINF_SUCCESS)
}
return rc;
}
/**
* Pushes a word onto the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u16Value The value to push.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Dst, sizeof(*pu16Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pushes a dword onto the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u32Value The value to push.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Dst, sizeof(*pu32Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pushes a dword onto the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param u64Value The value to push.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Dst, sizeof(*pu64Dst), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_W);
if (rc == VINF_SUCCESS)
{
}
/* Commit the new RSP value unless we an access handler made trouble. */
if (rc == VINF_SUCCESS)
return rc;
}
/**
* Pops a word from the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu16Value Where to store the popped value.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu16Src, sizeof(*pu16Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rc == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rc == VINF_SUCCESS)
}
return rc;
}
/**
* Pops a dword from the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu32Value Where to store the popped value.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Src, sizeof(*pu32Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rc == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rc == VINF_SUCCESS)
}
return rc;
}
/**
* Pops a qword from the stack, using a temporary stack pointer.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pu64Value Where to store the popped value.
* @param pTmpRsp Pointer to the temporary stack pointer.
*/
{
/* Increment the stack pointer. */
/* Write the word the lazy way. */
VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, (void **)&pu64Src, sizeof(*pu64Src), X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
if (rcStrict == VINF_SUCCESS)
{
/* Commit the new RSP value. */
if (rcStrict == VINF_SUCCESS)
}
return rcStrict;
}
/**
* Begin a special stack push (used by interrupt, exceptions and such).
*
* This will raise #SS or #PF if appropriate.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param cbMem The number of bytes to push onto the stack.
* @param ppvMem Where to return the pointer to the stack memory.
* As with the other memory functions this could be
* direct access or bounce buffered access, so
* don't commit register until the commit call
* succeeds.
* @param puNewRsp Where to return the new RSP value. This must be
* passed unchanged to
* iemMemStackPushCommitSpecial().
*/
static VBOXSTRICTRC iemMemStackPushBeginSpecial(PIEMCPU pIemCpu, size_t cbMem, void **ppvMem, uint64_t *puNewRsp)
{
}
/**
* Commits a special stack push (started by iemMemStackPushBeginSpecial).
*
* This will update the rSP.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pvMem The pointer returned by
* iemMemStackPushBeginSpecial().
* @param uNewRsp The new RSP value returned by
* iemMemStackPushBeginSpecial().
*/
{
if (rcStrict == VINF_SUCCESS)
return rcStrict;
}
/**
* Begin a special stack pop (used by iret, retf and such).
*
* This will raise #SS or #PF if appropriate.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param cbMem The number of bytes to push onto the stack.
* @param ppvMem Where to return the pointer to the stack memory.
* @param puNewRsp Where to return the new RSP value. This must be
* passed unchanged to
* iemMemStackPopCommitSpecial().
*/
static VBOXSTRICTRC iemMemStackPopBeginSpecial(PIEMCPU pIemCpu, size_t cbMem, void const **ppvMem, uint64_t *puNewRsp)
{
}
/**
* Commits a special stack pop (started by iemMemStackPopBeginSpecial).
*
* This will update the rSP.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param pvMem The pointer returned by
* iemMemStackPopBeginSpecial().
* @param uNewRsp The new RSP value returned by
* iemMemStackPopBeginSpecial().
*/
static VBOXSTRICTRC iemMemStackPopCommitSpecial(PIEMCPU pIemCpu, void const *pvMem, uint64_t uNewRsp)
{
if (rcStrict == VINF_SUCCESS)
return rcStrict;
}
/**
* Fetches a descriptor table entry.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU.
* @param pDesc Where to return the descriptor table entry.
* @param uSel The selector which table entry to fetch.
*/
{
/** @todo did the 286 require all 8 bytes to be accessible? */
/*
* Get the selector table base and check bounds.
*/
if (uSel & X86_SEL_LDT)
{
{
Log(("iemMemFetchSelDesc: LDT selector %#x is out of bounds (%3x) or ldtr is NP (%#x)\n",
/** @todo is this the right exception? */
}
}
else
{
{
/** @todo is this the right exception? */
}
}
/*
* Read the legacy descriptor and maybe the long mode extensions if
* required.
*/
VBOXSTRICTRC rcStrict = iemMemFetchDataU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
if (rcStrict == VINF_SUCCESS)
{
if ( !IEM_IS_LONG_MODE(pIemCpu)
else if ((uint32_t)(uSel & X86_SEL_MASK) + 15 < (uSel & X86_SEL_LDT ? pCtx->ldtrHid.u32Limit : pCtx->gdtr.cbGdt))
rcStrict = iemMemFetchDataU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
else
{
/** @todo is this the right exception? */
}
}
return rcStrict;
}
/**
* Marks the selector descriptor as accessed (only non-system descriptors).
*
* This function ASSUMES that iemMemFetchSelDesc has be called previously and
* will therefore skip the limit checks.
*
* @returns Strict VBox status code.
* @param pIemCpu The IEM per CPU.
* @param uSel The selector.
*/
{
/*
* Get the selector table base and check bounds.
*/
VBOXSTRICTRC rcStrict = iemMemMap(pIemCpu, (void **)&pu32, 4, UINT8_MAX, GCPtr, IEM_ACCESS_DATA_RW);
if (rcStrict == VINF_SUCCESS)
{
}
return rcStrict;
}
/** @} */
/** @name Misc Helpers
* @{
*/
/**
* Checks if we are allowed to access the given I/O port, raising the
* appropriate exceptions if we aren't (or if the I/O bitmap is not
* accessible).
*
* @returns Strict VBox status code.
*
* @param pIemCpu The IEM per CPU data.
* @param pCtx The register context.
* @param u16Port The port number.
* @param cbOperand The operand size.
*/
DECLINLINE(VBOXSTRICTRC) iemHlpCheckPortIOPermission(PIEMCPU pIemCpu, PCCPUMCTX pCtx, uint16_t u16Port, uint8_t cbOperand)
{
{
/** @todo I/O port permission bitmap check */
}
return VINF_SUCCESS;
}
/** @} */
/** @name C Implementations
* @{
*/
/**
* Implements a 16-bit popa.
*/
{
/*
* The docs are a bit hard to comprehend here, but it looks like we wrap
* around in real mode as long as none of the individual "popa" crosses the
* end of the stack segment. In protected mode we check the whole access
* in one go. For efficiency, only do the word-by-word thing if we're in
* danger of wrapping around.
*/
/** @todo do popa boundary / wrap-around checks. */
{
/* word-by-word */
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
}
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
}
}
else
{
if (rcStrict == VINF_SUCCESS)
{
/* skip sp */
if (rcStrict == VINF_SUCCESS)
{
}
}
}
return rcStrict;
}
/**
* Implements a 32-bit popa.
*/
{
/*
* The docs are a bit hard to comprehend here, but it looks like we wrap
* around in real mode as long as none of the individual "popa" crosses the
* end of the stack segment. In protected mode we check the whole access
* in one go. For efficiency, only do the word-by-word thing if we're in
* danger of wrapping around.
*/
/** @todo do popa boundary / wrap-around checks. */
{
/* word-by-word */
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
}
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
#if 1 /** @todo what actually happens with the high bits when we're in 16-bit mode? */
#endif
}
}
else
{
if (rcStrict == VINF_SUCCESS)
{
/* skip esp */
if (rcStrict == VINF_SUCCESS)
{
}
}
}
return rcStrict;
}
/**
* Implements a 16-bit pusha.
*/
{
/*
* The docs are a bit hard to comprehend here, but it looks like we wrap
* around in real mode as long as none of the individual "pushd" crosses the
* end of the stack segment. In protected mode we check the whole access
* in one go. For efficiency, only do the word-by-word thing if we're in
* danger of wrapping around.
*/
/** @todo do pusha boundary / wrap-around checks. */
&& IEM_IS_REAL_OR_V86_MODE(pIemCpu) ) )
{
/* word-by-word */
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
}
}
else
{
if (rcStrict == VINF_SUCCESS)
{
if (rcStrict == VINF_SUCCESS)
{
}
}
}
return rcStrict;
}
/**
* Implements a 32-bit pusha.
*/
{
/*
* The docs are a bit hard to comprehend here, but it looks like we wrap
* around in real mode as long as none of the individual "pusha" crosses the
* end of the stack segment. In protected mode we check the whole access
* in one go. For efficiency, only do the word-by-word thing if we're in
* danger of wrapping around.
*/
/** @todo do pusha boundary / wrap-around checks. */
&& IEM_IS_REAL_OR_V86_MODE(pIemCpu) ) )
{
/* word-by-word */
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
if (rcStrict == VINF_SUCCESS)
{
}
}
else
{
if (rcStrict == VINF_SUCCESS)
{
if (rcStrict == VINF_SUCCESS)
{
}
}
}
return rcStrict;
}
/**
* Implements pushf.
*
*
* @param enmEffOpSize The effective operand size.
*/
{
/*
* If we're in V8086 mode some care is required (which is why we're in
* doing this in a C implementation).
*/
if ( (fEfl & X86_EFL_VM)
{
if ( enmEffOpSize != IEMMODE_16BIT
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/*
* Ok, clear RF and VM and push the flags.
*/
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
break;
case IEMMODE_32BIT:
break;
case IEMMODE_64BIT:
break;
}
if (rcStrict != VINF_SUCCESS)
return rcStrict;
return VINF_SUCCESS;
}
/**
* Implements popf.
*
* @param enmEffOpSize The effective operand size.
*/
{
/*
* V8086 is special as usual.
*/
if (fEflOld & X86_EFL_VM)
{
/*
* Almost anything goes if IOPL is 3.
*/
{
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
break;
}
case IEMMODE_32BIT:
if (rcStrict != VINF_SUCCESS)
return rcStrict;
break;
}
}
/*
* Interrupt flag virtualization with CR4.VME=1.
*/
else if ( enmEffOpSize == IEMMODE_16BIT
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/** @todo Is the popf VME #GP(0) delivered after updating RSP+RIP
* or before? */
if ( ( (u16Value & X86_EFL_IF)
&& (fEflOld & X86_EFL_VIP))
|| (u16Value & X86_EFL_TF) )
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/*
* Not in V8086 mode.
*/
else
{
/* Pop the flags. */
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
break;
}
case IEMMODE_32BIT:
case IEMMODE_64BIT:
if (rcStrict != VINF_SUCCESS)
return rcStrict;
break;
}
/* Merge them with the current flags. */
{
}
{
}
else
{
}
}
/*
* Commit the flags.
*/
return VINF_SUCCESS;
}
/**
* Implements a 16-bit relative call.
*
*
* @param offDisp The displacment offset.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
return VINF_SUCCESS;
}
/**
* Implements a 32-bit relative call.
*
*
* @param offDisp The displacment offset.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
return VINF_SUCCESS;
}
/**
* Implements a 64-bit relative call.
*
*
* @param offDisp The displacment offset.
*/
{
if (!IEM_IS_CANONICAL(NewPC))
return iemRaiseNotCanonical(pIemCpu);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
return VINF_SUCCESS;
}
/**
* Implements far jumps.
*
* @param uSel The selector.
* @param offSeg The segment offset.
*/
{
/*
* Real mode and V8086 mode are easy. The only snag seems to be that
* CS.limit doesn't change and the limit check is done against the current
* limit.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
else
/** @todo REM reset the accessed bit (see on jmp far16 after disabling
* PE. Check with VT-x and AMD-V. */
#ifdef IEM_VERIFICATION_MODE
#endif
return VINF_SUCCESS;
}
/*
* Protected mode. Need to parse the specified descriptor...
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* Fetch the descriptor. */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/* Is it there? */
{
}
/*
* Deal with it according to its type.
*/
{
/* Only code segments. */
{
Log(("jmpf %04x:%08x -> not a code selector (u4Type=%#x).\n", uSel, offSeg, Desc.Legacy.Gen.u4Type));
}
/* L vs D. */
&& IEM_IS_LONG_MODE(pIemCpu))
{
}
{
{
Log(("jmpf %04x:%08x -> DPL violation (conforming); DPL=%d CPL=%u\n",
}
}
else
{
{
Log(("jmpf %04x:%08x -> CPL != DPL; DPL=%d CPL=%u\n", uSel, offSeg, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
}
{
Log(("jmpf %04x:%08x -> RPL > DPL; RPL=%d CPL=%u\n", uSel, offSeg, (uSel & X86_SEL_RPL), pIemCpu->uCpl));
}
}
/* Limit check. (Should alternatively check for non-canonical addresses
here, but that is ruled out by offSeg being 32-bit, right?) */
u64Base = 0;
else
{
{
}
}
/*
* Ok, everything checked out fine. Now set the accessed bit before
* committing the result into CS, CSHID and RIP.
*/
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
}
/* commit */
/** @todo check if the hidden bits are loaded correctly for 64-bit
* mode. */
return VINF_SUCCESS;
}
/*
* System selector.
*/
if (IEM_IS_LONG_MODE(pIemCpu))
{
case AMD64_SEL_TYPE_SYS_LDT:
/* Call various functions to do the work. */
default:
}
{
case X86_SEL_TYPE_SYS_LDT:
/* Call various functions to do the work. */
/* Call various functions to do the work. */
default:
}
}
/**
* Implements far calls.
*
* @param uSel The selector.
* @param offSeg The segment offset.
* @param enmOpSize The operand size (in case we need it).
*/
{
void *pvRet;
/*
* Real mode and V8086 mode are easy. The only snag seems to be that
* CS.limit doesn't change and the limit check is done against the current
* limit.
*/
{
/* Check stack first - may #SS(0). */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/* Check the target address range. */
if (offSeg > UINT32_MAX)
return iemRaiseGeneralProtectionFault0(pIemCpu);
/* Everything is fine, push the return address. */
if (enmOpSize == IEMMODE_16BIT)
{
}
else
{
}
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/* Branch. */
/** @todo Does REM reset the accessed bit here to? (See on jmp far16
* after disabling PE.) Check with VT-x and AMD-V. */
#ifdef IEM_VERIFICATION_MODE
#endif
return VINF_SUCCESS;
}
}
/**
* Implements retf.
*
* @param enmEffOpSize The effective operand size.
* @param cbPop The amount of arguments to pop from the stack
* (bytes).
*/
{
/*
* Real mode and V8086 mode are easy.
*/
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
if (enmEffOpSize == IEMMODE_32BIT)
{
}
else
{
}
/** @todo check how this is supposed to work if sp=0xfffe. */
/* Check the limit of the new EIP. */
/** @todo Intel pseudo code only does the limit check for 16-bit
* operands, AMD does not make any distinction. What is right? */
/* commit the operation. */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/** @todo do we load attribs and limit as well? */
if (cbPop)
return VINF_SUCCESS;
}
AssertFailed();
return VERR_NOT_IMPLEMENTED;
}
/**
* Implements retn.
*
* We're doing this in C because of the \#GP that might be raised if the popped
* program counter is out of bounds.
*
* @param enmEffOpSize The effective operand size.
* @param cbPop The amount of arguments to pop from the stack
* (bytes).
*/
{
/* Fetch the RSP from the stack. */
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
NewRip.u = 0;
break;
case IEMMODE_32BIT:
NewRip.u = 0;
break;
case IEMMODE_64BIT:
break;
}
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/* Check the new RSP before loading it. */
/** @todo Should test this as the intel+amd pseudo code doesn't mention half
* of it. The canonical test is performed here and for call. */
if (enmEffOpSize != IEMMODE_64BIT)
{
{
}
}
else
{
if (!IEM_IS_CANONICAL(NewRip.u))
{
return iemRaiseNotCanonical(pIemCpu);
}
}
/* Commit it. */
if (cbPop)
return VINF_SUCCESS;
}
/**
* Implements int3 and int XX.
*
* @param u8Int The interrupt vector number.
* @param fIsBpInstr Is it the breakpoint instruction.
*/
{
/** @todo we should call TRPM to do this job. */
/*
* Real mode is easy.
*/
&& IEM_IS_REAL_MODE(pIemCpu))
{
/* read the IDT entry. */
return iemRaiseGeneralProtectionFault(pIemCpu, X86_TRAP_ERR_IDT | ((uint16_t)u8Int << X86_TRAP_ERR_SEL_SHIFT));
rcStrict = iemMemFetchDataU32(pIemCpu, (uint32_t *)&Idte, UINT8_MAX, pCtx->idtr.pIdt + UINT32_C(4) * u8Int);
return rcStrict;
/* push the stack frame. */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
return rcStrict;
/* load the vector address into cs:ip. */
/** @todo do we load attribs and limit as well? Should we check against limit like far jump? */
return VINF_SUCCESS;
}
AssertFailed();
return VERR_NOT_IMPLEMENTED;
}
/**
* Implements iret.
*
* @param enmEffOpSize The effective operand size.
*/
{
/*
* Real mode is easy, V8086 mode is relative similar.
*/
{
/* iret throws an exception if VME isn't enabled. */
return iemRaiseGeneralProtectionFault0(pIemCpu);
/* Do the stack bits, but don't commit RSP before everything checks
out right. */
union
{
void const *pv;
} uFrame;
if (enmEffOpSize == IEMMODE_32BIT)
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
| X86_EFL_ID;
}
else
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/** @todo The intel pseudo code does not indicate what happens to
* reserved flags. We just ignore them. */
}
/** @todo Check how this is supposed to work if sp=0xfffe. */
/* Check the limit of the new EIP. */
/** @todo Only the AMD pseudo code check the limit here, what's
* right? */
/* V8086 checks and flag adjustments */
{
{
/* Preserve IOPL and clear RF. */
}
else if ( enmEffOpSize == IEMMODE_16BIT
&& ( !(uNewFlags & X86_EFL_IF)
&& !(uNewFlags & X86_EFL_TF) )
{
/* Move IF to VIF, clear RF and preserve IF and IOPL.*/
uNewFlags &= ~X86_EFL_VIF;
}
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* commit the operation. */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/** @todo do we load attribs and limit as well? */
return VINF_SUCCESS;
}
AssertFailed();
return VERR_NOT_IMPLEMENTED;
}
/**
* Implements 'mov SReg, r/m'.
*
* @param iSegReg The segment register number (valid).
* @param uSel The new selector value.
*/
{
/*
* Real mode and V8086 mode are easy.
*/
{
/** @todo Does the CPU actually load limits and attributes in the
* jumps... Affects unreal mode. */
if (iSegReg == X86_SREG_SS)
return VINF_SUCCESS;
}
/*
* Protected mode.
*
* Check if it's a null segment selector value first, that's OK for DS, ES,
* FS and GS. If not null, then we have to load and parse the descriptor.
*/
{
if (iSegReg == X86_SREG_SS)
{
|| uSel != 0) /** @todo We cannot 'mov ss, 3' in 64-bit kernel mode, can we? */
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* In 64-bit kernel mode, the stack can be 0 because of the way
interrupts are dispatched when in kernel ctx. Just load the
selector value into the register and leave the hidden bits
as is. */
return VINF_SUCCESS;
}
&& iSegReg != X86_SREG_FS
&& iSegReg != X86_SREG_GS)
{
/** @todo figure out what this actually does, it works. Needs
* testcase! */
}
else
{
}
return VINF_SUCCESS;
}
/* Fetch the descriptor. */
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/* Check GPs first. */
{
}
{
{
}
{
}
{
}
{
Log(("load sreg SS, %#x - DPL (%d) and CPL (%d) differs -> #GP\n", uSel, Desc.Legacy.Gen.u2Dpl, pIemCpu->uCpl));
}
}
else
{
{
}
!= (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
{
#if 0 /* this is what intel says. */
{
Log(("load sreg%u, %#x - both RPL (%d) and CPL (%d) are greater than DPL (%d) -> #GP\n",
}
#else /* this is what makes more sense. */
{
Log(("load sreg%u, %#x - RPL (%d) is greater than DPL (%d) -> #GP\n",
}
{
Log(("load sreg%u, %#x - CPL (%d) is greater than DPL (%d) -> #GP\n",
}
#endif
}
}
/* Is it there? */
{
}
/* The the base and limit. */
&& iSegReg < X86_SREG_FS)
u64Base = 0;
else
/*
* Ok, everything checked out fine. Now set the accessed bit before
* committing the result into the registers.
*/
{
if (rcStrict != VINF_SUCCESS)
return rcStrict;
}
/* commit */
pHid->Attr.u = (Desc.Legacy.u >> (16+16+8)) & UINT32_C(0xf0ff); /** @todo do we have a define for 0xf0ff? */
/** @todo check if the hidden bits are loaded correctly for 64-bit
* mode. */
if (iSegReg == X86_SREG_SS)
return VINF_SUCCESS;
}
/**
* Implements lgs, lfs, les, lds & lss.
*/
{
/*
* Use iemCImpl_LoadSReg to do the tricky segment register loading.
*/
/** @todo verify and test that mov, pop and lXs works the segment
* register loading in the exact same way. */
if (rcStrict == VINF_SUCCESS)
{
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
break;
case IEMMODE_32BIT:
break;
case IEMMODE_64BIT:
break;
}
}
return rcStrict;
}
/**
* Implements 'pop SReg'.
*
* @param iSegReg The segment register number (valid).
* @param enmEffOpSize The efficient operand size (valid).
*/
{
/*
* Read the selector off the stack and join paths with mov ss, reg.
*/
switch (enmEffOpSize)
{
case IEMMODE_16BIT:
{
if (rcStrict == VINF_SUCCESS)
break;
}
case IEMMODE_32BIT:
{
if (rcStrict == VINF_SUCCESS)
break;
}
case IEMMODE_64BIT:
{
if (rcStrict == VINF_SUCCESS)
break;
}
}
/*
* Commit the stack on success.
*/
if (rcStrict == VINF_SUCCESS)
return rcStrict;
}
/**
* Implements lgdt.
*
* @param iEffSeg The segment of the new ldtr contents
* @param GCPtrEffSrc The address of the new ldtr contents.
* @param enmEffOpSize The effective operand size.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
/*
* Fetch the limit and base address.
*/
VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pIemCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
if (rcStrict == VINF_SUCCESS)
{
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
if (rcStrict == VINF_SUCCESS)
}
return rcStrict;
}
/**
* Implements lidt.
*
* @param iEffSeg The segment of the new ldtr contents
* @param GCPtrEffSrc The address of the new ldtr contents.
* @param enmEffOpSize The effective operand size.
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
/*
* Fetch the limit and base address.
*/
VBOXSTRICTRC rcStrict = iemMemFetchDataXdtr(pIemCpu, &cbLimit, &GCPtrBase, iEffSeg, GCPtrEffSrc, enmEffOpSize);
if (rcStrict == VINF_SUCCESS)
{
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
if (rcStrict == VINF_SUCCESS)
}
return rcStrict;
}
/**
* Implements mov GReg,CRx.
*
* @param iGReg The general register to store the CRx value in.
* @param iCrReg The CRx register to read (valid).
*/
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
/* read it */
switch (iCrReg)
{
case 8:
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
crX = 0xff;
#endif
break;
IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
}
/* store it */
else
return VINF_SUCCESS;
}
/**
* Implements mov CRx,GReg.
*
* @param iCrReg The CRx register to read (valid).
* @param iGReg The general register to store the CRx value in.
*/
{
int rc;
return iemRaiseGeneralProtectionFault0(pIemCpu);
/*
* Read the new value from the source register.
*/
else
/*
* Try store it.
* Unfortunately, CPUM only does a tiny bit of the work.
*/
switch (iCrReg)
{
case 0:
{
/*
* Perform checks.
*/
/* Check for reserved bits. */
{
Log(("Trying to set reserved CR0 bits: NewCR0=%#llx InvalidBits=%#llx\n", NewCrX, NewCrX & ~(uint64_t)fValid));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* Check for invalid combinations. */
if ( (NewCrX & X86_CR0_PG)
&& !(NewCrX & X86_CR0_PE) )
{
Log(("Trying to set CR0.PG without CR0.PE\n"));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
if ( !(NewCrX & X86_CR0_CD)
&& (NewCrX & X86_CR0_NW) )
{
Log(("Trying to clear CR0.CD while leaving CR0.NW set\n"));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* Long mode consistency checks. */
if ( (NewCrX & X86_CR0_PG)
&& !(OldCrX & X86_CR0_PG)
{
{
Log(("Trying to enabled long mode paging without CR4.PAE set\n"));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
{
Log(("Trying to enabled long mode paging with a long CS descriptor loaded.\n"));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
}
/** @todo check reserved PDPTR bits as AMD states. */
/*
* Change CR0.
*/
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
/*
* Change EFER.LMA if entering or leaving long mode.
*/
{
if (NewCrX & X86_CR0_PG)
else
NewEFER &= ~MSR_K6_EFER_LME;
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
}
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
/*
* Inform PGM.
*/
{
/* ignore informational status codes */
}
/** @todo Status code management. */
#else
#endif
break;
}
/*
* CR2 can be changed without any restrictions.
*/
case 2:
break;
/*
* CR3 is relatively simple, although AMD and Intel have different
* accounts of how setting reserved bits are handled. We take intel's
* word for the lower bits and AMD's for the high bits (63:52).
*/
/** @todo Testcase: Setting reserved bits in CR3, especially before
* enabling paging. */
case 3:
{
/* check / mask the value. */
{
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
else
{
Log(("Automatically clearing reserved bits in CR3 load: NewCR3=%#llx ClearedBits=%#llx\n",
}
/** @todo If we're in PAE mode we should check the PDPTRs for
* invalid bits. */
/* Make the change. */
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
/* Inform PGM. */
{
/* ignore informational status codes */
/** @todo status code management */
}
#endif
break;
}
/*
* CR4 is a bit more tedious as there are bits which cannot be cleared
* under some circumstances and such.
*/
case 4:
{
/* reserved bits */
//if (xxx)
// fValid |= X86_CR4_VMXE;
//if (xxx)
// fValid |= X86_CR4_OSXSAVE;
{
Log(("Trying to set reserved CR4 bits: NewCR4=%#llx InvalidBits=%#llx\n", NewCrX, NewCrX & ~(uint64_t)fValid));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* long mode checks. */
if ( (OldCrX & X86_CR4_PAE)
&& !(NewCrX & X86_CR4_PAE)
{
Log(("Trying to set clear CR4.PAE while long mode is active\n"));
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/*
* Change it.
*/
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
/*
* Notify SELM and PGM.
*/
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
/* SELM - VME may change things wrt to the TSS shadowing. */
/* PGM - flushing and mode. */
{
/* ignore informational status codes */
}
/** @todo Status code management. */
#else
#endif
break;
}
/*
* CR8 maps to the APIC TPR.
*/
case 8:
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
break;
IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* call checks */
}
/*
* Advance the RIP on success.
*/
/** @todo Status code management. */
if (rcStrict == VINF_SUCCESS)
return rcStrict;
}
/**
* Implements 'IN eAX, port'.
*
* @param u16Port The source port.
* @param cbReg The register size.
*/
{
/*
* CPL check
*/
if (rcStrict != VINF_SUCCESS)
return rcStrict;
/*
* Perform the I/O.
*/
#if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
#else
#endif
if (IOM_SUCCESS(rcStrict))
{
switch (cbReg)
{
default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
}
}
/** @todo massage rcStrict. */
return rcStrict;
}
/**
* Implements 'IN eAX, DX'.
*
* @param cbReg The register size.
*/
{
}
/**
* Implements 'OUT port, eAX'.
*
* @param u16Port The destination port.
* @param cbReg The register size.
*/
{
/*
* CPL check
*/
{
/** @todo I/O port permission bitmap check */
}
/*
* Perform the I/O.
*/
switch (cbReg)
{
default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
}
# if !defined(IEM_VERIFICATION_MODE) || defined(IEM_VERIFICATION_MODE_NO_REM)
# else
# endif
if (IOM_SUCCESS(rc))
{
/** @todo massage rc. */
}
return rc;
}
/**
* Implements 'OUT DX, eAX'.
*
* @param cbReg The register size.
*/
{
}
/**
* Implements 'CLI'.
*/
{
{
{
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* V8086 */
else if (uIopl == 3)
else if ( uIopl < 3
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* real mode */
else
return VINF_SUCCESS;
}
/**
* Implements 'STI'.
*/
{
{
{
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* V8086 */
else if (uIopl == 3)
else if ( uIopl < 3
else
return iemRaiseGeneralProtectionFault0(pIemCpu);
}
/* real mode */
else
return VINF_SUCCESS;
}
/*
* Instantiate the various string operation combinations.
*/
#define OP_SIZE 8
#define ADDR_SIZE 16
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 8
#define ADDR_SIZE 32
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 8
#define ADDR_SIZE 64
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 16
#define ADDR_SIZE 16
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 16
#define ADDR_SIZE 32
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 16
#define ADDR_SIZE 64
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 32
#define ADDR_SIZE 16
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 32
#define ADDR_SIZE 32
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 32
#define ADDR_SIZE 64
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 64
#define ADDR_SIZE 32
#include "IEMAllCImplStrInstr.cpp.h"
#define OP_SIZE 64
#define ADDR_SIZE 64
#include "IEMAllCImplStrInstr.cpp.h"
/** @} */
/** @name "Microcode" macros.
*
* The idea is that we should be able to use the same code to interpret
* instructions as well as recompiler instructions. Thus this obfuscation.
*
* @{
*/
#define IEM_MC_END() }
#define IEM_MC_PAUSE() do {} while (0)
#define IEM_MC_CONTINUE() do {} while (0)
/** Internal macro. */
#define IEM_MC_RETURN_ON_FAILURE(a_Expr) \
do \
{ \
if (rcStrict2 != VINF_SUCCESS) \
return rcStrict2; \
} while (0)
#define IEM_MC_REL_JMP_S16(a_i16) IEM_MC_RETURN_ON_FAILURE(iemRegRipRelativeJumpS16(pIemCpu, a_i16))
#define IEM_MC_REL_JMP_S32(a_i32) IEM_MC_RETURN_ON_FAILURE(iemRegRipRelativeJumpS32(pIemCpu, a_i32))
#define IEM_MC_SET_RIP_U16(a_u16NewIP) IEM_MC_RETURN_ON_FAILURE(iemRegRipJump((pIemCpu), (a_u16NewIP)))
#define IEM_MC_SET_RIP_U32(a_u32NewIP) IEM_MC_RETURN_ON_FAILURE(iemRegRipJump((pIemCpu), (a_u32NewIP)))
#define IEM_MC_SET_RIP_U64(a_u64NewIP) IEM_MC_RETURN_ON_FAILURE(iemRegRipJump((pIemCpu), (a_u64NewIP)))
#define IEM_MC_COMMIT_EFLAGS(a_EFlags) \
do { (pIemCpu)->CTX_SUFF(pCtx)->eflags.u = (a_EFlags); Assert((pIemCpu)->CTX_SUFF(pCtx)->eflags.u & X86_EFL_1); } while (0)
#define IEM_MC_FETCH_GREG_U8_ZX_U16(a_u16Dst, a_iGReg) (a_u16Dst) = iemGRegFetchU8(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_GREG_U8_ZX_U32(a_u32Dst, a_iGReg) (a_u32Dst) = iemGRegFetchU8(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_GREG_U8_ZX_U64(a_u64Dst, a_iGReg) (a_u64Dst) = iemGRegFetchU8(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_GREG_U16_ZX_U32(a_u32Dst, a_iGReg) (a_u32Dst) = iemGRegFetchU16(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_GREG_U16_ZX_U64(a_u64Dst, a_iGReg) (a_u64Dst) = iemGRegFetchU16(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_GREG_U32_ZX_U64(a_u64Dst, a_iGReg) (a_u64Dst) = iemGRegFetchU32(pIemCpu, (a_iGReg))
#define IEM_MC_FETCH_SREG_ZX_U32(a_u32Dst, a_iSReg) (a_u32Dst) = iemSRegFetchU16(pIemCpu, (a_iSReg))
#define IEM_MC_FETCH_SREG_ZX_U64(a_u64Dst, a_iSReg) (a_u64Dst) = iemSRegFetchU16(pIemCpu, (a_iSReg))
#define IEM_MC_STORE_GREG_U16(a_iGReg, a_u16Value) *(uint16_t *)iemGRegRef(pIemCpu, (a_iGReg)) = (a_u16Value)
#define IEM_MC_STORE_GREG_U32(a_iGReg, a_u32Value) *(uint64_t *)iemGRegRef(pIemCpu, (a_iGReg)) = (uint32_t)(a_u32Value) /* clear high bits. */
#define IEM_MC_STORE_GREG_U64(a_iGReg, a_u64Value) *(uint64_t *)iemGRegRef(pIemCpu, (a_iGReg)) = (a_u64Value)
#define IEM_MC_REF_GREG_U16(a_pu16Dst, a_iGReg) (a_pu16Dst) = (uint16_t *)iemGRegRef(pIemCpu, (a_iGReg))
/** @todo User of IEM_MC_REF_GREG_U32 needs to clear the high bits on
* commit. */
#define IEM_MC_REF_GREG_U32(a_pu32Dst, a_iGReg) (a_pu32Dst) = (uint32_t *)iemGRegRef(pIemCpu, (a_iGReg))
#define IEM_MC_REF_GREG_U64(a_pu64Dst, a_iGReg) (a_pu64Dst) = (uint64_t *)iemGRegRef(pIemCpu, (a_iGReg))
#define IEM_MC_ADD_GREG_U8(a_iGReg, a_u16Value) *(uint8_t *)iemGRegRef(pIemCpu, (a_iGReg)) += (a_u8Value)
#define IEM_MC_ADD_GREG_U16(a_iGReg, a_u16Value) *(uint16_t *)iemGRegRef(pIemCpu, (a_iGReg)) += (a_u16Value)
do { \
*pu32Reg += (a_u32Value); \
} while (0)
#define IEM_MC_ADD_GREG_U64(a_iGReg, a_u64Value) *(uint64_t *)iemGRegRef(pIemCpu, (a_iGReg)) += (a_u64Value)
#define IEM_MC_SUB_GREG_U8(a_iGReg, a_u8Value) *(uint8_t *)iemGRegRef(pIemCpu, (a_iGReg)) -= (a_u8Value)
#define IEM_MC_SUB_GREG_U16(a_iGReg, a_u16Value) *(uint16_t *)iemGRegRef(pIemCpu, (a_iGReg)) -= (a_u16Value)
do { \
*pu32Reg -= (a_u32Value); \
} while (0)
#define IEM_MC_SUB_GREG_U64(a_iGReg, a_u64Value) *(uint64_t *)iemGRegRef(pIemCpu, (a_iGReg)) -= (a_u64Value)
#define IEM_MC_SET_EFL_BIT(a_fBit) do { (pIemCpu)->CTX_SUFF(pCtx)->eflags.u |= (a_fBit); } while (0)
#define IEM_MC_CLEAR_EFL_BIT(a_fBit) do { (pIemCpu)->CTX_SUFF(pCtx)->eflags.u &= ~(a_fBit); } while (0)
do { \
} while (0)
do { \
} while (0)
do { \
} while (0)
do { \
} while (0)
do { \
} while (0)
do { \
} while (0)
#define IEM_MC_PUSH_U16(a_u16Value) \
#define IEM_MC_PUSH_U32(a_u32Value) \
#define IEM_MC_PUSH_U64(a_u64Value) \
#define IEM_MC_POP_U16(a_pu16Value) \
#define IEM_MC_POP_U32(a_pu32Value) \
#define IEM_MC_POP_U64(a_pu64Value) \
/** Maps guest memory for direct or bounce buffered access.
* The purpose is to pass it to an operand implementation, thus the a_iArg.
* @remarks May return.
*/
IEM_MC_RETURN_ON_FAILURE(iemMemMap(pIemCpu, (void **)&(a_pMem), sizeof(*(a_pMem)), (a_iSeg), (a_GCPtrMem), (a_fAccess)))
/** Maps guest memory for direct or bounce buffered access.
* The purpose is to pass it to an operand implementation, thus the a_iArg.
* @remarks May return.
*/
IEM_MC_RETURN_ON_FAILURE(iemMemMap(pIemCpu, (void **)&(a_pvMem), (a_cbMem), (a_iSeg), (a_GCPtrMem), (a_fAccess)))
/** Commits the memory and unmaps the guest memory.
* @remarks May return.
*/
/** Calculate efficient address from R/M. */
/**
* Defers the rest of the instruction emulation to a C implementation routine
* and returns, only taking the standard parameters.
*
* @param a_pfnCImpl The pointer to the C routine.
* @sa IEM_DECL_IMPL_C_TYPE_0 and IEM_CIMPL_DEF_0.
*/
/**
* Defers the rest of instruction emulation to a C implementation routine and
* returns, taking one argument in addition to the standard ones.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The argument.
*/
/**
* Defers the rest of the instruction emulation to a C implementation routine
* and returns, taking two arguments in addition to the standard ones.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The first extra argument.
* @param a1 The second extra argument.
*/
#define IEM_MC_CALL_CIMPL_2(a_pfnCImpl, a0, a1) return (a_pfnCImpl)(pIemCpu, pIemCpu->offOpcode, a0, a1)
/**
* Defers the rest of the instruction emulation to a C implementation routine
* and returns, taking two arguments in addition to the standard ones.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The first extra argument.
* @param a1 The second extra argument.
* @param a2 The third extra argument.
*/
#define IEM_MC_CALL_CIMPL_3(a_pfnCImpl, a0, a1, a2) return (a_pfnCImpl)(pIemCpu, pIemCpu->offOpcode, a0, a1, a2)
/**
* Defers the rest of the instruction emulation to a C implementation routine
* and returns, taking two arguments in addition to the standard ones.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The first extra argument.
* @param a1 The second extra argument.
* @param a2 The third extra argument.
* @param a3 The fourth extra argument.
* @param a4 The fifth extra argument.
*/
#define IEM_MC_CALL_CIMPL_5(a_pfnCImpl, a0, a1, a2, a3, a4) return (a_pfnCImpl)(pIemCpu, pIemCpu->offOpcode, a0, a1, a2, a3, a4)
/**
* Defers the entire instruction emulation to a C implementation routine and
* returns, only taking the standard parameters.
*
* This shall be used without any IEM_MC_BEGIN or IEM_END macro surrounding it.
*
* @param a_pfnCImpl The pointer to the C routine.
* @sa IEM_DECL_IMPL_C_TYPE_0 and IEM_CIMPL_DEF_0.
*/
/**
* Defers the entire instruction emulation to a C implementation routine and
* returns, taking one argument in addition to the standard ones.
*
* This shall be used without any IEM_MC_BEGIN or IEM_END macro surrounding it.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The argument.
*/
/**
* Defers the entire instruction emulation to a C implementation routine and
* returns, taking two arguments in addition to the standard ones.
*
* This shall be used without any IEM_MC_BEGIN or IEM_END macro surrounding it.
*
* @param a_pfnCImpl The pointer to the C routine.
* @param a0 The first extra argument.
* @param a1 The second extra argument.
*/
#define IEM_MC_DEFER_TO_CIMPL_2(a_pfnCImpl, a0, a1) (a_pfnCImpl)(pIemCpu, pIemCpu->offOpcode, a0, a1)
#define IEM_MC_IF_CX_IS_NZ_AND_EFL_BIT_SET(a_fBit) \
#define IEM_MC_IF_ECX_IS_NZ_AND_EFL_BIT_SET(a_fBit) \
#define IEM_MC_IF_RCX_IS_NZ_AND_EFL_BIT_SET(a_fBit) \
#define IEM_MC_IF_CX_IS_NZ_AND_EFL_BIT_NOT_SET(a_fBit) \
#define IEM_MC_IF_ECX_IS_NZ_AND_EFL_BIT_NOT_SET(a_fBit) \
#define IEM_MC_IF_RCX_IS_NZ_AND_EFL_BIT_NOT_SET(a_fBit) \
#define IEM_MC_IF_GREG_BIT_SET(a_iGReg, a_iBitNo) if (*(uint64_t *)iemGRegRef(pIemCpu, (a_iGReg)) & RT_BIT_64(a_iBitNo)) {
#define IEM_MC_ELSE() } else {
#define IEM_MC_ENDIF() } do {} while (0)
/** @} */
/** @name Opcode Debug Helpers.
* @{
*/
#ifdef DEBUG
# define IEMOP_MNEMONIC(a_szMnemonic) \
Log2(("decode - %04x:%08RGv %s\n", pIemCpu->CTX_SUFF(pCtx)->cs, pIemCpu->CTX_SUFF(pCtx)->rip, a_szMnemonic))
Log2(("decode - %04x:%08RGv %s %s\n", pIemCpu->CTX_SUFF(pCtx)->cs, pIemCpu->CTX_SUFF(pCtx)->rip, a_szMnemonic, a_szOps))
#else
# define IEMOP_MNEMONIC(a_szMnemonic) do { } while (0)
#endif
/** @} */
/** @name Opcode Helpers.
* @{
*/
/** The instruction allows no lock prefixing (in this encoding), throw #UD if
* lock prefixed. */
#define IEMOP_HLP_NO_LOCK_PREFIX() \
do \
{ \
return IEMOP_RAISE_INVALID_LOCK_PREFIX(); \
} while (0)
/** The instruction is not available in 64-bit mode, throw #UD if we're in
* 64-bit mode. */
#define IEMOP_HLP_NO_64BIT() \
do \
{ \
return IEMOP_RAISE_INVALID_OPCODE(); \
} while (0)
/** The instruction defaults to 64-bit operand size if 64-bit mode. */
#define IEMOP_HLP_DEFAULT_64BIT_OP_SIZE() \
do \
{ \
} while (0)
/**
* Calculates the effective address of a ModR/M memory operand.
*
* Meant to be used via IEM_MC_CALC_RM_EFF_ADDR.
*
* @return Strict VBox status code.
* @param pIemCpu The IEM per CPU data.
* @param bRm The ModRM byte.
* @param pGCPtrEff Where to return the effective address.
*/
{
#define SET_SS_DEF() \
do \
{ \
} while (0)
/** @todo Check the effective address size crap! */
switch (pIemCpu->enmEffAddrMode)
{
case IEMMODE_16BIT:
{
/* Handle the disp16 form with no registers first. */
else
{
/* Get the displacment. */
{
case 0: u16EffAddr = 0; break;
}
/* Add the base and index registers to the disp. */
switch (bRm & X86_MODRM_RM_MASK)
{
}
}
*pGCPtrEff = u16EffAddr;
return VINF_SUCCESS;
}
case IEMMODE_32BIT:
{
/* Handle the disp32 form with no registers first. */
else
{
/* Get the register (or SIB) value. */
switch ((bRm & X86_MODRM_RM_MASK))
{
case 4: /* SIB */
{
/* Get the index and scale it. */
{
}
/* add base */
switch (bSib & X86_SIB_BASE_MASK)
{
case 5:
if ((bRm & X86_MODRM_MOD_MASK) != 0)
{
SET_SS_DEF();
}
else
{
u32EffAddr += u32Disp;
}
break;
}
break;
}
}
/* Get and add the displacement. */
{
case 0:
break;
case 1:
{
u32EffAddr += i8Disp;
break;
}
case 2:
{
u32EffAddr += u32Disp;
break;
}
default:
}
}
*pGCPtrEff = u32EffAddr;
else
{
}
return VINF_SUCCESS;
}
case IEMMODE_64BIT:
{
/* Handle the rip+disp32 form with no registers first. */
{
}
else
{
/* Get the register (or SIB) value. */
{
/* SIB */
case 4:
case 12:
{
/* Get the index and scale it. */
{
}
/* add base */
{
/* complicated encodings */
case 5:
case 13:
if ((bRm & X86_MODRM_MOD_MASK) != 0)
{
{
SET_SS_DEF();
}
else
}
else
{
}
break;
}
break;
}
}
/* Get and add the displacement. */
{
case 0:
break;
case 1:
{
u64EffAddr += i8Disp;
break;
}
case 2:
{
break;
}
IEM_NOT_REACHED_DEFAULT_CASE_RET(); /* (caller checked for these) */
}
}
*pGCPtrEff = u64EffAddr;
else
return VINF_SUCCESS;
}
}
}
/** @} */
/*
* Include the instructions
*/
#include "IEMAllInstructions.cpp.h"
#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
/**
* Sets up execution verification mode.
*/
{
# ifndef IEM_VERIFICATION_MODE_NO_REM
/*
* Switch state.
*/
s_DebugCtx = *pOrgCtx;
# endif
/*
* See if there is an interrupt pending in TRPM and inject it if we can.
*/
&& TRPMHasTrap(pVCpu)
//&& TRPMIsSoftwareInterrupt(pVCpu)
{
}
/*
* Reset the counters.
*/
pIemCpu->fMulDivHack = false;
/*
* Free all verification records.
*/
do
{
while (pEvtRec)
{
}
} while (pEvtRec);
}
/**
* Allocate an event record.
* @returns Poitner to a record.
*/
{
if (pEvtRec)
else
{
if (!pIemCpu->ppIemEvtRecNext)
return NULL; /* Too early (fake PCIBIOS), ignore notification. */
pEvtRec = (PIEMVERIFYEVTREC)MMR3HeapAlloc(IEMCPU_TO_VM(pIemCpu), MM_TAG_EM /* lazy bird*/, sizeof(*pEvtRec));
if (!pEvtRec)
return NULL;
}
return pEvtRec;
}
/**
* IOMMMIORead notification.
*/
{
# ifndef IEM_VERIFICATION_MODE_NO_REM
if (!pVCpu)
return;
if (!pEvtRec)
return;
# endif
}
/**
* IOMMMIOWrite notification.
*/
{
# ifndef IEM_VERIFICATION_MODE_NO_REM
if (!pVCpu)
return;
if (!pEvtRec)
return;
# endif
}
/**
* IOMIOPortRead notification.
*/
{
# ifndef IEM_VERIFICATION_MODE_NO_REM
if (!pVCpu)
return;
if (!pEvtRec)
return;
# endif
}
/**
* IOMIOPortWrite notification.
*/
{
# ifndef IEM_VERIFICATION_MODE_NO_REM
if (!pVCpu)
return;
if (!pEvtRec)
return;
# endif
}
VMM_INT_DECL(void) IEMNotifyIOPortReadString(PVM pVM, RTIOPORT Port, RTGCPTR GCPtrDst, RTGCUINTREG cTransfers, size_t cbValue)
{
AssertFailed();
}
VMM_INT_DECL(void) IEMNotifyIOPortWriteString(PVM pVM, RTIOPORT Port, RTGCPTR GCPtrSrc, RTGCUINTREG cTransfers, size_t cbValue)
{
AssertFailed();
}
/**
* Fakes and records an I/O port read.
*
* @returns VINF_SUCCESS.
* @param pIemCpu The IEM per CPU data.
* @param Port The I/O port.
* @param pu32Value Where to store the fake value.
* @param cbValue The size of the access.
*/
static VBOXSTRICTRC iemVerifyFakeIOPortRead(PIEMCPU pIemCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
{
if (pEvtRec)
{
}
*pu32Value = 0xffffffff;
return VINF_SUCCESS;
}
/**
* Fakes and records an I/O port write.
*
* @returns VINF_SUCCESS.
* @param pIemCpu The IEM per CPU data.
* @param Port The I/O port.
* @param u32Value The value being written.
* @param cbValue The size of the access.
*/
static VBOXSTRICTRC iemVerifyFakeIOPortWrite(PIEMCPU pIemCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
{
if (pEvtRec)
{
}
return VINF_SUCCESS;
}
/**
* Used by iemVerifyAssertRecord and iemVerifyAssertRecords to add a record
* dump to the assertion info.
*
* @param pEvtRec The record to dump.
*/
{
{
RTAssertMsg2Add("I/O PORT READ from %#6x, %d bytes\n",
break;
RTAssertMsg2Add("I/O PORT WRITE to %#6x, %d bytes, value %#x\n",
break;
case IEMVERIFYEVENT_RAM_READ:
RTAssertMsg2Add("RAM READ at %RGp, %#4zx bytes\n",
break;
case IEMVERIFYEVENT_RAM_WRITE:
RTAssertMsg2Add("RAM WRITE at %RGp, %#4zx bytes: %.*RHxs\n",
break;
default:
break;
}
}
/**
* Raises an assertion on the specified record, showing the given message with
* a record dump attached.
*
* @param pEvtRec1 The first record.
* @param pEvtRec2 The second record.
* @param pszMsg The message explaining why we're asserting.
*/
static void iemVerifyAssertRecords(PIEMVERIFYEVTREC pEvtRec1, PIEMVERIFYEVTREC pEvtRec2, const char *pszMsg)
{
}
/**
* Raises an assertion on the specified record, showing the given message with
* a record dump attached.
*
* @param pEvtRec1 The first record.
* @param pszMsg The message explaining why we're asserting.
*/
{
}
/**
* Performs the post-execution verfication checks.
*/
{
# if defined(IEM_VERIFICATION_MODE) && !defined(IEM_VERIFICATION_MODE_NO_REM)
/*
* Switch back the state.
*/
/*
* Execute the instruction in REM.
*/
#if 0
{
{
}
}
#endif
/*
* Compare the register states.
*/
unsigned cDiffs = 0;
{
Log(("REM and IEM ends up with different registers!\n"));
# define CHECK_FIELD(a_Field) \
do \
{ \
{ \
{ \
case 1: RTAssertMsg2Weak(" %8s differs - iem=%02x - rem=%02x\n", #a_Field, pDebugCtx->a_Field, pOrgCtx->a_Field); break; \
case 2: RTAssertMsg2Weak(" %8s differs - iem=%04x - rem=%04x\n", #a_Field, pDebugCtx->a_Field, pOrgCtx->a_Field); break; \
case 4: RTAssertMsg2Weak(" %8s differs - iem=%08x - rem=%08x\n", #a_Field, pDebugCtx->a_Field, pOrgCtx->a_Field); break; \
case 8: RTAssertMsg2Weak(" %8s differs - iem=%016llx - rem=%016llx\n", #a_Field, pDebugCtx->a_Field, pOrgCtx->a_Field); break; \
} \
cDiffs++; \
} \
} while (0)
# define CHECK_BIT_FIELD(a_Field) \
do \
{ \
{ \
RTAssertMsg2Weak(" %8s differs - iem=%02x - rem=%02x\n", #a_Field, pDebugCtx->a_Field, pOrgCtx->a_Field); \
cDiffs++; \
} \
} while (0)
{
{
RTAssertMsg2Weak(" the FPU state differs\n");
cDiffs++;
}
else
RTAssertMsg2Weak(" the FPU state differs - happens the first time...\n");
}
if (pIemCpu->fMulDivHack)
fFlagsMask &= ~(X86_EFL_OF);
{
RTAssertMsg2Weak(" rflags differs - iem=%08llx rem=%08llx\n", pDebugCtx->rflags.u, pOrgCtx->rflags.u);
}
CHECK_FIELD(dr[0]);
if (cDiffs != 0)
AssertFailed();
}
/*
* If the register state compared fine, check the verification event
* records.
*/
if (cDiffs == 0)
{
/*
* Compare verficiation event records.
* - I/O port accesses should be a 1:1 match.
*/
{
/* Since we might miss RAM writes and reads, ignore extra ones
made by IEM. */
/* Do the compare. */
{
break;
}
bool fEquals;
{
break;
break;
case IEMVERIFYEVENT_RAM_READ:
break;
case IEMVERIFYEVENT_RAM_WRITE:
break;
default:
fEquals = false;
break;
}
if (!fEquals)
{
break;
}
/* advance */
}
/* Ignore extra writes and reads. */
}
# endif
}
#endif /* IEM_VERIFICATION_MODE && IN_RING3 */
/**
* Execute one instruction.
*
* @return Strict VBox status code.
* @param pVCpu The current virtual CPU.
*/
{
#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
#endif
#ifdef DEBUG
char szInstr[256];
Log2(("**** "
" eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n"
" eip=%08x esp=%08x ebp=%08x iopl=%d\n"
" cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x efl=%08x\n"
" %s\n"
,
szInstr));
#endif
/*
* Do the decoding and emulation.
*/
if (rcStrict != VINF_SUCCESS)
return rcStrict;
if (rcStrict == VINF_SUCCESS)
pIemCpu->cInstructions++;
//#ifdef DEBUG
// AssertMsg(pIemCpu->offOpcode == cbInstr || rcStrict != VINF_SUCCESS, ("%u %u\n", pIemCpu->offOpcode, cbInstr));
//#endif
/* Execute the next instruction as well if a cli, pop ss or
mov ss, Gr has just completed successfully. */
if ( rcStrict == VINF_SUCCESS
{
if (rcStrict == VINF_SUCCESS)
{
b; IEM_OPCODE_GET_NEXT_BYTE(pIemCpu, &b);
if (rcStrict == VINF_SUCCESS)
pIemCpu->cInstructions++;
}
}
/*
* Assert some sanity.
*/
#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
#endif
return rcStrict;
}