diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-app.cpp | 1376 |
1 files changed, 1376 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp new file mode 100644 index 00000000..40ab8e7b --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp @@ -0,0 +1,1376 @@ +/* $Id: cidet-app.cpp $ */ +/** @file + * CPU Instruction Decoding & Execution Tests - Ring-3 Driver Application. + */ + +/* + * Copyright (C) 2014-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "cidet.h" + +#include <iprt/asm-amd64-x86.h> +#include <iprt/buildconfig.h> +#include <iprt/err.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/rand.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/test.h> + +#ifdef RT_OS_WINDOWS +# include <iprt/win/windows.h> +#else +# define USE_SIGNALS +# include <signal.h> +# include <unistd.h> +# include <sys/ucontext.h> +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** @def CIDET_LEAVE_GS_ALONE + * Leave GS alone on 64-bit darwin (gs is 0, no ldt or gdt entry to load that'll + * restore the lower 32-bits of the base when saving and restoring the register). + */ +#if (defined(RT_OS_DARWIN) && defined(RT_ARCH_AMD64)) || defined(DOXYGEN_RUNNING) +# define CIDET_LEAVE_GS_ALONE +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * CIDET driver app buffer. + */ +typedef struct CIDETAPPBUF +{ + /** The buffer size. */ + size_t cb; + /** The normal allocation. + * There is a fence page before this as well as at pbNormal+cb. */ + uint8_t *pbNormal; + /** The low memory allocation (32-bit addressable if 64-bit host, 16-bit + * addressable if 32-bit host). */ + uint8_t *pbLow; + /** 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). */ + uint8_t fDefaultProt : 3; + /** The memory protection for the last page (RTMEM_PROT_XXX). */ + uint8_t fLastPageProt : 3; + /** The buffer index. */ + uint16_t idxCfg; +} 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. */ + CIDETCORE Core; + /** The execute return context. */ + CIDETCPUCTX ExecuteCtx; + /** Code buffers (runs parallel to g_aCodeBufCfgs). */ + CIDETAPPBUF aCodeBuffers[CIDETAPP_CODE_BUF_COUNT]; + /** Data buffers (runs parallel to g_aDataBufCfgs). */ + CIDETAPPBUF aDataBuffers[CIDETAPP_DATA_BUF_COUNT]; + + /** The lowest stack address. */ + uint8_t *pbStackLow; + /** The end of the stack allocation (highest address). */ + uint8_t *pbStackEnd; + /** Stack size (= pbStackEnd - pbStackLow). */ + uint32_t cbStack; + /** Whether we're currently using the 'lock int3' to deal with tricky stack. */ + bool fUsingLockedInt3; +} CIDETAPP; +/** Pointer to a CIDET driver app instance. */ +typedef CIDETAPP *PCIDETAPP; +/** Pointer to a pointer to a CIDET driver app instance. */ +typedef PCIDETAPP *PPCIDETAPP; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The test instance handle. */ +static RTTEST g_hTest; +/** 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; +#ifdef USE_SIGNALS +/** The default signal mask. */ +static sigset_t g_ProcSigMask; +/** The alternative signal stack. */ +static stack_t g_AltStack; +#endif + + +/** Code buffer configurations (parallel to CIDETAPP::aCodeBuffers). */ +static CIDETBUFCFG g_aCodeBufCfgs[CIDETAPP_CODE_BUF_COUNT] = +{ + { + "Normal", + CIDETBUF_PROT_RWX | CIDETBUF_DPL_3 | CIDETBUF_DPL_SAME | CIDETBUF_SEG_ER | CIDETBUF_KIND_CODE, + }, +}; + +/** Data buffer configurations (parallel to CIDETAPP::aDataBuffers). */ +static CIDETBUFCFG g_aDataBufCfgs[CIDETAPP_DATA_BUF_COUNT] = +{ + { + "Normal", + CIDETBUF_PROT_RWX | CIDETBUF_DPL_3 | CIDETBUF_DPL_SAME | CIDETBUF_SEG_RW | CIDETBUF_KIND_DATA, + }, +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +DECLASM(void) CidetAppSaveAndRestoreCtx(void); +DECLASM(void) CidetAppRestoreCtx(PCCIDETCPUCTX pRestoreCtx); +DECLASM(void) CidetAppExecute(PCIDETCPUCTX pSaveCtx, PCCIDETCPUCTX pRestoreCtx); + + +/* + * + * + * Exception and signal handling. + * Exception and signal handling. + * Exception and signal handling. + * + * + */ + +#ifdef RT_OS_WINDOWS +static int CidetAppXcptFilter(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF +{ + /* + * Grab the this point. We expect at most one signal. + */ + PCIDETAPP pThis = g_pExecutingThis; + g_pExecutingThis = NULL; + if (pThis == NULL) + { + /* we're up the infamous creek... */ + for (;;) ExitProcess(2); + } + + /* + * Gather CPU state information from the context structure. + */ + CONTEXT *pSrcCtx = pXcptPtrs->ContextRecord; +# ifdef RT_ARCH_AMD64 + if ( (pSrcCtx->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)) + != (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)) + __debugbreak(); + pThis->Core.ActualCtx.rip = pSrcCtx->Rip; + pThis->Core.ActualCtx.rfl = pSrcCtx->EFlags; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pSrcCtx->Rax; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pSrcCtx->Rcx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pSrcCtx->Rdx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pSrcCtx->Rbx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pSrcCtx->Rsp; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pSrcCtx->Rbp; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pSrcCtx->Rsi; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pSrcCtx->Rdi; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x8] = pSrcCtx->R8; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x9] = pSrcCtx->R9; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = pSrcCtx->R10; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = pSrcCtx->R11; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = pSrcCtx->R12; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = pSrcCtx->R13; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = pSrcCtx->R14; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = pSrcCtx->R15; + pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pSrcCtx->SegEs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pSrcCtx->SegCs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pSrcCtx->SegSs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pSrcCtx->SegDs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pSrcCtx->SegFs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pSrcCtx->SegGs; + if (pSrcCtx->ContextFlags & CONTEXT_FLOATING_POINT) + { + /* ... */ + } + if (pSrcCtx->ContextFlags & CONTEXT_DEBUG_REGISTERS) + { + /* ... */ + } + +# elif defined(RT_ARCH_X86) + if ( (pSrcCtx->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)) + != (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)) + __debugbreak(); + pThis->Core.ActualCtx.rip = pSrcCtx->Eip; + pThis->Core.ActualCtx.rfl = pSrcCtx->EFlags; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pSrcCtx->Eax; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pSrcCtx->Ecx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pSrcCtx->Edx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pSrcCtx->Ebx; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pSrcCtx->Esp; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pSrcCtx->Ebp; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pSrcCtx->Esi; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pSrcCtx->Edi; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x8] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x9] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = 0; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = 0; + pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pSrcCtx->SegEs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pSrcCtx->SegCs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pSrcCtx->SegSs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pSrcCtx->SegDs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pSrcCtx->SegFs; + pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pSrcCtx->SegGs; + if (pSrcCtx->ContextFlags & CONTEXT_FLOATING_POINT) + { + /* ... */ + } + if (pSrcCtx->ContextFlags & CONTEXT_EXTENDED_REGISTERS) + { + /* ... */ + } + if (pSrcCtx->ContextFlags & CONTEXT_DEBUG_REGISTERS) + { + /* ... */ + } +# else +# error "Not supported" +# endif + + /* + * Add/Adjust CPU state information according to the exception code. + */ + pThis->Core.ActualCtx.uErr = UINT64_MAX; + switch (pXcptPtrs->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_INT_DIVIDE_BY_ZERO: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_DE; + break; + case EXCEPTION_SINGLE_STEP: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_DB; + break; + case EXCEPTION_BREAKPOINT: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_BP; + break; + case EXCEPTION_INT_OVERFLOW: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_OF; + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_BR; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_UD; + break; + + case EXCEPTION_PRIV_INSTRUCTION: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_GP; + pThis->Core.ActualCtx.uErr = 0; + break; + + case EXCEPTION_ACCESS_VIOLATION: + { + pThis->Core.ActualCtx.uXcpt = X86_XCPT_PF; + pThis->Core.ActualCtx.cr2 = pXcptPtrs->ExceptionRecord->ExceptionInformation[1]; + pThis->Core.ActualCtx.uErr = 0; + if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT) + pThis->Core.ActualCtx.uErr = X86_TRAP_PF_RW; + else if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT) + pThis->Core.ActualCtx.uErr = X86_TRAP_PF_ID; + else if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] != EXCEPTION_READ_FAULT) + AssertFatalFailed(); + + MEMORY_BASIC_INFORMATION MemInfo = {0}; + if (VirtualQuery((PVOID)pXcptPtrs->ExceptionRecord->ExceptionInformation[1], &MemInfo, sizeof(MemInfo)) > 0) + switch (MemInfo.Protect & 0xff) + { + 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: + pThis->Core.ActualCtx.uErr |= X86_TRAP_PF_P; + break; + default: + AssertFatalFailed(); + } + break; + } + + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_MF; + break; + + case EXCEPTION_DATATYPE_MISALIGNMENT: + pThis->Core.ActualCtx.uXcpt = X86_XCPT_AC; + break; + + default: + pThis->Core.ActualCtx.uXcpt = pXcptPtrs->ExceptionRecord->ExceptionCode; + break; + } + + /* + * Our own personal long jump implementation. + */ + CidetAppRestoreCtx(&pThis->ExecuteCtx); + + /* Won't return...*/ + return EXCEPTION_EXECUTE_HANDLER; +} + + +/** + * Vectored exception handler. + * + * @returns Long jumps or terminates the process. + * @param pXcptPtrs The exception record. + */ +static LONG CALLBACK CidetAppVectoredXcptHandler(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF +{ + RTStrmPrintf(g_pStdErr, "CidetAppVectoredXcptHandler!\n"); + CidetAppXcptFilter(pXcptPtrs); + + /* won't get here. */ + return EXCEPTION_CONTINUE_SEARCH; +} + + +/** + * Unhandled exception filter. + * + * @returns Long jumps or terminates the process. + * @param pXcptPtrs The exception record. + */ +static LONG CALLBACK CidetAppUnhandledXcptFilter(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF +{ + RTStrmPrintf(g_pStdErr, "CidetAppUnhandledXcptFilter!\n"); + CidetAppXcptFilter(pXcptPtrs); + + /* won't get here. */ + return EXCEPTION_CONTINUE_SEARCH; +} + + +#elif defined(USE_SIGNALS) +/** + * Signal handler. + */ +static void CidetAppSigHandler(int iSignal, siginfo_t *pSigInfo, void *pvCtx) +{ +# if 1 + if ( !g_pExecutingThis + || !g_pExecutingThis->fUsingLockedInt3 + || iSignal != SIGILL) + { + RTStrmPrintf(g_pStdErr, "signal %d pSigInfo=%p pvCtx=%p", iSignal, pSigInfo, pvCtx); + if (pSigInfo) + RTStrmPrintf(g_pStdErr, " si_addr=%p si_code=%#x sival_ptr=%p sival_int=%d", + pSigInfo->si_addr, pSigInfo->si_code, pSigInfo->si_value.sival_ptr, pSigInfo->si_value.sival_int); + RTStrmPrintf(g_pStdErr, "\n"); + } +# endif + + /* + * Grab the this point. We expect at most one signal. + */ + PCIDETAPP pThis = g_pExecutingThis; + g_pExecutingThis = NULL; + if (pThis == NULL) + { + /* we're up the infamous creek... */ + RTStrmPrintf(g_pStdErr, "Creek time!\n"); + for (;;) _exit(2); + } + + /* + * Gather all the CPU state information available. + */ +# ifdef RT_OS_LINUX + ucontext_t const *pCtx = (ucontext_t const *)pvCtx; +# ifdef RT_ARCH_AMD64 + + pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pCtx->uc_mcontext.gregs[REG_RAX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pCtx->uc_mcontext.gregs[REG_RCX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pCtx->uc_mcontext.gregs[REG_RDX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pCtx->uc_mcontext.gregs[REG_RBX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pCtx->uc_mcontext.gregs[REG_RSP]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pCtx->uc_mcontext.gregs[REG_RBP]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pCtx->uc_mcontext.gregs[REG_RSI]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pCtx->uc_mcontext.gregs[REG_RDI]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x8 ] = pCtx->uc_mcontext.gregs[REG_R8]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x9 ] = pCtx->uc_mcontext.gregs[REG_R9]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = pCtx->uc_mcontext.gregs[REG_R10]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = pCtx->uc_mcontext.gregs[REG_R11]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = pCtx->uc_mcontext.gregs[REG_R12]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = pCtx->uc_mcontext.gregs[REG_R13]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = pCtx->uc_mcontext.gregs[REG_R14]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = pCtx->uc_mcontext.gregs[REG_R15]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = RT_LO_U16((uint32_t)pCtx->uc_mcontext.gregs[REG_CSGSFS]); + pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = RT_HI_U16((uint32_t)pCtx->uc_mcontext.gregs[REG_CSGSFS]); + pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = (uint16_t)RT_HI_U32(pCtx->uc_mcontext.gregs[REG_CSGSFS]); + pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = ASMGetDS(); + pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = ASMGetES(); + pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = ASMGetSS(); + pThis->Core.ActualCtx.rip = pCtx->uc_mcontext.gregs[REG_RIP]; + pThis->Core.ActualCtx.rfl = pCtx->uc_mcontext.gregs[REG_EFL]; + pThis->Core.ActualCtx.cr2 = pCtx->uc_mcontext.gregs[REG_CR2]; + pThis->Core.ActualCtx.uXcpt = pCtx->uc_mcontext.gregs[REG_TRAPNO]; + pThis->Core.ActualCtx.uErr = pCtx->uc_mcontext.gregs[REG_ERR]; + + /* Fudge the FS and GS registers as setup_sigcontext returns 0. */ + if (pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] == 0) + pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pThis->Core.ExpectedCtx.aSRegs[X86_SREG_FS]; + if (pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] == 0) + pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pThis->Core.ExpectedCtx.aSRegs[X86_SREG_GS]; + +# elif defined(RT_ARCH_X86) + pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pCtx->uc_mcontext.gregs[REG_EAX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pCtx->uc_mcontext.gregs[REG_ECX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pCtx->uc_mcontext.gregs[REG_EDX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pCtx->uc_mcontext.gregs[REG_EBX]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pCtx->uc_mcontext.gregs[REG_ESP]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pCtx->uc_mcontext.gregs[REG_EBP]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pCtx->uc_mcontext.gregs[REG_ESI]; + pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pCtx->uc_mcontext.gregs[REG_EDI]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pCtx->uc_mcontext.gregs[REG_CS]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pCtx->uc_mcontext.gregs[REG_DS]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pCtx->uc_mcontext.gregs[REG_ES]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pCtx->uc_mcontext.gregs[REG_FS]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pCtx->uc_mcontext.gregs[REG_GS]; + pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pCtx->uc_mcontext.gregs[REG_SS]; + pThis->Core.ActualCtx.rip = pCtx->uc_mcontext.gregs[REG_EIP]; + pThis->Core.ActualCtx.rfl = pCtx->uc_mcontext.gregs[REG_EFL]; + pThis->Core.ActualCtx.cr2 = pCtx->uc_mcontext.cr2; + pThis->Core.ActualCtx.uXcpt = pCtx->uc_mcontext.gregs[REG_TRAPNO]; + pThis->Core.ActualCtx.uErr = pCtx->uc_mcontext.gregs[REG_ERR]; + +# else +# error "Unsupported arch." +# endif + + /* Adjust uErr. */ + switch (pThis->Core.ActualCtx.uXcpt) + { + case X86_XCPT_TS: + case X86_XCPT_NP: + case X86_XCPT_SS: + case X86_XCPT_GP: + case X86_XCPT_PF: + case X86_XCPT_AC: + case X86_XCPT_DF: + break; + default: + pThis->Core.ActualCtx.uErr = UINT64_MAX; + break; + } + +# if 0 + /* Fudge the resume flag (it's probably always set here). */ + if ( (pThis->Core.ActualCtx.rfl & X86_EFL_RF) + && !(pThis->Core.ExpectedCtx.rfl & X86_EFL_RF)) + pThis->Core.ActualCtx.rfl &= ~X86_EFL_RF; +# endif + +# else + /** @todo */ +# endif + + + /* + * Check for the 'lock int3' instruction used for tricky stacks. + */ + if ( pThis->fUsingLockedInt3 + && pThis->Core.ActualCtx.uXcpt == X86_XCPT_UD + && pThis->Core.ActualCtx.rip == pThis->Core.CodeBuf.uEffBufAddr - pThis->Core.CodeBuf.offSegBase + + pThis->Core.CodeBuf.offActive + pThis->Core.CodeBuf.cbActive ) + { + pThis->Core.ActualCtx.uXcpt = UINT32_MAX; + Assert(pThis->Core.ActualCtx.uErr == UINT64_MAX); + pThis->Core.ActualCtx.rfl &= ~X86_EFL_RF; + } + + /* + * Jump back to CidetAppCbExecute. + */ + CidetAppRestoreCtx(&pThis->ExecuteCtx); +} +#endif + + + +/* + * + * Buffer handling + * Buffer handling + * Buffer handling + * + * + */ + +static int cidetAppAllocateAndConfigureOneBuffer(PCIDETAPP pThis, PCIDETAPPBUF pBuf, uint16_t idxBuf, bool fIsCode, + uint32_t fFlags) +{ + RT_NOREF_PV(pThis); + static uint8_t const s_afBufProtToDefaultMemProt[] = + { + /* [0] = */ RTMEM_PROT_NONE, + /* [1] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, + /* [2] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE, + /* [3] = */ RTMEM_PROT_READ | RTMEM_PROT_EXEC, + /* [4] = */ RTMEM_PROT_READ, + /* [5] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, + /* [6] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, + /* [7] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, + /* [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, + /* [1] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, + /* [2] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE, + /* [3] = */ RTMEM_PROT_READ | RTMEM_PROT_EXEC, + /* [4] = */ RTMEM_PROT_READ, + /* [5] = */ RTMEM_PROT_NONE, + /* [6] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE, + /* [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; + Assert(CIDETBUF_IS_CODE(fFlags) == fIsCode); + pBuf->fIsCode = fIsCode; + pBuf->idxCfg = idxBuf; + pBuf->fUsingNormal = true; + pBuf->fDefaultProt = s_afBufProtToDefaultMemProt[fFlags & CIDETBUF_PROT_MASK]; + pBuf->fLastPageProt = s_afBufProtToLastPageMemProt[fFlags & CIDETBUF_PROT_MASK]; + if (pBuf->fDefaultProt != RTMEM_PROT_NONE) + { + /* + * Allocate a 3 page buffer plus two fence pages. + */ + pBuf->cb = fIsCode ? CIDET_CODE_BUF_SIZE : CIDET_DATA_BUF_SIZE; + pBuf->pbNormal = (uint8_t *)RTMemPageAlloc(PAGE_SIZE + pBuf->cb + PAGE_SIZE); + if (pBuf->pbNormal) + { + memset(pBuf->pbNormal, 0x55, PAGE_SIZE); + memset(pBuf->pbNormal + PAGE_SIZE, 0xcc, pBuf->cb); + memset(pBuf->pbNormal + PAGE_SIZE + pBuf->cb, 0x77, PAGE_SIZE); + + /* Set up fence pages. */ + rc = RTMemProtect(pBuf->pbNormal, PAGE_SIZE, RTMEM_PROT_NONE); /* fence */ + if (RT_SUCCESS(rc)) + rc = RTMemProtect(pBuf->pbNormal + PAGE_SIZE + pBuf->cb, PAGE_SIZE, RTMEM_PROT_NONE); /* fence */ + pBuf->pbNormal += PAGE_SIZE; + + /* 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) + && (uintptr_t)pBuf->pbNormal + pBuf->cb > RT_BIT_64(sizeof(uintptr_t) / 2 * 8)) + { + /** @todo Buffers for the other addressing mode. */ + pBuf->pbLow = NULL; + } + else + pBuf->pbLow = pBuf->pbNormal; + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + } + else + rc = RTTestIFailedRc(VERR_NO_PAGE_MEMORY, "Error allocating three pages."); + } + else + rc = RTTestIFailedRc(VERR_NO_PAGE_MEMORY, "Unsupported buffer config: fFlags=%#x, idxBuf=%u", fFlags, idxBuf); + return rc; +} + + +static void CidetAppDeleteBuffer(PCIDETAPPBUF pBuf) +{ + RTMemProtect(pBuf->pbNormal - PAGE_SIZE, PAGE_SIZE + pBuf->cb + PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + RTMemPageFree(pBuf->pbNormal - PAGE_SIZE, PAGE_SIZE + pBuf->cb + PAGE_SIZE); + if (pBuf->pbLow != pBuf->pbNormal && pBuf->pbLow) + { + RTMemProtect(pBuf->pbLow, pBuf->cb, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + RTMemFreeEx(pBuf->pbLow, pBuf->cb); + } +} + + +static bool CidetAppArmBuf(PCIDETAPP pThis, PCIDETAPPBUF pAppBuf) +{ + RT_NOREF_PV(pThis); + uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow); + if (pAppBuf->fLastPageProt == pAppBuf->fDefaultProt) + { + 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); + } + pAppBuf->fArmed = true; + return true; +} + + +static bool CidetAppDearmBuf(PCIDETAPP pThis, PCIDETAPPBUF pAppBuf) +{ + RT_NOREF_PV(pThis); + uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow); + 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; + } + pAppBuf->fArmed = false; + return true; +} + + +/** + * @interface_method_impl{CIDETCORE,pfnReInitDataBuf} + */ +static DECLCALLBACK(bool) CidetAppCbReInitDataBuf(PCIDETCORE pThis, PCIDETBUF pBuf) +{ + PCIDETAPP pThisApp = (PCIDETAPP)pThis; + PCIDETAPPBUF pAppBuf = &pThisApp->aDataBuffers[pBuf->idxCfg]; + Assert(CIDETBUF_IS_DATA(pBuf->pCfg->fFlags)); + + /* + * De-arm the buffer. + */ + if (pAppBuf->fArmed) + if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf))) + return false; + + /* + * Check the allocation requirements. + */ + if (RT_UNLIKELY((size_t)pBuf->off + pBuf->cb > pAppBuf->cb)) + { + RTTestIFailed("Buffer too small; off=%#x cb=%#x pAppBuf->cb=%#x (%s)", + pBuf->off, pBuf->cb, pAppBuf->cb, pBuf->pCfg->pszName); + return false; + } + + /* + * Do we need to use the low buffer? Check that we have one, if we need it. + */ + bool fUseNormal = pThis->cbAddrMode == ARCH_BITS / 8; + if (!fUseNormal && !pAppBuf->pbLow) + return false; + + /* + * Update the state. + */ + pAppBuf->fUsingNormal = fUseNormal; + + pBuf->offActive = pBuf->off; + pBuf->cbActive = pBuf->cb; + pBuf->cbPrologue = 0; + pBuf->cbEpilogue = 0; + pBuf->uSeg = UINT32_MAX; + pBuf->cbActiveSegLimit = UINT64_MAX; + pBuf->uSegBase = 0; + if (fUseNormal) + pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbNormal; + else + pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbLow; + + return true; +} + + +/** + * @interface_method_impl{CIDETCORE,pfnSetupDataBuf} + */ +static DECLCALLBACK(bool) CidetAppCbSetupDataBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvSrc) +{ + PCIDETAPP pThisApp = (PCIDETAPP)pThis; + PCIDETAPPBUF pAppBuf = &pThisApp->aDataBuffers[pBuf->idxCfg]; + Assert(CIDETBUF_IS_DATA(pBuf->pCfg->fFlags)); + Assert(!pAppBuf->fArmed); + + + /* + * Copy over the data. + */ + uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow); + memcpy(pbUsingBuf + pBuf->offActive, pvSrc, pBuf->cbActive); + + /* + * Arm the buffer. + */ + return CidetAppArmBuf(pThisApp, pAppBuf); +} + + +/** + * @interface_method_impl{CIDETCORE,pfnIsBufEqual} + */ +static DECLCALLBACK(bool) CidetAppCbIsBufEqual(PCIDETCORE pThis, struct CIDETBUF *pBuf, void const *pvExpected) +{ + PCIDETAPP pThisApp = (PCIDETAPP)pThis; + PCIDETAPPBUF pAppBuf = CIDETBUF_IS_CODE(pBuf->pCfg->fFlags) + ? &pThisApp->aCodeBuffers[pBuf->idxCfg] + : &pThisApp->aDataBuffers[pBuf->idxCfg]; + + /* + * Disarm the buffer if we can't read it all. + */ + if ( pAppBuf->fArmed + && ( !(pAppBuf->fLastPageProt & RTMEM_PROT_READ) + || !(pAppBuf->fDefaultProt & RTMEM_PROT_READ)) ) + if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf))) + return false; + + /* + * Do the comparing. + */ + uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow); + if (memcmp(pbUsingBuf + pBuf->offActive, pvExpected, pBuf->cbActive) != 0) + { + /** @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} + */ +static DECLCALLBACK(bool) CidetAppCbReInitCodeBuf(PCIDETCORE pThis, PCIDETBUF pBuf) +{ + PCIDETAPP pThisApp = (PCIDETAPP)pThis; + PCIDETAPPBUF pAppBuf = &pThisApp->aCodeBuffers[pBuf->idxCfg]; + Assert(CIDETBUF_IS_CODE(pBuf->pCfg->fFlags)); + Assert(pAppBuf->fUsingNormal); + + /* + * De-arm the buffer. + */ + if (pAppBuf->fArmed) + if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf))) + return false; + + /* + * Determin the prologue and epilogue sizes. + */ + uint16_t cbPrologue = 0; + uint16_t cbEpilogue = ARCH_BITS == 64 ? 0x56 : 0x4e; + if (pThis->InCtx.fTrickyStack) + cbEpilogue = 16; + + /* + * Check the allocation requirements. + */ + if (RT_UNLIKELY( cbPrologue > pBuf->off + || (size_t)pBuf->off + pBuf->cb + cbEpilogue > pAppBuf->cb)) + { + RTTestIFailed("Buffer too small; off=%#x cb=%#x cbPro=%#x cbEpi=%#x pAppBuf->cb=%#x (%s)", + pBuf->off, pBuf->cb, cbPrologue, cbEpilogue, pAppBuf->cb, pBuf->pCfg->pszName); + return false; + } + + /* + * Update the state. + */ + pAppBuf->fUsingNormal = true; + + pBuf->cbActive = pBuf->cb; + pBuf->offActive = pBuf->off; + pBuf->cbPrologue = cbPrologue; + pBuf->cbEpilogue = cbEpilogue; + pBuf->uSeg = UINT32_MAX; + pBuf->cbActiveSegLimit = UINT64_MAX; + pBuf->uSegBase = 0; + pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbNormal; + + return true; +} + + +/** + * @interface_method_impl{CIDETCORE,pfnSetupCodeBuf} + */ +static DECLCALLBACK(bool) CidetAppCbSetupCodeBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvInstr) +{ + PCIDETAPP pThisApp = (PCIDETAPP)pThis; + PCIDETAPPBUF pAppBuf =&pThisApp->aCodeBuffers[pBuf->idxCfg]; + Assert(CIDETBUF_IS_CODE(pBuf->pCfg->fFlags)); + Assert(pAppBuf->fUsingNormal); + Assert(!pAppBuf->fArmed); + + /* + * Emit prologue code. + */ + uint8_t *pbDst = pAppBuf->pbNormal + pBuf->offActive - pBuf->cbPrologue; + + /* + * Copy over the code. + */ + Assert(pbDst == &pAppBuf->pbNormal[pBuf->offActive]); + memcpy(pbDst, pvInstr, pBuf->cbActive); + pbDst += pBuf->cbActive; + + /* + * Emit epilogue code. + */ + if (!pThis->InCtx.fTrickyStack) + { + /* + * The stack is reasonably good, do minimal work. + * + * Note! Ideally, we would just fill in 16 int3s here and check that + * we hit the first right one. However, if we wish to run this + * code with IEM, we better skip unnecessary trips to ring-0. + */ + uint8_t * const pbStartEpilogue = pbDst; + + /* jmp $+6 */ + *pbDst++ = 0xeb; + *pbDst++ = 0x06; /* This is a push es, so if the decoder is one off, we'll hit the int 3 below. */ + + /* Six int3s for trapping incorrectly decoded instructions. */ + *pbDst++ = 0xcc; + *pbDst++ = 0xcc; + *pbDst++ = 0xcc; + *pbDst++ = 0xcc; + *pbDst++ = 0xcc; + *pbDst++ = 0xcc; + + /* push rip / call $+0 */ + *pbDst++ = 0xe8; + *pbDst++ = 0x00; + *pbDst++ = 0x00; + *pbDst++ = 0x00; + *pbDst++ = 0x00; + uint8_t offRipAdjust = (uint8_t)(uintptr_t)(pbStartEpilogue - pbDst); + + /* push xCX */ + *pbDst++ = 0x51; + + /* mov xCX, [xSP + xCB] */ + *pbDst++ = 0x48; + *pbDst++ = 0x8b; + *pbDst++ = 0x4c; + *pbDst++ = 0x24; + *pbDst++ = sizeof(uintptr_t); + + /* lea xCX, [xCX - 24] */ + *pbDst++ = 0x48; + *pbDst++ = 0x8d; + *pbDst++ = 0x49; + *pbDst++ = offRipAdjust; + + /* mov xCX, [xSP + xCB] */ + *pbDst++ = 0x48; + *pbDst++ = 0x89; + *pbDst++ = 0x4c; + *pbDst++ = 0x24; + *pbDst++ = sizeof(uintptr_t); + + /* mov xCX, &pThis->ActualCtx */ +#ifdef RT_ARCH_AMD64 + *pbDst++ = 0x48; +#endif + *pbDst++ = 0xb9; + *(uintptr_t *)pbDst = (uintptr_t)&pThisApp->Core.ActualCtx; + pbDst += sizeof(uintptr_t); + + /* pop [ss:rcx + ActualCtx.aGRegs[X86_GREG_xCX]] */ + *pbDst++ = 0x36; + *pbDst++ = 0x8f; + *pbDst++ = 0x41; + *pbDst++ = RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xCX]); + Assert(RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xCX]) < 0x7f); + + /* mov [ss:rcx + ActualCtx.aGRegs[X86_GREG_xDX]], rdx */ + *pbDst++ = 0x36; +#ifdef RT_ARCH_AMD64 + *pbDst++ = 0x48; +#endif + *pbDst++ = 0x89; + *pbDst++ = 0x51; + *pbDst++ = RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xDX]); + Assert(RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xDX]) < 0x7f); + + /* mov [ss:rcx + ActualCtx.aSRegs[X86_GREG_DS]], ds */ + *pbDst++ = 0x36; + *pbDst++ = 0x8c; + *pbDst++ = 0x99; + *(uint32_t *)pbDst = RT_UOFFSETOF(CIDETCPUCTX, aSRegs[X86_SREG_DS]); + pbDst += sizeof(uint32_t); + + /* mov edx, 0XXYYh */ + *pbDst++ = 0xba; + *(uint32_t *)pbDst = pThisApp->Core.InTemplateCtx.aSRegs[X86_SREG_DS]; + pbDst += sizeof(uint32_t); + + /* mov ds, dx */ + *pbDst++ = 0x8e; + *pbDst++ = 0xda; + + /* mov xDX, &pThisApp->ExecuteCtx */ +#ifdef RT_ARCH_AMD64 + *pbDst++ = 0x48; +#endif + *pbDst++ = 0xba; + *(uintptr_t *)pbDst = (uintptr_t)&pThisApp->ExecuteCtx; + pbDst += sizeof(uintptr_t); + +#ifdef RT_ARCH_AMD64 + /* jmp [cs:$ wrt rip] */ + *pbDst++ = 0xff; + *pbDst++ = 0x25; + *(uint32_t *)pbDst = 0; + pbDst += sizeof(uint32_t); +#else + /* jmp NAME(CidetAppSaveAndRestoreCtx) */ + *pbDst++ = 0xb9; +#endif + *(uintptr_t *)pbDst = (uintptr_t)CidetAppSaveAndRestoreCtx; + pbDst += sizeof(uintptr_t); + + /* int3 */ + *pbDst++ = 0xcc; + + pThisApp->fUsingLockedInt3 = false; + + } + else + { + /* + * Tricky stack, so just make it raise #UD after a successful run. + */ + *pbDst++ = 0xf0; /* lock prefix */ + memset(pbDst, 0xcc, 15); /* int3 */ + pbDst += 15; + + pThisApp->fUsingLockedInt3 = true; + } + + AssertMsg(pbDst == &pAppBuf->pbNormal[pBuf->offActive + pBuf->cb + pBuf->cbEpilogue], + ("cbEpilogue=%#x, actual %#x\n", pBuf->cbEpilogue, pbDst - &pAppBuf->pbNormal[pBuf->offActive + pBuf->cb])); + + /* + * Arm the buffer. + */ + return CidetAppArmBuf(pThisApp, pAppBuf); +} + + +/** + * @interface_method_impl{CIDETCORE,pfnExecute} + */ +static DECLCALLBACK(bool) CidetAppCbExecute(PCIDETCORE pThis) +{ +#if defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) + /* Skip tricky stack because windows cannot dispatch exception if RSP/ESP is bad. */ + if (pThis->InCtx.fTrickyStack) + return false; +#endif + + g_pExecutingThis = (PCIDETAPP)pThis; +#ifdef RT_OS_WINDOWS + __try + { + CidetAppExecute(&((PCIDETAPP)pThis)->ExecuteCtx, &pThis->InCtx); + } + __except (CidetAppXcptFilter(GetExceptionInformation())) + { + /* Won't end up here... */ + } + g_pExecutingThis = NULL; +#else + CidetAppExecute(&((PCIDETAPP)pThis)->ExecuteCtx, &pThis->InCtx); + if (g_pExecutingThis) + g_pExecutingThis = NULL; + else + { + RTTESTI_CHECK_RC(sigprocmask(SIG_SETMASK, &g_ProcSigMask, NULL), 0); + RTTESTI_CHECK_RC(sigaltstack(&g_AltStack, NULL), 0); + } +#endif + + return true; +} + + + + +/* + * + * + * CIDET Application. + * CIDET Application. + * CIDET Application. + * + * + */ + + +/** + * @interface_method_impl{CIDETCORE,pfnFailure} + */ +static DECLCALLBACK(void) CidetAppCbFailureV(PCIDETCORE pThis, const char *pszFormat, va_list va) +{ + RT_NOREF_PV(pThis); + RTTestIFailedV(pszFormat, va); +} + + +static int cidetAppAllocateAndConfigureBuffers(PCIDETAPP pThis) +{ + /* + * Code buffers. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCodeBuffers); i++) + { + int rc = cidetAppAllocateAndConfigureOneBuffer(pThis, &pThis->aCodeBuffers[i], i, true /*fCode*/, + g_aCodeBufCfgs[i].fFlags); + if (RT_FAILURE(rc)) + return rc; + } + + /* + * Data buffers. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDataBuffers); i++) + { + int rc = cidetAppAllocateAndConfigureOneBuffer(pThis, &pThis->aDataBuffers[i], i, false /*fCode*/, + g_aDataBufCfgs[i].fFlags); + if (RT_FAILURE(rc)) + return rc; + } + + /* + * Stack. + */ + pThis->cbStack = _32K; + pThis->pbStackLow = (uint8_t *)RTMemPageAlloc(pThis->cbStack); + if (!pThis->pbStackLow) + { + RTTestIFailed("Failed to allocate %u bytes for stack\n", pThis->cbStack); + return false; + } + pThis->pbStackEnd = pThis->pbStackLow + pThis->cbStack; + + return true; +} + + +static int CidetAppCreate(PPCIDETAPP ppThis) +{ + *ppThis = NULL; + + PCIDETAPP pThis = (PCIDETAPP)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return RTTestIFailedRc(VERR_NO_MEMORY, "Error allocating %zu bytes.", sizeof(*pThis)); + + /* Create a random source. */ + RTRAND hRand; + int rc = RTRandAdvCreateParkMiller(&hRand); + if (RT_SUCCESS(rc)) + { + uint64_t uSeed = ASMReadTSC(); + rc = RTRandAdvSeed(hRand, uSeed); + if (RT_SUCCESS(rc)) + RTTestIPrintf(RTTESTLVL_ALWAYS, "Random seed %#llx\n", uSeed); + + /* Initialize the CIDET structure. */ + rc = CidetCoreInit(&pThis->Core, hRand); + if (RT_SUCCESS(rc)) + { + pThis->Core.pfnReInitDataBuf = CidetAppCbReInitDataBuf; + pThis->Core.pfnSetupDataBuf = CidetAppCbSetupDataBuf; + pThis->Core.pfnIsBufEqual = CidetAppCbIsBufEqual; + pThis->Core.pfnReInitCodeBuf = CidetAppCbReInitCodeBuf; + pThis->Core.pfnSetupCodeBuf = CidetAppCbSetupCodeBuf; + pThis->Core.pfnExecute = CidetAppCbExecute; + pThis->Core.pfnFailure = CidetAppCbFailureV; + + pThis->Core.paCodeBufConfigs = g_aCodeBufCfgs; + pThis->Core.cCodeBufConfigs = CIDETAPP_CODE_BUF_COUNT; + pThis->Core.paDataBufConfigs = g_aDataBufCfgs; + pThis->Core.cDataBufConfigs = CIDETAPP_DATA_BUF_COUNT; + + rc = cidetAppAllocateAndConfigureBuffers(pThis); + if (RT_SUCCESS(rc)) + { + rc = CidetCoreSetTargetMode(&pThis->Core, ARCH_BITS == 32 ? CIDETMODE_PP_32 : CIDETMODE_LM_64); + if (RT_SUCCESS(rc)) + { + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_CS] = ASMGetCS(); + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_DS] = ASMGetDS(); + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_ES] = ASMGetES(); + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_FS] = ASMGetFS(); + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_GS] = ASMGetGS(); + pThis->Core.InTemplateCtx.aSRegs[X86_SREG_SS] = ASMGetSS(); + pThis->Core.InTemplateCtx.aGRegs[X86_GREG_xSP] = (uintptr_t)pThis->pbStackEnd - 64; + + pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_CS; + pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_DS; + pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_ES; +#if !defined(RT_OS_WINDOWS) + pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_FS; +#endif +#if !defined(CIDET_LEAVE_GS_ALONE) + pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_GS; +#endif + + *ppThis = pThis; + return VINF_SUCCESS; + } + rc = RTTestIFailedRc(rc, "Error setting target mode: %Rrc", rc); + } + CidetCoreDelete(&pThis->Core); + } + else + { + rc = RTTestIFailedRc(rc, "CidetCoreInit failed: %Rrc", rc); + RTRandAdvDestroy(hRand); + } + } + else + rc = RTTestIFailedRc(rc, "RTRandAdvCreate failed: %Rrc", rc); + RTMemFree(pThis); + return rc; +} + + +static void CidetAppDestroy(PCIDETAPP pThis) +{ + CidetCoreDelete(&pThis->Core); + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCodeBuffers); i++) + CidetAppDeleteBuffer(&pThis->aCodeBuffers[i]); + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDataBuffers); i++) + CidetAppDeleteBuffer(&pThis->aDataBuffers[i]); + RTMemPageFree(pThis->pbStackLow, pThis->cbStack); + + RTMemFree(pThis); +} + + +static void CidetAppTestBunch(PCIDETAPP pThis, PCCIDETINSTR paInstructions, uint32_t cInstructions, const char *pszBunchName) +{ + for (uint32_t iInstr = 0; iInstr < cInstructions; iInstr++) + { + RTTestSubF(g_hTest, "%s - %s", pszBunchName, paInstructions[iInstr].pszMnemonic); + CidetCoreTestInstruction(&pThis->Core, &paInstructions[iInstr]); + } +} + + +int main(int argc, char **argv) +{ + /* + * Initialize the runtime. + */ + RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, 0, "cidet-app", &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + /* + * Parse arguments. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--noop", 'n', RTGETOPT_REQ_NOTHING }, + }; + + int chOpt; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + while ((chOpt = RTGetOpt(&GetState, &ValueUnion))) + { + switch (chOpt) + { + case 'n': + break; + + case 'h': + RTPrintf("usage: %s\n", argv[0]); + return RTEXITCODE_SUCCESS; + + case 'V': + RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision()); + return RTEXITCODE_SUCCESS; + + default: + return RTGetOptPrintError(chOpt, &ValueUnion); + } + } + +#ifdef USE_SIGNALS + /* + * Set up signal handlers with alternate stack. + */ + /* Get the default signal mask. */ + RTTESTI_CHECK_RC_RET(sigprocmask(SIG_BLOCK, NULL, &g_ProcSigMask), 0, RTEXITCODE_FAILURE); + + /* Alternative stack so we can play with esp/rsp. */ + RT_ZERO(g_AltStack); + g_AltStack.ss_flags = 0; +# ifdef SIGSTKSZ + g_AltStack.ss_size = RT_MAX(SIGSTKSZ, _128K); +# else + g_AltStack.ss_size = _128K; +# endif +#ifdef RT_OS_FREEBSD + g_AltStack.ss_sp = (char *)RTMemPageAlloc(g_AltStack.ss_size); +#else + g_AltStack.ss_sp = RTMemPageAlloc(g_AltStack.ss_size); +#endif + RTTESTI_CHECK_RET(g_AltStack.ss_sp != NULL, RTEXITCODE_FAILURE); + RTTESTI_CHECK_RC_RET(sigaltstack(&g_AltStack, NULL), 0, RTEXITCODE_FAILURE); + + /* Default signal action config. */ + struct sigaction Act; + RT_ZERO(Act); + Act.sa_sigaction = CidetAppSigHandler; + Act.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigfillset(&Act.sa_mask); + + /* Hook the signals we might need. */ + sigaction(SIGILL, &Act, NULL); + sigaction(SIGTRAP, &Act, NULL); +# ifdef SIGEMT + sigaction(SIGEMT, &Act, NULL); +# endif + sigaction(SIGFPE, &Act, NULL); + sigaction(SIGBUS, &Act, NULL); + sigaction(SIGSEGV, &Act, NULL); + +#elif defined(RT_OS_WINDOWS) + /* + * Register vectored exception handler and override the default unhandled + * exception filter, just to be on the safe side... + */ + RTTESTI_CHECK(AddVectoredExceptionHandler(1 /* first */, CidetAppVectoredXcptHandler) != NULL); + SetUnhandledExceptionFilter(CidetAppUnhandledXcptFilter); +#endif + + /* + * Do the work. + */ + RTTestBanner(g_hTest); + + PCIDETAPP pThis; + int rc = CidetAppCreate(&pThis); + if (RT_SUCCESS(rc)) + { + CidetAppTestBunch(pThis, g_aCidetInstructions1, g_cCidetInstructions1, "First Bunch"); + + CidetAppDestroy(pThis); + } + + return RTTestSummaryAndDestroy(g_hTest); +} + |