cidet-app.cpp revision 3bc264992af3416ecba168e6ffae1a334fe79088
/* $Id$ */
/** @file
* CPU Instruction Decoding & Execution Tests - Ring-3 Driver Application.
*/
/*
* Copyright (C) 2014 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "cidet.h"
#include <iprt/asm-amd64-x86.h>
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
#ifdef RT_OS_WINDOWS
# include <Windows.h>
#else
# define USE_SIGNALS
# include <signal.h>
# include <unistd.h>
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* CIDET driver app buffer.
*/
typedef struct CIDETAPPBUF
{
/** The buffer size. */
/** The normal allocation.
* There is a fence page before this as well as at pbNormal+cb. */
/** The low memory allocation (32-bit addressable if 64-bit host, 16-bit
* addressable if 32-bit host). */
/** Set if we're using the normal buffer, clear if it's the low one. */
bool fUsingNormal : 1;
/** Set if the buffer is armed, clear if mostly accessible. */
bool fArmed : 1;
/** Set if this is a code buffer. */
bool fIsCode : 1;
/** The memory protection for the pages (RTMEM_PROT_XXX). */
/** The memory protection for the last page (RTMEM_PROT_XXX). */
/** The buffer index. */
} CIDETAPPBUF;
/** Pointer to a CIDET driver app buffer. */
typedef CIDETAPPBUF *PCIDETAPPBUF;
/** Number of code buffers. */
#define CIDETAPP_CODE_BUF_COUNT 1
/** Number of data buffers. */
#define CIDETAPP_DATA_BUF_COUNT 1
/**
* CIDET driver app instance.
*/
typedef struct CIDETAPP
{
/** The core structure. */
/** The execute return context. */
/** Code buffers (runs parallel to g_aCodeBufCfgs). */
/** Data buffers (runs parallel to g_aDataBufCfgs). */
} CIDETAPP;
/** Pointer to a CIDET driver app instance. */
/** Pointer to a pointer to a CIDET driver app instance. */
typedef PCIDETAPP *PPCIDETAPP;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The test instance handle. */
/** Points to the instance data while executing, NULL if not executing or if
* we've already handled the first exception while executing. */
static PCIDETAPP volatile g_pExecutingThis;
/** Code buffer configurations (parallel to CIDETAPP::aCodeBuffers). */
{
{
"Normal",
},
};
/** Data buffer configurations (parallel to CIDETAPP::aDataBuffers). */
{
{
"Normal",
},
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
DECLASM(void) CidetAppSaveAndRestoreCtx(void);
/*
*
*
* Exception and signal handling.
* Exception and signal handling.
* Exception and signal handling.
*
*
*/
#ifdef RT_OS_WINDOWS
{
/*
* Grab the this point. We expect at most one signal.
*/
{
/* we're up the infamous creek... */
for (;;) ExitProcess(2);
}
/*
* Gather CPU state information from the context structure.
*/
# ifdef RT_ARCH_AMD64
__debugbreak();
{
/* ... */
}
{
/* ... */
}
# elif defined(RT_ARCH_X86)
__debugbreak();
{
/* ... */
}
{
/* ... */
}
{
/* ... */
}
# else
# error "Not supported"
# endif
/*
*/
{
break;
case EXCEPTION_SINGLE_STEP:
break;
case EXCEPTION_BREAKPOINT:
break;
case EXCEPTION_INT_OVERFLOW:
break;
break;
break;
break;
{
MEMORY_BASIC_INFORMATION MemInfo = {0};
if (VirtualQuery((PVOID)pXcptPtrs->ExceptionRecord->ExceptionInformation[1], &MemInfo, sizeof(MemInfo)) > 0)
{
case PAGE_NOACCESS:
break;
case PAGE_READONLY:
case PAGE_READWRITE:
case PAGE_WRITECOPY:
case PAGE_EXECUTE:
case PAGE_EXECUTE_READ:
case PAGE_EXECUTE_READWRITE:
case PAGE_EXECUTE_WRITECOPY:
break;
default:
}
break;
}
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
break;
break;
default:
break;
}
/*
* Our own personal long jump implementation.
*/
/* Won't return...*/
return EXCEPTION_EXECUTE_HANDLER;
}
#elif defined(USE_SIGNALS)
/**
* Signal handler.
*/
{
# if 1
if (pSigInfo)
# endif
/*
* Grab the this point. We expect at most one signal.
*/
{
/* we're up the infamous creek... */
for (;;) _exit(2);
}
/*
* Gather all the CPU state information available.
*/
/*
* Jump back to CidetAppCbExecute.
*/
}
#endif
/*
*
* Buffer handling
* Buffer handling
* Buffer handling
*
*
*/
{
}
static int cidetAppAllocateAndConfigureOneBuffer(PCIDETAPP pThis, PCIDETAPPBUF pBuf, uint16_t idxBuf, bool fIsCode,
{
static uint8_t const s_afBufProtToDefaultMemProt[] =
{
/* [0] = */ RTMEM_PROT_NONE,
/* [4] = */ RTMEM_PROT_READ,
/* [8] = */ RTMEM_PROT_NONE,
/* [9] = */ RTMEM_PROT_NONE,
/* [10] = */ RTMEM_PROT_NONE,
/* [11] = */ RTMEM_PROT_NONE,
/* [12] = */ RTMEM_PROT_NONE,
/* [13] = */ RTMEM_PROT_NONE,
/* [14] = */ RTMEM_PROT_NONE,
/* [15] = */ RTMEM_PROT_NONE,
};
static uint8_t const s_afBufProtToLastPageMemProt[] =
{
/* [0] = */ RTMEM_PROT_NONE,
/* [4] = */ RTMEM_PROT_READ,
/* [5] = */ RTMEM_PROT_NONE,
/* [7] = */ RTMEM_PROT_READ,
/* [8] = */ RTMEM_PROT_NONE,
/* [9] = */ RTMEM_PROT_NONE,
/* [10] = */ RTMEM_PROT_NONE,
/* [11] = */ RTMEM_PROT_NONE,
/* [12] = */ RTMEM_PROT_NONE,
/* [13] = */ RTMEM_PROT_NONE,
/* [14] = */ RTMEM_PROT_NONE,
/* [15] = */ RTMEM_PROT_NONE,
};
int rc;
pBuf->fUsingNormal = true;
{
/*
* Allocate a 3 page buffer plus two fence pages.
*/
{
/* Set up fence pages. */
if (RT_SUCCESS(rc))
/* Default protection + read + write. */
if (RT_SUCCESS(rc))
rc = RTMemProtect(pBuf->pbNormal, pBuf->cb, pBuf->fDefaultProt | RTMEM_PROT_READ | RTMEM_PROT_WRITE);
/*
* Allocate a low memory buffer or LDT if necessary.
*/
if ( RT_SUCCESS(rc)
{
/** @todo Buffers for the other addressing mode. */
}
else
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
}
else
}
else
rc = RTTestIFailedRc(VERR_NO_PAGE_MEMORY, "Unsupported buffer config: fFlags=%#x, idxBuf=%u", fFlags, idxBuf);
return rc;
}
{
{
if ((pAppBuf->fDefaultProt & (RTMEM_PROT_READ | RTMEM_PROT_WRITE)) != (RTMEM_PROT_READ | RTMEM_PROT_WRITE))
RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf, pAppBuf->cb, pAppBuf->fDefaultProt), VINF_SUCCESS, false);
}
else
{
if ((pAppBuf->fDefaultProt & (RTMEM_PROT_READ | RTMEM_PROT_WRITE)) != (RTMEM_PROT_READ | RTMEM_PROT_WRITE))
RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf, pAppBuf->cb - PAGE_SIZE, pAppBuf->fDefaultProt), VINF_SUCCESS, false);
RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf + pAppBuf->cb - PAGE_SIZE, PAGE_SIZE, pAppBuf->fLastPageProt),
VINF_SUCCESS, false);
}
return true;
}
{
int rc = RTMemProtect(pbUsingBuf, pAppBuf->cb, pAppBuf->fDefaultProt | RTMEM_PROT_READ | RTMEM_PROT_WRITE);
if (RT_FAILURE(rc))
{
RTTestIFailed("RTMemProtect failed on %s buf #%u: %Rrc", pAppBuf->fIsCode ? "code" : "data", pAppBuf->idxCfg, rc);
return false;
}
return false;
}
/**
* @interface_method_impl{CIDETCORE::pfnReInitDataBuf}
*/
{
/*
* De-arm the buffer.
*/
return false;
/*
* Check the allocation requirements.
*/
{
RTTestIFailed("Buffer too small; off=%#x cb=%#x pAppBuf->cb=%#x (%s)",
return false;
}
/*
* Do we need to use the low buffer? Check that we have one, if we need it.
*/
return false;
/*
* Update the state.
*/
pBuf->cbPrologue = 0;
pBuf->cbEpilogue = 0;
if (fUseNormal)
else
return true;
}
/**
* @interface_method_impl{CIDETCORE::pfnSetupDataBuf}
*/
static DECLCALLBACK(bool) CidetAppCbSetupDataBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvSrc)
{
/*
* Copy over the data.
*/
/*
* Arm the buffer.
*/
}
/**
* @interface_method_impl{CIDETCORE::pfnIsBufEqual}
*/
static DECLCALLBACK(bool) CidetAppCbIsBufEqual(PCIDETCORE pThis, struct CIDETBUF *pBuf, void const *pvExpected)
{
/*
* Disarm the buffer if we can't read it all.
*/
return false;
/*
* Do the comparing.
*/
{
/** @todo RTMEM_PROT_NONE may kill content on some hosts... */
return false;
}
/** @todo check padding. */
return true;
}
/*
*
* Code buffer, prologue, epilogue, and execution.
* Code buffer, prologue, epilogue, and execution.
* Code buffer, prologue, epilogue, and execution.
*
*
*/
/**
* @interface_method_impl{CIDETCORE::pfnReInitCodeBuf}
*/
{
/*
* De-arm the buffer.
*/
return false;
/*
* Determin the prologue and epilogue sizes.
*/
uint16_t cbPrologue = 0;
{
/** @todo tricky stack. */
AssertReleaseFailedReturn(false);
}
/*
* Check the allocation requirements.
*/
{
RTTestIFailed("Buffer too small; off=%#x cb=%#x cbPro=%#x cbEpi=%#x pAppBuf->cb=%#x (%s)",
return false;
}
/*
* Update the state.
*/
pAppBuf->fUsingNormal = true;
return true;
}
/**
* @interface_method_impl{CIDETCORE::pfnSetupCodeBuf}
*/
static DECLCALLBACK(bool) CidetAppCbSetupCodeBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvInstr)
{
/*
* Emit prologue code.
*/
/*
* Copy over the code.
*/
/*
* Emit epilogue code.
*/
/* Restore working stack if necessary. */
{
/** @todo emit code for establishing a working stack. */
AssertReleaseFailedReturn(false);
}
else
{
/*
* The stack is reasonably good, do minimal work.
*/
/* push xCX */
*pbDst++ = 0x51;
/* mov xCX, &pThis->ActualCtx */
#ifdef RT_ARCH_AMD64
*pbDst++ = 0x48;
#endif
*pbDst++ = 0xb9;
/* pop [ss:rcx + ActualCtx.aGRegs[X86_GREG_xCX]] */
*pbDst++ = 0x36;
*pbDst++ = 0x8f;
*pbDst++ = 0x41;
/* mov [ss:rcx + ActualCtx.aGRegs[X86_GREG_xDX]], rdx */
*pbDst++ = 0x36;
#ifdef RT_ARCH_AMD64
*pbDst++ = 0x48;
#endif
*pbDst++ = 0x85;
*pbDst++ = 0x51;
/* mov [ss:rcx + ActualCtx.aSRegs[X86_GREG_DS]], ds */
*pbDst++ = 0x36;
*pbDst++ = 0x8c;
*pbDst++ = 0x99;
/* mov edx, 0XXYYh */
*pbDst++ = 0xba;
/* mov ds, dx */
*pbDst++ = 0x8e;
*pbDst++ = 0xda;
/* mov xDX, &pThisApp->ExecuteCtx */
#ifdef RT_ARCH_AMD64
*pbDst++ = 0x48;
#endif
*pbDst++ = 0xba;
#ifdef RT_ARCH_AMD64
/* jmp [cs:$ wrt rip] */
*pbDst++ = 0xff;
*pbDst++ = 0x25;
#else
/* jmp NAME(CidetAppSaveAndRestoreCtx) */
*pbDst++ = 0xb9;
#endif
}
("cbEpilogue=%#x, actual %#x\n", pBuf->cbEpilogue, pbDst - &pAppBuf->pbNormal[pBuf->offActive + pBuf->cb]));
/*
* Arm the buffer.
*/
}
/**
* @interface_method_impl{CIDETCORE::pfnSetupBuf}
*/
{
#ifdef RT_OS_WINDOWS
{
}
{
/* Won't end up here... */
}
#else
#endif
}
/*
*
*
* CIDET Application.
* CIDET Application.
* CIDET Application.
*
*
*/
/**
* @interface_method_impl{CIDETCORE::pfnSetupBuf}
*/
{
}
{
{
g_aCodeBufCfgs[i].fFlags);
if (RT_FAILURE(rc))
return rc;
}
{
g_aDataBufCfgs[i].fFlags);
if (RT_FAILURE(rc))
return rc;
}
return true;
}
{
if (!pThis)
/* Create a random source. */
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
/* Initialize the CIDET structure. */
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
}
}
else
{
}
}
else
return rc;
}
{
}
static void CidetAppTestBunch(PCIDETAPP pThis, PCCIDETINSTR paInstructions, uint32_t cInstructions, const char *pszBunchName)
{
{
}
}
{
/*
* Initialize the runtime.
*/
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
/*
* Parse arguments.
*/
static const RTGETOPTDEF s_aOptions[] =
{
};
int chOpt;
{
switch (chOpt)
{
case 'n':
break;
case 'h':
return RTEXITCODE_SUCCESS;
case 'V':
return RTEXITCODE_SUCCESS;
default:
}
}
#ifdef USE_SIGNALS
/*
* Set up signal handlers.
*/
# ifdef SIGEMT
# endif
#endif
/*
* Do the work.
*/
if (RT_SUCCESS(rc))
{
}
return RTTestSummaryAndDestroy(g_hTest);
}