diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/ValidationKit/utils/cpu | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit/utils/cpu')
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/Makefile.kmk | 84 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-app.cpp | 1376 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-appA.asm | 319 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-core.cpp | 2368 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp | 297 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet.h | 1092 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet.mac | 75 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp | 223 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp | 205 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm | 160 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp | 272 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm | 162 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/rdtsc.cpp | 294 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm | 162 | ||||
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp | 130 |
15 files changed, 7219 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/utils/cpu/Makefile.kmk b/src/VBox/ValidationKit/utils/cpu/Makefile.kmk new file mode 100644 index 00000000..219f928b --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/Makefile.kmk @@ -0,0 +1,84 @@ +# $Id: Makefile.kmk $ +## @file +# VirtualBox Validation Kit - CPU Test Utilities. +# + +# +# Copyright (C) 2009-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 +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if1of ($(KBUILD_TARGET_ARCH), x86 amd64) + PROGRAMS += xmmsaving + xmmsaving_TEMPLATE = VBoxValidationKitR3 + xmmsaving_SOURCES = xmmsaving.cpp xmmsaving-asm.asm +endif + +if1of ($(KBUILD_TARGET_ARCH), x86 amd64) + PROGRAMS += exceptionsR3 + exceptionsR3_TEMPLATE = VBoxValidationKitR3 + exceptionsR3_SOURCES = exceptionsR3.cpp exceptionsR3-asm.asm +endif + +PROGRAMS += cpu-numa +cpu-numa_TEMPLATE = VBoxValidationKitR3 +cpu-numa_SOURCES = cpu-numa.cpp + +PROGRAMS += cpu-alloc-all-mem +cpu-alloc-all-mem_TEMPLATE = VBoxValidationKitR3 +cpu-alloc-all-mem_SOURCES = cpu-alloc-all-mem.cpp + +if1of ($(KBUILD_TARGET_ARCH), x86 amd64) + ifneq ($(KBUILD_HOST),os2) + PROGRAMS += cidet-app + endif + cidet-app_TEMPLATE = VBoxValidationKitR3 + cidet-app_SOURCES = \ + cidet-app.cpp \ + cidet-appA.asm \ + cidet-core.cpp \ + cidet-instr-1.cpp + cidet-app_DEFS = IN_DIS + cidet-app_DEFS.linux = CIDET_REDUCED_CTX + cidet-app_LIBS = $(PATH_STAGE_LIB)/DisasmR3Static$(VBOX_SUFF_LIB) + cidet-app_VBOX_IMPORT_CHECKER.win.x86 = $(NO_SUCH_VARIABLE) # doesn't work on NT4 yet. +endif + +if1of ($(KBUILD_TARGET_ARCH), x86 amd64) + PROGRAMS += rdtsc + rdtsc_TEMPLATE = VBoxValidationKitR3 + rdtsc_SOURCES = rdtsc.cpp rdtsc-asm.asm +endif + + +include $(FILE_KBUILD_SUB_FOOTER) + 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); +} + diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm b/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm new file mode 100644 index 00000000..55feb013 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm @@ -0,0 +1,319 @@ +; $Id: cidet-appA.asm $ +;; @file +; CPU Instruction Decoding & Execution Tests - Ring-3 Driver Application, Assembly Code. +; + +; +; Copyright (C) 2009-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 "iprt/asmdefs.mac" +%include "iprt/x86.mac" +%include "cidet.mac" + + +;******************************************************************************* +;* Global Variables * +;******************************************************************************* +%ifdef RT_ARCH_X86 +;; Used by CidetAppSaveAndRestoreCtx when we have a tricky target stack. +g_uTargetEip dd 0 +g_uTargetCs dw 0 +%endif + + +;; +; 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). +%ifdef RT_OS_DARWIN + %ifdef RT_ARCH_AMD64 + %define CIDET_LEAVE_GS_ALONE + %endif +%endif + + + +BEGINCODE + +;; +; ASSUMES that it's called and the EIP/RIP is found on the stack. +; +; @param pSaveCtx ds:xCX The context to save; DS, xDX and xCX have +; already been saved by the caller. +; @param pRestoreCtx ds:xDX The context to restore. +; +BEGINPROC CidetAppSaveAndRestoreCtx + ; + ; Save the stack pointer and program counter first so we can later + ; bypass this step if we need to. + ; + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8], xAX ; need scratch register. + lea xAX, [xSP + xCB] + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8], xAX + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2], ss + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2], cs + mov xAX, [xSP] + mov [xCX + CIDETCPUCTX.rip], xAX + jmp CidetAppSaveAndRestoreCtx_1 + +GLOBALNAME CidetAppSaveAndRestoreCtx_NoSsSpCsIp + mov [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8], xAX +CidetAppSaveAndRestoreCtx_1: + + ; Flags. +%ifdef RT_ARCH_AMD64 + pushfq +%else + pushfd +%endif + pop xAX + mov [xCX + CIDETCPUCTX.rfl], xAX + + ; Segment registers. + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_ES * 2], es + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_FS * 2], fs + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_GS * 2], gs + + ; Remaining GPRs. + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8], xBX + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8], xBP + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8], xSI + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8], xDI +%ifdef RT_ARCH_AMD64 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8], r8 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8], r9 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8], r10 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8], r11 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8], r12 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8], r13 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8], r14 + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8], r15 + xor eax, eax + mov [xCX + CIDETCPUCTX.cr2], rax + %ifndef CIDET_REDUCED_CTX + mov [xCX + CIDETCPUCTX.cr0], rax + mov [xCX + CIDETCPUCTX.cr3], rax + mov [xCX + CIDETCPUCTX.cr4], rax + mov [xCX + CIDETCPUCTX.cr8], rax + mov [xCX + CIDETCPUCTX.dr0], rax + mov [xCX + CIDETCPUCTX.dr1], rax + mov [xCX + CIDETCPUCTX.dr2], rax + mov [xCX + CIDETCPUCTX.dr3], rax + mov [xCX + CIDETCPUCTX.dr6], rax + mov [xCX + CIDETCPUCTX.dr7], rax + mov [xCX + CIDETCPUCTX.tr], ax + mov [xCX + CIDETCPUCTX.ldtr], ax + %endif +%else + xor eax, eax + mov [xCX + CIDETCPUCTX.rfl + 4], eax + mov [xCX + CIDETCPUCTX.rip + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8 ], eax + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8 + 4], eax + mov [xCX + CIDETCPUCTX.cr2 ], eax + mov [xCX + CIDETCPUCTX.cr2 + 4], eax + %ifndef CIDET_REDUCED_CTX + mov [xCX + CIDETCPUCTX.cr0 ], eax + mov [xCX + CIDETCPUCTX.cr0 + 4], eax + mov [xCX + CIDETCPUCTX.cr3 ], eax + mov [xCX + CIDETCPUCTX.cr3 + 4], eax + mov [xCX + CIDETCPUCTX.cr4 ], eax + mov [xCX + CIDETCPUCTX.cr4 + 4], eax + mov [xCX + CIDETCPUCTX.cr8 ], eax + mov [xCX + CIDETCPUCTX.cr8 + 4], eax + mov [xCX + CIDETCPUCTX.dr0 ], eax + mov [xCX + CIDETCPUCTX.dr0 + 4], eax + mov [xCX + CIDETCPUCTX.dr1 ], eax + mov [xCX + CIDETCPUCTX.dr1 + 4], eax + mov [xCX + CIDETCPUCTX.dr2 ], eax + mov [xCX + CIDETCPUCTX.dr2 + 4], eax + mov [xCX + CIDETCPUCTX.dr3 ], eax + mov [xCX + CIDETCPUCTX.dr3 + 4], eax + mov [xCX + CIDETCPUCTX.dr6 ], eax + mov [xCX + CIDETCPUCTX.dr6 + 4], eax + mov [xCX + CIDETCPUCTX.dr7 ], eax + mov [xCX + CIDETCPUCTX.dr7 + 4], eax + mov [xCX + CIDETCPUCTX.tr], ax + mov [xCX + CIDETCPUCTX.ldtr], ax + %endif +%endif + dec xAX + mov [xCX + CIDETCPUCTX.uErr], xAX +%ifdef RT_ARCH_X86 + mov [xCX + CIDETCPUCTX.uErr + 4], eax +%endif + mov [xCX + CIDETCPUCTX.uXcpt], eax + + ; + ; Restore the other state (pointer in xDX). + ; +NAME(CidetAppSaveAndRestoreCtx_Restore): + + ; Restore ES, FS, and GS. + mov es, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_ES * 2] + mov fs, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_FS * 2] +%ifndef CIDET_LEAVE_GS_ALONE + mov gs, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_GS * 2] +%endif + + ; Restore most GPRs (except xCX, xAX and xSP). + mov xCX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8] + mov xBX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8] + mov xBP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8] + mov xSI, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8] + mov xDI, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8] +%ifdef RT_ARCH_AMD64 + mov r8, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8] + mov r9, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8] + mov r10, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8] + mov r11, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8] + mov r12, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8] + mov r13, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8] + mov r14, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8] + mov r15, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8] +%endif + +%ifdef RT_ARCH_AMD64 + ; Create an iret frame which restores SS:RSP, RFLAGS, and CS:RIP. + movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2] + push xAX + push qword [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8] + push qword [xDX + CIDETCPUCTX.rfl] + movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2] + push xAX + push qword [xDX + CIDETCPUCTX.rip] + + ; Restore DS, xAX and xDX then do the iret. + mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2] + mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8] + mov xDX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8] + iretq +%else + ; In 32-bit mode iret doesn't restore CS:ESP for us, so we have to + ; make a choice whether the SS:ESP is more important than EFLAGS. + cmp byte [xDX + CIDETCPUCTX.fTrickyStack], 0 + jne .tricky_stack + + mov ss, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2] + mov xSP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8] + + push dword [xDX + CIDETCPUCTX.rfl] ; iret frame + movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2] ; iret frame + push xAX ; iret frame + push dword [xDX + CIDETCPUCTX.rip] ; iret frame + + mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8] + mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2] + mov xDX, [cs:xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8] + iretd + +.tricky_stack: + mov xAX, [xDX + CIDETCPUCTX.rip] + mov [g_uTargetEip], xAX + mov ax, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2] + mov [g_uTargetCs], ax + push dword [xDX + CIDETCPUCTX.rfl] + popfd + mov ss, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2] + mov xSP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8] + mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8] + mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2] + mov xDX, [cs:xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8] + jmp far [cs:g_uTargetEip] +%endif +ENDPROC CidetAppSaveAndRestoreCtx + + +;; +; C callable version of CidetAppSaveAndRestoreCtx more or less. +; +; @param pSaveCtx x86:esp+4 gcc:rdi msc:rcx +; @param pRestoreCtx x86:esp+8 gcc:rsi msc:rdx +BEGINPROC CidetAppExecute +%ifdef RT_ARCH_X86 + mov ecx, [esp + 4] + mov edx, [esp + 8] +%elifdef ASM_CALL64_GCC + mov rcx, rdi + mov rdx, rsi +%elifndef ASM_CALL64_MSC + %error "unsupport arch." +%endif + mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2], ds + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8], xDX + mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8], xCX + jmp NAME(CidetAppSaveAndRestoreCtx) +ENDPROC CidetAppExecute + + +;; +; C callable restore function. +; +; @param pRestoreCtx x86:esp+4 gcc:rdi msc:rcx +BEGINPROC CidetAppRestoreCtx +%ifdef RT_ARCH_X86 + mov edx, [esp + 4] +%elifdef ASM_CALL64_GCC + mov rdx, rdi +%elifdef ASM_CALL64_MSC + mov rdx, rcx +%else + %error "unsupport arch." +%endif + mov ds, [cs:xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2] + jmp NAME(CidetAppSaveAndRestoreCtx_Restore) +ENDPROC CidetAppRestoreCtx + diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp new file mode 100644 index 00000000..ff350861 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp @@ -0,0 +1,2368 @@ +/* $Id: cidet-core.cpp $ */ +/** @file + * CPU Instruction Decoding & Execution Tests - Simple Instructions. + */ + +/* + * 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 + */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define CIDET_INSTR_TEST_OP_FLAG(a_pInstr, a_fFlag) \ + ( ((a_pInstr)->afOperands[0] & (a_fFlag)) \ + || ((a_pInstr)->afOperands[1] & (a_fFlag)) \ + || ( (a_pInstr)->cOperands > 2 \ + && ( ((a_pInstr)->afOperands[2] & (a_fFlag)) \ + || ((a_pInstr)->afOperands[3] & (a_fFlag)) ) ) ) + +#define CIDET_INSTR_TEST_OP_MASK_VALUE(a_pInstr, a_fMask, a_fValue) \ + ( ((a_pInstr)->afOperands[0] & (a_fMask)) == (a_fValue) \ + || ((a_pInstr)->afOperands[1] & (a_fMask)) == (a_fValue) \ + || ( (a_pInstr)->cOperands > 2 \ + && ( ((a_pInstr)->afOperands[2] & (a_fMask)) == (a_fValue) \ + || ((a_pInstr)->afOperands[3] & (a_fMask)) == (a_fValue) ) ) ) + +/** @def CIDET_DPRINTF + * Debug printf. */ +#if 1 //def DEBUG_bird +# define CIDET_DPRINTF(a) do { RTPrintf a; } while (0) +# define CIDET_DPRINTF_ENABLED +#else +# define CIDET_DPRINTF(a) do { } while (0) +#endif + +/** @def CIDET_DEBUG_DISAS + * Enables instruction disassembly. */ +#if defined(DOXYGEN_RUNNING) +# define CIDET_DEBUG_DISAS 1 +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "cidet.h" + +#include <iprt/assert.h> +#include <iprt/rand.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/errcore.h> +#if defined(CIDET_DPRINTF_ENABLED) || defined(CIDET_DEBUG_DISAS) +# include <VBox/dis.h> +# include <iprt/stream.h> +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** For translating CIDET_OF_Z_XXX values (after shifting). */ +uint16_t const g_acbCidetOfSizes[] = +{ + /* [CIDET_OF_Z_NONE] = */ 0, + /* [CIDET_OF_Z_BYTE] = */ 1, + /* [CIDET_OF_Z_WORD] = */ 2, + /* [CIDET_OF_Z_DWORD] = */ 4, + /* [CIDET_OF_Z_QWORD] = */ 8, + /* [CIDET_OF_Z_TBYTE] = */ 10, + /* [CIDET_OF_Z_OWORD] = */ 16, + /* [CIDET_OF_Z_YWORD] = */ 32, + /* [CIDET_OF_Z_ZWORD] = */ 64, + /* [CIDET_OF_Z_VAR_WDQ] = */ UINT16_MAX, + /* [0xa] = */ 0, + /* [0xb] = */ 0, + /* [0xc] = */ 0, + /* [0xd] = */ 0, + /* [0xe] = */ 0, + /* [CIDET_OF_Z_SPECIAL] = */ UINT16_MAX - 1, +}; + + +/** Converts operand sizes in bytes to 64-bit masks. */ +static const uint64_t g_au64ByteSizeToMask[] = +{ + UINT64_C(0x0000000000000000), + UINT64_C(0x00000000000000ff), + UINT64_C(0x000000000000ffff), + UINT64_C(0x0000000000ffffff), + UINT64_C(0x00000000ffffffff), + UINT64_C(0x000000ffffffffff), + UINT64_C(0x0000ffffffffffff), + UINT64_C(0x00ffffffffffffff), + UINT64_C(0xffffffffffffffff), +}; + +/** Converts operand sizes in bytes to 64-bit signed max values. */ +static const int64_t g_ai64ByteSizeToMax[] = +{ + INT64_C(0x0000000000000000), + INT64_C(0x000000000000007f), + INT64_C(0x0000000000007fff), + INT64_C(0x00000000007fffff), + INT64_C(0x000000007fffffff), + INT64_C(0x0000007fffffffff), + INT64_C(0x00007fffffffffff), + INT64_C(0x007fffffffffffff), + INT64_C(0x7fffffffffffffff), +}; + + +bool CidetInstrHasMrmMemOperand(PCCIDETINSTR pInstr) +{ + return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_M); +} + + +bool CidetInstrHasMrmRegOperand(PCCIDETINSTR pInstr) +{ + return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_R); +} + + +bool CidetInstrRespondsToOperandSizePrefixes(PCCIDETINSTR pInstr) +{ + return CIDET_INSTR_TEST_OP_MASK_VALUE(pInstr, CIDET_OF_Z_MASK, CIDET_OF_Z_VAR_WDQ); +} + + + + +int CidetCoreInit(PCIDETCORE pThis, RTRAND hRand) +{ + AssertPtr(pThis); + AssertPtr(hRand); + + RT_ZERO(*pThis); + pThis->u32Magic = CIDETCORE_MAGIC; + pThis->hRand = hRand; + return VINF_SUCCESS; +} + + +void CidetCoreDelete(PCIDETCORE pThis) +{ + AssertPtr(pThis); Assert(pThis->u32Magic == CIDETCORE_MAGIC); + + RTRandAdvDestroy(pThis->hRand); + RT_ZERO(*pThis); +} + + +/** + * Report a test failure via CIDET::pfnFailure + * + * @returns false + * @param pThis Pointer to the core structure. + * @param pszFormat Format string containing failure details. + * @param va Arguments referenced in @a pszFormat. + */ +int CidetCoreSetErrorV(PCIDETCORE pThis, const char *pszFormat, va_list va) +{ + pThis->pfnFailure(pThis, pszFormat, va); + return false; +} + + +/** + * Report a test failure via CIDET::pfnFailure + * + * @returns false + * @param pThis Pointer to the core structure. + * @param pszFormat Format string containing failure details. + * @param ... Arguments referenced in @a pszFormat. + */ +bool CidetCoreSetError(PCIDETCORE pThis, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + CidetCoreSetErrorV(pThis, pszFormat, va); + va_end(va); + return false; +} + + +/** + * Get a signed random number, with a given number of significant bytes. + * + * @returns Random number. + * @param pThis Pointer to the core structure. + * @param cbSignificant The number of significant bytes. + */ +int64_t CidetCoreGetRandS64(PCIDETCORE pThis, uint8_t cbSignificant) +{ + int64_t iVal = RTRandAdvS64(pThis->hRand); + switch (cbSignificant) + { + case 1: return (int8_t)iVal; + case 2: return (int16_t)iVal; + case 4: return (int32_t)iVal; + case 8: return iVal; + default: + AssertReleaseFailed(); + return iVal; + } +} + + +/** + * Get an unsigned random number, with a given number of significant bytes. + * + * @returns Random number. + * @param pThis Pointer to the core structure. + * @param cbSignificant The number of significant bytes. + */ +uint64_t CidetCoreGetRandU64(PCIDETCORE pThis, uint8_t cbSignificant) +{ + Assert(cbSignificant == 1 || cbSignificant == 2 || cbSignificant == 4 || cbSignificant == 8); + + uint64_t uVal = RTRandAdvU64(pThis->hRand); + uVal &= g_au64ByteSizeToMask[cbSignificant]; + + return uVal; +} + + + +void CidetCoreInitializeCtxTemplate(PCIDETCORE pThis) +{ + pThis->InTemplateCtx.rip = UINT64_MAX; + pThis->InTemplateCtx.rfl = X86_EFL_1 | X86_EFL_ID | X86_EFL_IF; + + unsigned i = RT_ELEMENTS(pThis->InTemplateCtx.aGRegs); + if (CIDETMODE_IS_LM(pThis->bMode)) + while (i-- > 0) + pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0x3fefcc00daba005d) + | ((uint64_t)i << 32) + | ((uint32_t)i << 8); + else + while (i-- > 0) + pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0xfada009b) + | ((uint32_t)i << 12) + | ((uint32_t)i << 8); + i = RT_ELEMENTS(pThis->InTemplateCtx.aSRegs); + while (i-- > 0) + pThis->InTemplateCtx.aSRegs[i] = 0; /* Front end sets these afterwards. */ + pThis->InTemplateCtx.cr2 = 0; +#ifndef CIDET_REDUCED_CTX + pThis->InTemplateCtx.tr = 0; + pThis->InTemplateCtx.ldtr = 0; + pThis->InTemplateCtx.cr0 = 0; + pThis->InTemplateCtx.cr3 = 0; + pThis->InTemplateCtx.cr4 = 0; + pThis->InTemplateCtx.cr8 = 0; +#endif + pThis->InTemplateCtx.fIgnoredRFlags = 0; + pThis->InTemplateCtx.uXcpt = UINT32_MAX; + pThis->InTemplateCtx.uErr = UINT64_MAX; + pThis->InTemplateCtx.fTrickyStack = false; +} + + +/** + * Sets the target mode. + * + * Caller must set up default selector values after calling this function. + * + * @returns VBox status code. + * @param pThis Pointer to the core structure. + * @param bMode The new mode. + */ +int CidetCoreSetTargetMode(PCIDETCORE pThis, uint8_t bMode) +{ + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertReturn(pThis->u32Magic == CIDETCORE_MAGIC, VERR_INVALID_HANDLE); + switch (bMode) + { + //case CIDETMODE_RM: + //case CIDETMODE_PE_16: + //case CIDETMODE_PE_32: + //case CIDETMODE_PE_V86: + //case CIDETMODE_PP_16: + case CIDETMODE_PP_32: + //case CIDETMODE_PP_V86: + //case CIDETMODE_PAE_16: + case CIDETMODE_PAE_32: + //case CIDETMODE_PAE_V86: + //case CIDETMODE_LM_S16: + //case CIDETMODE_LM_32: + case CIDETMODE_LM_64: + break; + default: + return VERR_NOT_IMPLEMENTED; + } + pThis->bMode = bMode; + CidetCoreInitializeCtxTemplate(pThis); + return VINF_SUCCESS; +} + + +bool CidetCoreIsEncodingCompatibleWithInstruction(PCIDETCORE pThis) +{ + RT_NOREF_PV(pThis); + return true; +} + + +/** + * Selects the next address size mode. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + */ +static bool cidetCoreSetupNextBaseEncoding_AddressSize(PCIDETCORE pThis) +{ + if (pThis->fAddrSizePrf) + { + /* + * Reset to default. + */ + pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode); + pThis->fAddrSizePrf = false; + } + else + { + /* + * The other addressing size. + */ + if (CIDETMODE_IS_64BIT(pThis->bMode)) + pThis->cbAddrMode = 4; + else if (CIDETMODE_IS_32BIT(pThis->bMode)) + pThis->cbAddrMode = 2; + else + { + AssertRelease(CIDETMODE_IS_16BIT(pThis->bMode)); + pThis->cbAddrMode = 2; + } + pThis->fAddrSizePrf = true; + } + return pThis->fAddrSizePrf; +} + + +/** + * Selects the first REG encoding. + * + * @param pThis The core state structure. + */ +static void cidetCoreSetupFirstBaseEncoding_MrmReg(PCIDETCORE pThis) +{ + pThis->aOperands[pThis->idxMrmRegOp].iReg = 0; + pThis->aOperands[pThis->idxMrmRegOp].fIsMem = false; + pThis->aOperands[pThis->idxMrmRegOp].fIsRipRelative = false; + pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false; + pThis->aOperands[pThis->idxMrmRegOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRegOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRegOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRegOp].uMemScale = 1; + pThis->aOperands[pThis->idxMrmRegOp].iEffSeg = UINT8_MAX; + pThis->bModRm &= ~X86_MODRM_REG_MASK; + pThis->fRexR = false; +} + + +/** + * Selects the next REG (ModR/M) encoding. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_MrmReg(PCIDETCORE pThis, uint8_t iReg) +{ + Assert(pThis->idxMrmRegOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRegOp].fIsMem); + Assert(iReg < 16); + + /* + * Clear the collision flags here because of the byte register kludge. + */ + pThis->fHasRegCollisionDirect = false; + pThis->fHasRegCollisionMemBase = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasRegCollisionMem = false; + + /* + * Clear the REX prefix and high byte register tracking too. ASSUMES MrmReg is after MrmRmMod. + */ + Assert(!pThis->fNoRexPrefixMrmRm); + Assert(!pThis->fHasHighByteRegInMrmRm); + pThis->fNoRexPrefixMrmReg = false; + pThis->fNoRexPrefix = false; + pThis->fHasHighByteRegInMrmReg = false; + pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false; + + /* + * Special kludge for ah, ch, dh, bh, spl, bpl, sil, and dil. + * Needs extra care in 64-bit mode and special collision detection code. + */ + CIDET_DPRINTF(("aOperands[%u].cb=%u fGpr=%u iReg=%d fRex=%d fRexW=%u fRexX=%u fRexB=%u fRexR=%d\n", + pThis->idxMrmRegOp, pThis->aOperands[pThis->idxMrmRegOp].cb, CIDET_OF_K_IS_GPR(pThis->fMrmRegOp), iReg, + pThis->fRex, pThis->fRexW, pThis->fRexX, pThis->fRexB, pThis->fRexR)); + if ( pThis->aOperands[pThis->idxMrmRegOp].cb == 1 + && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) + && iReg >= 3 + && ( iReg <= 6 + || (CIDETMODE_IS_64BIT(pThis->bMode) && iReg == 7 && !pThis->fRex)) ) + + { + if (!pThis->fRex && iReg >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix) + { + /* The AMD64 low variants: spl, bpl, sil and dil. */ + pThis->fRex = true; + pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP; + + /* Check for collisions. */ + if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands)) + { + Assert(!pThis->fHasHighByteRegInMrmRm); + if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem) + pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp) + && iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg; + else + { + Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX); + + pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg; + pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + } + } + else + { + /* Next register: ah, ch, dh and bh. */ + iReg++; + pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg; + pThis->bModRm &= ~X86_MODRM_REG_MASK; + pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT; + pThis->fRex = false; + pThis->fRexR = false; + pThis->fNoRexPrefixMrmReg = true; + pThis->fNoRexPrefix = true; + pThis->fHasHighByteRegInMrmReg = true; + pThis->fHasStackRegInMrmReg = false; + pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = true; + Assert(!pThis->fRexW); Assert(!pThis->fRexX); Assert(!pThis->fRexB); + + /* Check for collisions. */ + if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands)) + { + if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem) + pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp) + && ( ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1 + && iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg + && pThis->fHasHighByteRegInMrmRm) + || ( pThis->aOperands[pThis->idxMrmRmOp].cb > 1 + && iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iReg)); + else + { + Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX); + + pThis->fHasRegCollisionMemBase = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg; + pThis->fHasRegCollisionMemIndex = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + } + } + return true; + } + + Assert(!pThis->fRex || (iReg == 7 && CIDETMODE_IS_64BIT(pThis->bMode))); + pThis->fRex = false; + + /* + * Next register. + */ + iReg = (iReg + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) ? 15 : 7); + + pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg; + pThis->bModRm &= ~X86_MODRM_REG_MASK; + pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT; + pThis->fRexR = iReg >= 8; + pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + + /* + * Register collision detection. + */ + if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands)) + { + if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem) + pThis->fHasRegCollisionDirect = iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg + && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp); + else if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)) + { + Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX); + pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg; + pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + } + Assert(!pThis->fSib); + + return iReg != 0; +} + + +/** + * Selects the next MOD & R/M encoding, 16-bit addressing variant. + * + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg) +{ + if (CidetInstrHasMrmRegOperand(pThis->pCurInstr)) + { + pThis->aOperands[pThis->idxMrmRmOp].iReg = 0; + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT; + pThis->fRexB = false; + pThis->fRexX = false; + pThis->fHasMemoryOperand = false; + pThis->fHasRegCollisionDirect = iReg == 0 + && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp); + pThis->fHasRegCollisionMem = false; + pThis->fHasRegCollisionMemBase = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasStackRegInMrmRmBase = false; + } + else + { + Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr)); + pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true; + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->fRexB = false; + pThis->fRexX = false; + pThis->fHasMemoryOperand = true; + pThis->fHasRegCollisionDirect = false; + iReg -= pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + pThis->fHasStackRegInMrmRmBase = false; + } +} + + +/** + * Selects the next MOD & R/M encoding, 16-bit addressing variant. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg) +{ + AssertRelease(!pThis->fRexB); + AssertRelease(!pThis->fRexX); + uint8_t iRm = pThis->bModRm & X86_MODRM_RM_MASK; + uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK; + if (iMod == 3) + { + /* + * Register access mode. + */ + Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem); + Assert(!pThis->fHasMemoryOperand); + Assert(!pThis->fHasRegCollisionMem); + Assert(!pThis->fHasRegCollisionMemBase); + Assert(!pThis->fHasRegCollisionMemIndex); + if (iRm < 7) + { + iRm++; + pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm; + pThis->bModRm &= ~X86_MODRM_RM_MASK; + pThis->bModRm |= iRm; + pThis->fHasRegCollisionDirect = iRm == iReg + && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp); + pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRmOp); + return true; + } + + /* If no memory modes, we're done. */ + if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr)) + { + cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg); + return false; + } + + /* Next mode: 16-bit memory addressing without displacement. */ + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + iMod = 0; + } + else + { + /* + * Memory access mode. + */ + Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem); + Assert(pThis->fHasMemoryOperand); + if (iRm < 7) + { + iRm++; + switch (iRm) + { + case 1: + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI; + break; + case 2: + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI; + break; + case 3: + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI; + break; + case 4: + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI; + break; + case 5: + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI; + break; + case 6: + if (iMod == 0) + { + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 2; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + } + else + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + break; + case 7: + if (iMod == 0) + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + break; + default: AssertReleaseFailed(); + } + pThis->bModRm &= ~X86_MODRM_RM_MASK; + pThis->bModRm |= iRm; + if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)) + { + iReg -= pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg; + pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + return true; + } + + /* Last mode? */ + if (iMod >= 2) + { + cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg); + return false; + } + + /* Next memory addressing mode (if any). */ + iMod++; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp++; + } + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT; + pThis->fHasMemoryOperand = true; + pThis->fHasRegCollisionDirect = false; + pThis->fHasStackRegInMrmRmBase = false; + if (CIDET_OF_K_IS_GPR(pThis->fMrmRmOp)) + { + iReg -= pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX; + pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + return true; +} + + +/** + * Selects the first MOD & R/M encoding, 32-bit and 64-bit addressing variant. + * + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + * @param f64Bit Set if 64-bit, clear if 32-bit. + */ +static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit) +{ + RT_NOREF_PV(f64Bit); + if (CidetInstrHasMrmRegOperand(pThis->pCurInstr)) + { + pThis->aOperands[pThis->idxMrmRmOp].iReg = 0; + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT; + pThis->fRexB = false; + pThis->fRexX = false; + pThis->fHasMemoryOperand = false; + pThis->fHasRegCollisionDirect = iReg == 0 + && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp); + pThis->fHasRegCollisionMem = false; + pThis->fHasRegCollisionMemBase = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasStackRegInMrmRmBase = false; + } + else + { + Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr)); + pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true; + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false; + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->fRexB = false; + pThis->fRexX = false; + pThis->fHasMemoryOperand = true; + pThis->fHasRegCollisionDirect = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4 && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase; + pThis->fHasStackRegInMrmRmBase = false; + } +} + + +/** + * Selects the next MOD & R/M encoding, 32-bit and 64-bit addressing variant. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + * @param f64Bit Set if 64-bit, clear if 32-bit. + */ +static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit) +{ + AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode)); + AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode)); + uint8_t iRm = (pThis->bModRm & X86_MODRM_RM_MASK) + pThis->fRexB * 8; + uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK; + if (iMod == 3) + { + /* + * Register access mode. + */ + Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem); + Assert(!pThis->fHasMemoryOperand); + Assert(!pThis->fHasRegCollisionMem); + Assert(!pThis->fHasRegCollisionMemBase); + Assert(!pThis->fHasRegCollisionMemIndex); + + if (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX && !pThis->fNoRexPrefix) /* should be ignored. */ + { + pThis->fRexX = true; + return true; + } + + /* Reset the byte register kludges variables. */ + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false; + pThis->fHasHighByteRegInMrmRm = false; + pThis->fNoRexPrefixMrmRm = false; + pThis->fNoRexPrefix = pThis->fNoRexPrefixMrmReg; + + if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7)) + { + /* + * Byte register kludge. + */ + if ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1 + && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) + && iRm >= 3 + && ( iRm <= 6 + || (iRm == 7 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX) ) ) + { + if (!pThis->fRexX && iRm >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix) + { + /* The AMD64 low variants: spl, bpl, sil and dil. (Using fRexX here as REG covers fRex.) */ + pThis->fRexX = true; + pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) + && iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + } + else + { + /* Next register: ah, ch, dh and bh. */ + iRm++; + pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm; + pThis->bModRm &= ~X86_MODRM_RM_MASK; + pThis->bModRm |= iRm & X86_MODRM_RM_MASK; + pThis->fRexB = false; + pThis->fRexX = false; + if (!pThis->fRexR && !pThis->fRexW && !pThis->fRex) + { + pThis->fNoRexPrefixMrmRm = true; + pThis->fNoRexPrefix = true; + pThis->fHasHighByteRegInMrmRm = true; + pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = true; + pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) + && iRm - 4 == iReg - pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasStackRegInMrmRmBase = false; + + } + else + { + /* Can't do the high stuff, so do the spl, bpl, sil and dil variation instead. + Note! We don't set the RexX yet since the base register or operand width holds it down. */ + pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) + && iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + } + } + } + /* + * Normal register. + */ + else + { + iRm++; + pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm; + pThis->bModRm &= ~X86_MODRM_RM_MASK; + pThis->bModRm |= iRm & X86_MODRM_RM_MASK; + pThis->fRexB = iRm >= 8; + pThis->fRexX = false; + pThis->fHasRegCollisionDirect = iRm == iReg && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp); + pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + } + return true; + } + + /* If no memory modes, we're done. */ + if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr)) + { + cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit); + return false; + } + + /* Next mode: 32-bit/64-bit memory addressing without displacement. */ + pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + iMod = 0; + } + else + { + /* + * Memory access mode. + */ + Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem); + Assert(pThis->fHasMemoryOperand); + Assert(!pThis->fHasStackRegInMrmRmBase); + if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7)) + { + iRm++; + if (iRm == 12) + iRm++; /* Leave REX.B=1 to the next-sib-base function. */ + if (iRm == 4) + { + /* SIB */ + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = 0; + pThis->fSib = true; + pThis->bSib = 0; + } + else if ((iRm & 7) == 5 && iMod == 0) + { + /* Absolute or wrt rip addressing. */ + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = CIDETMODE_IS_64BIT(pThis->bMode); + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4; + } + else + { + if ((iRm & 7) == 6 && iMod == 0) + { + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false; + } + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iRm; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + } + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->bModRm &= ~X86_MODRM_RM_MASK; + pThis->bModRm |= iRm & X86_MODRM_RM_MASK; + pThis->fRexB = iRm >= 8; + pThis->fRexX = false; + if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)) + { + iReg -= pThis->fHasHighByteRegInMrmReg * 4; + pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg; + pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg; + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + } + return true; + } + + /* Last mode? */ + if (iMod >= 2) + { + cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit); + return false; + } + + /* Next memory addressing mode (if any). */ + iMod++; + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = iMod == 1 ? 1 : 4; + } + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK); + pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT; + pThis->fRexB = false; + pThis->fRexX = false; + pThis->fHasMemoryOperand = true; + pThis->fHasRegCollisionDirect = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4 + && CIDET_OF_K_IS_GPR(pThis->fMrmRmOp); + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase; + pThis->fHasStackRegInMrmRmBase = false; + return true; +} + + +/** + * Selects the next MOD & R/M encoding. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_MrmRmMod(PCIDETCORE pThis, uint8_t iReg) +{ + if (pThis->cbAddrMode == 2) + return cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(pThis, iReg); + if (pThis->cbAddrMode == 4) + return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, false); + if (pThis->cbAddrMode == 8) + return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, true); + AssertReleaseFailedReturn(false); +} + + + +/** + * Selects the next SIB base register (/ encoding). + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_SibBase(PCIDETCORE pThis, uint8_t iReg) +{ + AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode)); + + uint8_t iBase = (pThis->bSib & X86_SIB_BASE_MASK) + pThis->fRexB * 8; + iBase = (iBase + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7); + + if ((iBase & 7) == 5 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0) + { + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX; + } + else + { + if ((iBase & 7) == 6 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0) + pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0; + pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iBase; + } + pThis->bSib &= ~X86_SIB_BASE_MASK; + pThis->bSib |= iBase & X86_SIB_BASE_MASK; + pThis->fRexB = iBase >= 8; + pThis->fHasRegCollisionMemBase = pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg + == iReg - pThis->fHasHighByteRegInMrmReg * 4 + && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp); + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + pThis->fHasStackRegInMrmRmBase = iBase == X86_GREG_xSP; + + return iBase != 0; +} + + +/** + * Selects the next SIB index register (/ encoding). + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_SibIndex(PCIDETCORE pThis, uint8_t iReg) +{ + AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode)); + Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem); + + uint8_t iIndex = ((pThis->bSib >> X86_SIB_INDEX_SHIFT) & X86_SIB_INDEX_SMASK) + pThis->fRexX * 8; + iIndex = (iIndex + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7); + + if (iIndex == 4 && !pThis->fUsesVexIndexRegs) + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX; + else + pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = iIndex; + pThis->bSib &= ~X86_SIB_INDEX_MASK; + pThis->bSib |= (iIndex & X86_SIB_INDEX_SMASK) << X86_SIB_INDEX_SHIFT; + pThis->fRexX = iIndex >= 8; + pThis->fHasRegCollisionMemIndex = pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg + == iReg - pThis->fHasHighByteRegInMrmReg * 4 + && ( !pThis->fUsesVexIndexRegs + ? CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) : CIDET_OF_K_IS_VRX(pThis->fMrmRegOp) ); + pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex; + + return iIndex != 0; +} + + +/** + * Selects the next SIB scale. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + * @param iReg The value of MODRM.REG /w REX.R applied. + */ +static bool cidetCoreSetupNextBaseEncoding_SibScale(PCIDETCORE pThis, uint8_t iReg) +{ + RT_NOREF_PV(iReg); + switch ((pThis->bSib >> X86_SIB_SCALE_SHIFT) & X86_SIB_SCALE_SMASK) + { + case 0: + pThis->bSib |= 1 << X86_SIB_SCALE_SHIFT; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 2; + return true; + case 1: + pThis->bSib &= ~X86_SIB_SCALE_MASK; + pThis->bSib |= 2 << X86_SIB_SCALE_SHIFT; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 4; + return true; + case 2: + pThis->bSib |= 3 << X86_SIB_SCALE_SHIFT; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 8; + return true; + case 3: + pThis->bSib &= ~X86_SIB_SCALE_MASK; + pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1; + return false; + + default: AssertReleaseFailedReturn(false); + } +} + + +/** + * Selects the next segment prefix. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + */ +static bool cidetCoreSetupNextBaseEncoding_SegmentPrefix(PCIDETCORE pThis) +{ + if ( pThis->fHasMemoryOperand + && (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_MASK)) + { + switch (pThis->uSegPrf) + { + case X86_SREG_COUNT: + pThis->uSegPrf = X86_SREG_ES; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_ES) + return true; + RT_FALL_THRU(); + case X86_SREG_ES: + pThis->uSegPrf = X86_SREG_CS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_CS) + return true; + RT_FALL_THRU(); + case X86_SREG_CS: + pThis->uSegPrf = X86_SREG_SS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_SS) + return true; + RT_FALL_THRU(); + case X86_SREG_SS: + pThis->uSegPrf = X86_SREG_DS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_DS) + return true; + RT_FALL_THRU(); + case X86_SREG_DS: + pThis->uSegPrf = X86_SREG_FS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_FS) + return true; + RT_FALL_THRU(); + case X86_SREG_FS: + pThis->uSegPrf = X86_SREG_GS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_GS) + return true; + RT_FALL_THRU(); + case X86_SREG_GS: + break; + default: AssertReleaseFailedBreak(); + } + pThis->uSegPrf = X86_SREG_COUNT; + } + return false; +} + + +/** + * Updates the variable sized operands. + * + * @param pThis The core state structure. + */ +static void cidetCoreUpdateOperandSizes(PCIDETCORE pThis) +{ + uint8_t iOp = pThis->cOperands; + while (iOp-- > 0) + pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp); +} + + +/** + * Selects the next operand size. + * + * @returns @c true if done, @c false if the next wheel needs to be moved. + * @param pThis The core state structure. + */ +static bool cidetCoreSetupNextBaseEncoding_OperandSize(PCIDETCORE pThis) +{ + if (CidetInstrRespondsToOperandSizePrefixes(pThis->pCurInstr)) + { + if (CIDETMODE_IS_64BIT(pThis->bMode)) + { + switch (pThis->fOpSizePrf + pThis->fRexW * 2) + { + case 0: + pThis->fOpSizePrf = true; + cidetCoreUpdateOperandSizes(pThis); + return true; + case 1: + pThis->fOpSizePrf = false; + if (pThis->fNoRexPrefix) + break; + pThis->fRexW = true; + cidetCoreUpdateOperandSizes(pThis); + return true; + case 2: + pThis->fOpSizePrf = true; /* check that it's ignored. */ + cidetCoreUpdateOperandSizes(pThis); + return true; + default: AssertReleaseFailed(); + case 3: + break; + } + } + else + { + if (!pThis->fOpSizePrf) + { + pThis->fOpSizePrf = true; + cidetCoreUpdateOperandSizes(pThis); + return true; + } + } + pThis->fRexW = false; + pThis->fOpSizePrf = false; + cidetCoreUpdateOperandSizes(pThis); + } + return false; +} + + +bool CidetCoreSetupNextBaseEncoding(PCIDETCORE pThis) +{ + if (pThis->fUsesModRm) + { + /* + * The wheels are lined up as follows: + * 1. Address size prefix. + * 2. MODRM.MOD + * 3. MODRM.REG + REX.R + * 4. MODRM.R/M + REX.B + * 5. SIB - MODRM.R/M == 4 && MODRM.MOD != 3: + * 5a) SIB.BASE + REX.B + * 5b) SIB.INDEX + REX.X + * 5c) SIB.SCALE + * 6. Segment prefix overrides if applicable and supported (memory). + * 7. Operand size prefix and REX.W if applicable. + */ + if (cidetCoreSetupNextBaseEncoding_OperandSize(pThis)) + return true; + if (cidetCoreSetupNextBaseEncoding_SegmentPrefix(pThis)) + return true; + + /* The ModR/M register value for collision detection. */ + uint8_t iReg = ((pThis->bModRm >> X86_MODRM_REG_SHIFT) & X86_MODRM_REG_SMASK) + pThis->fRexR * 8; + + if (pThis->fSib) + { + AssertRelease(pThis->fHasMemoryOperand); + if (cidetCoreSetupNextBaseEncoding_SibScale(pThis, iReg)) + return true; + if (cidetCoreSetupNextBaseEncoding_SibIndex(pThis, iReg)) + return true; + if (cidetCoreSetupNextBaseEncoding_SibBase(pThis, iReg)) + return true; + Assert(pThis->bSib == 0); + pThis->fSib = false; + } + + if (cidetCoreSetupNextBaseEncoding_MrmRmMod(pThis, iReg)) + return true; + if (cidetCoreSetupNextBaseEncoding_MrmReg(pThis, iReg)) + return true; + if (cidetCoreSetupNextBaseEncoding_AddressSize(pThis)) + return true; + } + else + AssertFailedReturn(false); + return false; +} + + +bool CidetCoreSetupFirstBaseEncoding(PCIDETCORE pThis) +{ + /* + * Reset all the knobs and wheels. + */ + pThis->fSib = false; + pThis->uSegPrf = X86_SREG_COUNT; + pThis->fAddrSizePrf = false; + pThis->fOpSizePrf = false; + pThis->fRexW = false; + pThis->fRexR = false; + pThis->fRexX = false; + pThis->fRexB = false; + pThis->fRex = false; + pThis->bModRm = 0; + pThis->bSib = 0; + + /* Indicators. */ + pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode); + pThis->fHasMemoryOperand = false; + pThis->fHasRegCollisionMem = false; + pThis->fHasRegCollisionMemBase = false; + pThis->fHasRegCollisionMemIndex = false; + pThis->fHasStackRegInMrmRmBase = false; + + /* + * Now, drill down on the instruction encoding. + */ + if (pThis->pCurInstr->fFlags & CIDET_IF_MODRM) + { + Assert(pThis->fUsesModRm == true); + cidetCoreSetupFirstBaseEncoding_MrmReg(pThis); + if (pThis->cbAddrMode == 2) + cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, 0); + else if (pThis->cbAddrMode == 4) + cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, false); + else if (pThis->cbAddrMode == 8) + cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, true); + else + AssertReleaseFailedReturn(false); + } + else + AssertFailedReturn(false); + return true; +} + + +/** + * The next memory operand configuration. + * + * @returns true if new one to test, false if we've reached end already. + * @param pThis The core state structure. + */ +bool CidetCoreSetupNextMemoryOperandConfig(PCIDETCORE pThis) +{ + RT_NOREF_PV(pThis); + return false; +} + + +/** + * Sets up the first memory operand configuration and counts memory operands. + * + * @returns true on success, false if no data buffers configured or failure. + * @param pThis The core state structure. + */ +bool CidetCoreSetupFirstMemoryOperandConfig(PCIDETCORE pThis) +{ + pThis->cMemoryOperands = 0; + PCIDETBUF pDataBuf = &pThis->DataBuf; + uint8_t idxOp = pThis->cOperands; + while (idxOp-- > 0) + if (!pThis->aOperands[idxOp].fIsMem) + pThis->aOperands[idxOp].pDataBuf = NULL; + else + { + if (RT_UNLIKELY(!pThis->cDataBufConfigs)) + return false; + + pDataBuf->idxCfg = 0; + pDataBuf->pCfg = &pThis->paDataBufConfigs[0]; + pDataBuf->off = 0; + pDataBuf->cb = pThis->aOperands[idxOp].cb; + pDataBuf->cbSegLimit = UINT16_MAX; + pDataBuf->offSegBase = 0; + pDataBuf->fActive = false; + pDataBuf->idxOp = idxOp; + pDataBuf->fXcptAfterInstruction = false; + pDataBuf->enmExpectXcpt = kCidetExpectXcpt_None; + pThis->aOperands[idxOp].pDataBuf = pDataBuf; + pThis->cMemoryOperands++; + pDataBuf++; + } + + /** @todo implement more than one memory operand. */ + AssertReleaseReturn(pThis->cMemoryOperands <= 1, false); + return true; +} + + +/** + * The next code buffer configuration. + * + * @returns true if new one to test, false if we've reached end already. + * @param pThis The core state structure. + */ +bool CidetCoreSetupNextCodeBufferConfig(PCIDETCORE pThis) +{ + RT_NOREF_PV(pThis); + return false; +} + + +/** + * Sets up the first code buffer configuration. + * + * @returns true on success, false if no data buffers configured or failure. + * @param pThis The core state structure. + */ +bool CidetCoreSetupFirstCodeBufferConfig(PCIDETCORE pThis) +{ + Assert(pThis->cCodeBufConfigs > 0); + Assert(CIDETBUF_IS_CODE(pThis->paCodeBufConfigs[0].fFlags)); + pThis->CodeBuf.idxCfg = 0; + pThis->CodeBuf.pCfg = &pThis->paCodeBufConfigs[0]; + pThis->CodeBuf.off = 0; + pThis->CodeBuf.cb = 0x1000; + pThis->CodeBuf.cbSegLimit = UINT16_MAX; + pThis->CodeBuf.offSegBase = 0; + pThis->CodeBuf.fActive = true; + pThis->CodeBuf.idxOp = 7; + pThis->CodeBuf.fXcptAfterInstruction = false; + pThis->CodeBuf.enmExpectXcpt = kCidetExpectXcpt_None; + return true; +} + + +/** + * Gets the (encoded) size of the given operand in the current context. + * + * @returns Size in bytes. + * @param pThis The core state structure (for context). + * @param iOp The operand index. + */ +uint32_t CidetCoreGetOperandSize(PCIDETCORE pThis, uint8_t iOp) +{ + Assert(iOp < RT_ELEMENTS(pThis->aOperands)); + uint32_t cbOp = g_acbCidetOfSizes[(pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) >> CIDET_OF_Z_SHIFT]; + if (cbOp == UINT16_MAX) + { + Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_VAR_WDQ); + if (CIDETMODE_IS_64BIT(pThis->bMode)) + { + if (pThis->fRexW) + cbOp = 8; + else if (!pThis->fOpSizePrf) + cbOp = 4; + else + cbOp = 2; + } + else if (CIDETMODE_IS_32BIT(pThis->bMode)) + cbOp = !pThis->fOpSizePrf ? 4 : 2; + else + { + Assert(CIDETMODE_IS_16BIT(pThis->bMode)); + cbOp = !pThis->fOpSizePrf ? 2 : 4; + } + return cbOp; + } + + if (cbOp == UINT16_MAX - 1) + { + Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_SPECIAL); + AssertReleaseFailedReturn(0); + } + + if (cbOp) + { +#ifdef VBOX_STRICT + switch (cbOp) + { + case 1: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_BYTE); break; + case 2: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_WORD); break; + case 4: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_DWORD); break; + case 8: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_QWORD); break; + case 10: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_TBYTE); break; + case 16: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_OWORD); break; + case 32: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_YWORD); break; + case 64: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_ZWORD); break; + default: AssertFailed(); + } +#endif + return cbOp; + } + AssertReleaseFailedReturn(0); +} + + +bool CideCoreSetInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr) +{ + AssertReleaseMsgReturn(RT_VALID_PTR(pInstr), ("%p\n", pInstr), false); + + pThis->pCurInstr = pInstr; + + /* + * Extract info from the instruction descriptor. + */ + pThis->fUsesModRm = false; + pThis->fUsesVexIndexRegs = false; + pThis->idxMrmRegOp = 7; + pThis->idxMrmRmOp = 7; + pThis->fMrmRegOp = 0; + pThis->fMrmRmOp = 0; + pThis->fInstrFlags = pInstr->fFlags; + pThis->cOperands = pInstr->cOperands; + if (pInstr->fFlags & CIDET_IF_MODRM) + { + pThis->fUsesModRm = true; + for (uint8_t iOp = 0; iOp < pInstr->cOperands; iOp++) + if (pInstr->afOperands[iOp] & CIDET_OF_M_REG) + { + pThis->idxMrmRegOp = iOp; + pThis->fMrmRegOp = pInstr->afOperands[iOp]; + } + else if (pInstr->afOperands[iOp] & CIDET_OF_M_RM) + { + pThis->idxMrmRmOp = iOp; + pThis->fMrmRmOp = pInstr->afOperands[iOp]; + } + } + else + AssertFailedReturn(false); + + uint8_t iOp; + for (iOp = 0; iOp < pInstr->cOperands; iOp++) + { + pThis->aOperands[iOp].fFlags = pInstr->afOperands[iOp]; + pThis->aOperands[iOp].iReg = UINT8_MAX; + pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp); + pThis->aOperands[iOp].fIsImmediate = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_IMM; + pThis->aOperands[iOp].fIsMem = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_MEM; + pThis->aOperands[iOp].fIsRipRelative = false; + pThis->aOperands[iOp].cbMemDisp = 0; + pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[iOp].uMemScale = 1; + pThis->aOperands[iOp].iEffSeg = UINT8_MAX; + pThis->aOperands[iOp].offSeg = UINT64_MAX; + pThis->aOperands[iOp].uEffAddr = UINT64_MAX; + pThis->aOperands[iOp].uImmDispValue = UINT64_MAX; + pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX; + pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX; + pThis->aOperands[iOp].In.pv = NULL; + pThis->aOperands[iOp].Expected.pv = NULL; + pThis->aOperands[iOp].pDataBuf = NULL; + } + + for (; iOp < RT_ELEMENTS(pThis->aOperands); iOp++) + { + pThis->aOperands[iOp].fFlags = 0; + pThis->aOperands[iOp].iReg = UINT8_MAX; + pThis->aOperands[iOp].cb = 0; + pThis->aOperands[iOp].fIsImmediate = false; + pThis->aOperands[iOp].fIsMem = false; + pThis->aOperands[iOp].fIsRipRelative = false; + pThis->aOperands[iOp].cbMemDisp = 0; + pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX; + pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX; + pThis->aOperands[iOp].uMemScale = 1; + pThis->aOperands[iOp].iEffSeg = UINT8_MAX; + pThis->aOperands[iOp].offSeg = UINT64_MAX; + pThis->aOperands[iOp].uEffAddr = UINT64_MAX; + pThis->aOperands[iOp].uImmDispValue = UINT64_MAX; + pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX; + pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX; + pThis->aOperands[iOp].In.pv = NULL; + pThis->aOperands[iOp].Expected.pv = NULL; + pThis->aOperands[iOp].pDataBuf = NULL; + } + + /* + * Reset various things. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aiInOut); i++) + pThis->aiInOut[i] = 0; + + return true; +} + + +bool CidetCoreSetupInOut(PCIDETCORE pThis) +{ + /* + * Enumerate the operands. + */ + uint8_t *pbBuf = &pThis->abBuf[0]; + pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *); + + uint8_t idxOp = pThis->cOperands; + while (idxOp-- > 0) + { + if (pThis->aOperands[idxOp].fIsMem) + { + /* + * Memory operand. + */ + Assert(pThis->aOperands[idxOp].fIsMem); + + /* Set the In & Expected members to point to temporary buffer space. */ + pThis->aOperands[idxOp].Expected.pu8 = pbBuf; + pbBuf += pThis->aOperands[idxOp].cb; + pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *); + + pThis->aOperands[idxOp].In.pu8 = pbBuf; + pbBuf += pThis->aOperands[idxOp].cb; + pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *); + + /* Initialize the buffer we're gonna use. */ + pThis->aOperands[idxOp].iEffSeg = pThis->uSegPrf != X86_SREG_COUNT + ? pThis->uSegPrf + : !(pThis->aOperands[idxOp].fFlags & CIDET_OF_ALWAYS_SEG_ES) ? X86_SREG_DS + : X86_SREG_ES; + + PCIDETBUF pDataBuf = pThis->aOperands[idxOp].pDataBuf; + AssertReleaseReturn(pDataBuf, false); + Assert(pDataBuf->cb == pThis->aOperands[idxOp].cb); + Assert(pDataBuf->idxOp == idxOp); + if (!pThis->pfnReInitDataBuf(pThis, pDataBuf)) + { + pThis->cSkippedReInitDataBuf++; + return false; + } + pDataBuf->fActive = true; + + /* Calc buffer related operand members. */ + pThis->aOperands[idxOp].uEffAddr = pDataBuf->uEffBufAddr + pDataBuf->off; + uint64_t offSeg = pThis->aOperands[idxOp].uEffAddr - pDataBuf->uSegBase; + pThis->aOperands[idxOp].offSeg = offSeg; + AssertRelease(offSeg <= g_au64ByteSizeToMask[pThis->cbAddrMode]); + + /* + * Select register and displacement values for the buffer addressing (works on offSeg). + */ + uint8_t const iMemIndexReg = pThis->aOperands[idxOp].iMemIndexReg; + uint8_t const iMemBaseReg = pThis->aOperands[idxOp].iMemBaseReg; + if (pThis->aOperands[idxOp].fIsRipRelative) + { + /* rip relative. */ + pThis->aOperands[idxOp].uImmDispValue = offSeg - (pThis->InCtx.rip + pThis->cbInstr); + Assert(pThis->aOperands[idxOp].cbMemDisp == 4); + if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue > INT32_MAX + || (int64_t)pThis->aOperands[idxOp].uImmDispValue < INT32_MIN) + { + pThis->cSkippedDataBufWrtRip++; + return false; + } + } + else if (iMemBaseReg != UINT8_MAX) + { + if ( iMemBaseReg != iMemIndexReg + || pThis->fUsesVexIndexRegs) + { + /* [base] or [base + disp] or [base + index * scale] or [base + index * scale + disp] */ + if (pThis->aOperands[idxOp].cbMemDisp > 0) + { + pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp); + offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue; + } + + if (iMemIndexReg != UINT8_MAX) + { + pThis->aOperands[idxOp].uMemIndexRegValue = CidetCoreGetRandU64(pThis, pThis->cbAddrMode); + offSeg -= pThis->aOperands[idxOp].uMemIndexRegValue * pThis->aOperands[idxOp].uMemScale; + } + + pThis->aOperands[idxOp].uMemBaseRegValue = offSeg & g_au64ByteSizeToMask[pThis->cbAddrMode]; + } + else + { + /* base == index; [base + index * scale] or [base * (scale + 1)]. */ + uint8_t const uEffScale = pThis->aOperands[idxOp].uMemScale + 1; + if (pThis->aOperands[idxOp].cbMemDisp > 0) + { + pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp); + offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue; + offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode]; + uint8_t uRemainder = offSeg % uEffScale; + if (uRemainder != 0) + { + Assert(pThis->aOperands[idxOp].cbMemDisp < 8); + Assert( (int64_t)pThis->aOperands[idxOp].uImmDispValue + <= g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp]); + pThis->aOperands[idxOp].uImmDispValue = (int64_t)pThis->aOperands[idxOp].uImmDispValue + + uRemainder; + offSeg -= uRemainder; + if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue + > g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp]) + { + pThis->aOperands[idxOp].uImmDispValue -= uEffScale; + offSeg += uEffScale; + } + Assert(offSeg % uEffScale == 0); + } + } + else + { + offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode]; + if (offSeg % uEffScale != 0) + { + pThis->cSkippedSameBaseIndexRemainder++; + return false; + } + } + offSeg /= uEffScale; + pThis->aOperands[idxOp].uMemBaseRegValue = pThis->aOperands[idxOp].uMemIndexRegValue = offSeg; + } + } + else if (iMemIndexReg != UINT8_MAX) + { + /* [index * scale] or [index * scale + disp] */ + if (pThis->aOperands[idxOp].cbMemDisp > 0) + { + pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp); + offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue; + pThis->aOperands[idxOp].uImmDispValue += offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1); + offSeg &= ~(RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1); + } + else if (offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1)) + { + pThis->cSkippedOnlyIndexRemainder++; + return false; + } + + pThis->aOperands[idxOp].uMemIndexRegValue = offSeg / pThis->aOperands[idxOp].uMemScale; + Assert((offSeg % pThis->aOperands[idxOp].uMemScale) == 0); + AssertRelease(!pThis->fUsesVexIndexRegs); /** @todo implement VEX indexing */ + } + else + { + /* [disp] */ + Assert( pThis->aOperands[idxOp].cbMemDisp == 8 + || pThis->aOperands[idxOp].cbMemDisp == 4 + || pThis->aOperands[idxOp].cbMemDisp == 2 + || pThis->aOperands[idxOp].cbMemDisp == 1); + if ( pThis->aOperands[idxOp].cbMemDisp == 4 + ? (int64_t)offSeg != (int32_t)offSeg + : pThis->aOperands[idxOp].cbMemDisp == 2 + ? (int64_t)offSeg != (int16_t)offSeg + : pThis->aOperands[idxOp].cbMemDisp == 1 + ? (int64_t)offSeg != (int8_t)offSeg + : false /* 8 */) + { + pThis->cSkippedDirectAddressingOverflow++; + return false; + } + pThis->aOperands[idxOp].uImmDispValue = offSeg; + } + + /* + * Modify the input and expected output contexts with the base and + * index register values. To simplify verification and the work + * here, we update the uMemBaseRegValue and uMemIndexRegValue + * members to reflect the whole register. + */ + if (iMemBaseReg != UINT8_MAX) + { + if (pThis->cbAddrMode == 4) + { + pThis->aOperands[idxOp].uMemBaseRegValue &= UINT32_MAX; + pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffff00000000); + } + else if (pThis->cbAddrMode == 2) + { + pThis->aOperands[idxOp].uMemBaseRegValue &= UINT16_MAX; + pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffffffff0000); + } + pThis->InCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue; + pThis->ExpectedCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue; + } + + if (iMemIndexReg != UINT8_MAX) + { + if (pThis->cbAddrMode == 4) + { + pThis->aOperands[idxOp].uMemIndexRegValue &= UINT32_MAX; + pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffff00000000); + } + else if (pThis->cbAddrMode == 2) + { + pThis->aOperands[idxOp].uMemIndexRegValue &= UINT16_MAX; + pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffffffff0000); + } + pThis->InCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue; + pThis->ExpectedCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue; + } + } + else + { + /* + * Non-memory, so clear the memory related members. + */ + Assert(!pThis->aOperands[idxOp].fIsMem); + pThis->aOperands[idxOp].iEffSeg = UINT8_MAX; + pThis->aOperands[idxOp].offSeg = UINT64_MAX; + pThis->aOperands[idxOp].uEffAddr = UINT64_MAX; + pThis->aOperands[idxOp].pDataBuf = NULL; + + switch (pThis->aOperands[idxOp].fFlags & CIDET_OF_K_MASK) + { + case CIDET_OF_K_GPR: + if (!pThis->aOperands[idxOp].fIsHighByteRegister) + { + pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg]; + pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg]; + } + else + { + pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4]; + pThis->aOperands[idxOp].In.pu8++; + pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4]; + pThis->aOperands[idxOp].Expected.pu8++; + } + break; + + case CIDET_OF_K_IMM: + pThis->aOperands[idxOp].In.pv = NULL; + pThis->aOperands[idxOp].Expected.pv = NULL; + break; + + case CIDET_OF_K_SREG: + if (pThis->aOperands[idxOp].iReg < RT_ELEMENTS(pThis->InCtx.aSRegs)) + { + pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aSRegs[pThis->aOperands[idxOp].iReg]; + pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aSRegs[pThis->aOperands[idxOp].iReg]; + } + else + { + pThis->aOperands[idxOp].In.pv = NULL; + pThis->aOperands[idxOp].Expected.pv = NULL; + } + break; + + case CIDET_OF_K_CR: + case CIDET_OF_K_SSE: + case CIDET_OF_K_AVX: + case CIDET_OF_K_AVX512: + case CIDET_OF_K_FPU: + case CIDET_OF_K_MMX: + case CIDET_OF_K_AVXFUTURE: + case CIDET_OF_K_SPECIAL: + case CIDET_OF_K_TEST: + /** @todo Implement testing these registers. */ + case CIDET_OF_K_NONE: + default: + AssertReleaseFailedReturn(false); + } + } + } + AssertRelease((uintptr_t)pbBuf - (uintptr_t)&pThis->abBuf[0] <= sizeof(pThis->abBuf)); + + /* + * Call instruction specific setup function (for operand values and flags). + */ + int rc = pThis->pCurInstr->pfnSetupInOut(pThis, false /*fInvalid*/); + if (RT_FAILURE(rc)) + { + pThis->cSkippedSetupInOut++; + return false; + } + + /* + * Do the 2nd set of the memory operand preparations. + */ + if (pThis->fHasMemoryOperand) + { + idxOp = pThis->cOperands; + while (idxOp-- > 0) + if (pThis->aOperands[idxOp].fIsMem) + { + Assert(pThis->aOperands[idxOp].pDataBuf); + if (!pThis->pfnSetupDataBuf(pThis, pThis->aOperands[idxOp].pDataBuf, pThis->aOperands[idxOp].In.pv)) + { + pThis->cSkippedSetupDataBuf++; + return false; + } + + Assert( pThis->aOperands[idxOp].iMemBaseReg == UINT8_MAX + || pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemBaseReg] == pThis->aOperands[idxOp].uMemBaseRegValue); + Assert( pThis->aOperands[idxOp].iMemIndexReg == UINT8_MAX + || ( !pThis->fUsesVexIndexRegs + ? pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemIndexReg] + == pThis->aOperands[idxOp].uMemIndexRegValue + : false /** @todo VEX indexing */)); + } + } + + return true; +} + + +/** + * Figures the instruction length. + * + * This is a duplicate of CidetCoreAssemble() with the buffer updates removed. + * + * @returns true and pThis->cbInstr on success, false on failure. + * @param pThis The core state structure (for context). + */ +bool CidetCoreAssembleLength(PCIDETCORE pThis) +{ + uint8_t off = 0; + + /* + * Prefixes. + */ + if (1) + { + if (pThis->fAddrSizePrf) + off++; + if (pThis->fOpSizePrf) + off++; + } + else + { + /** @todo prefix list. */ + } + + /* + * Prefixes that must come right before the opcode. + */ + /** @todo VEX and EVEX. */ + if (pThis->fVex) + { + /** @todo VEX and EVEX. */ + } + else if (pThis->fEvex) + { + /** @todo VEX and EVEX. */ + } + else + { + if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex) + off++; + } + + /* + * The opcode. + */ + //uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode; + switch (pThis->pCurInstr->cbOpcode) + { + case 3: off++; RT_FALL_THRU(); + case 2: off++; RT_FALL_THRU(); + case 1: off++; + break; + default: + AssertReleaseFailedReturn(false); + } + + /* + * Mod R/M + */ + if (pThis->fUsesModRm) + { + off++; + if (pThis->fSib) + off++; + if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands)) + { + //uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue; + switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp) + { + case 0: break; + case 8: + case 7: + case 6: + case 5: + case 4: + case 3: + case 2: + case 1: + break; + default: AssertReleaseFailedReturn(false); + } + off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp; + } + } + + /* + * Immediates. + */ + uint8_t iOp = pThis->cOperands; + while (iOp-- > 0) + if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM) + { + //uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue; + switch (pThis->aOperands[iOp].cb) + { + case 8: + case 7: + case 6: + case 5: + case 4: + case 3: + case 2: + case 1: + break; + default: AssertReleaseFailedReturn(false); + } + off += pThis->aOperands[iOp].cb; + } + + pThis->cbInstr = off; + return true; +} + + +/** + * Assembles the instruction. + * + * This is a duplicate of CidetCoreAssembleLength() with buffer writes. + * + * @returns true and pThis->cbInstr and pThis->abInstr on success, false on + * failure. + * @param pThis The core state structure (for context). + */ +bool CidetCoreAssemble(PCIDETCORE pThis) +{ + uint8_t off = 0; + + /* + * Prefixes. + */ + if (1) + { + if (pThis->fAddrSizePrf) + pThis->abInstr[off++] = 0x67; + if (pThis->fOpSizePrf) + pThis->abInstr[off++] = 0x66; + } + else + { + /** @todo prefix list. */ + } + + /* + * Prefixes that must come right before the opcode. + */ + /** @todo VEX and EVEX. */ + if (pThis->fVex) + { + /** @todo VEX and EVEX. */ + } + else if (pThis->fEvex) + { + /** @todo VEX and EVEX. */ + } + else + { + if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex) + pThis->abInstr[off++] = 0x40 | (pThis->fRexB * 1) | (pThis->fRexX * 2) | (pThis->fRexR * 4) | (pThis->fRexW * 8); + } + + /* + * The opcode. + */ + uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode; + switch (pThis->pCurInstr->cbOpcode) + { + case 3: pThis->abInstr[off++] = *pbOpcode++; RT_FALL_THRU(); + case 2: pThis->abInstr[off++] = *pbOpcode++; RT_FALL_THRU(); + case 1: pThis->abInstr[off++] = *pbOpcode++; + break; + default: + AssertReleaseFailedReturn(false); + } + + /* + * Mod R/M + */ + if (pThis->fUsesModRm) + { + pThis->abInstr[off++] = pThis->bModRm; + if (pThis->fSib) + pThis->abInstr[off++] = pThis->bSib; + if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands)) + { + uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue; + switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp) + { + case 0: break; + case 8: pThis->abInstr[off + 3] = (uDispValue >> 56) & UINT8_C(0xff); RT_FALL_THRU(); + case 7: pThis->abInstr[off + 3] = (uDispValue >> 48) & UINT8_C(0xff); RT_FALL_THRU(); + case 6: pThis->abInstr[off + 3] = (uDispValue >> 40) & UINT8_C(0xff); RT_FALL_THRU(); + case 5: pThis->abInstr[off + 3] = (uDispValue >> 32) & UINT8_C(0xff); RT_FALL_THRU(); + case 4: pThis->abInstr[off + 3] = (uDispValue >> 24) & UINT8_C(0xff); RT_FALL_THRU(); + case 3: pThis->abInstr[off + 2] = (uDispValue >> 16) & UINT8_C(0xff); RT_FALL_THRU(); + case 2: pThis->abInstr[off + 1] = (uDispValue >> 8) & UINT8_C(0xff); RT_FALL_THRU(); + case 1: pThis->abInstr[off] = uDispValue & UINT8_C(0xff); + break; + default: AssertReleaseFailedReturn(false); + } + off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp; + } + } + + /* + * Immediates. + */ + uint8_t iOp = pThis->cOperands; + while (iOp-- > 0) + if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM) + { + uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue; + switch (pThis->aOperands[iOp].cb) + { + case 8: pThis->abInstr[off + 3] = (uImmValue >> 56) & UINT8_C(0xff); RT_FALL_THRU(); + case 7: pThis->abInstr[off + 3] = (uImmValue >> 48) & UINT8_C(0xff); RT_FALL_THRU(); + case 6: pThis->abInstr[off + 3] = (uImmValue >> 40) & UINT8_C(0xff); RT_FALL_THRU(); + case 5: pThis->abInstr[off + 3] = (uImmValue >> 32) & UINT8_C(0xff); RT_FALL_THRU(); + case 4: pThis->abInstr[off + 3] = (uImmValue >> 24) & UINT8_C(0xff); RT_FALL_THRU(); + case 3: pThis->abInstr[off + 2] = (uImmValue >> 16) & UINT8_C(0xff); RT_FALL_THRU(); + case 2: pThis->abInstr[off + 1] = (uImmValue >> 8) & UINT8_C(0xff); RT_FALL_THRU(); + case 1: pThis->abInstr[off] = uImmValue & UINT8_C(0xff); + break; + default: AssertReleaseFailedReturn(false); + } + off += pThis->aOperands[iOp].cb; + } + + pThis->cbInstr = off; + return true; +} + + +bool CidetCoreReInitCodeBuf(PCIDETCORE pThis) +{ + /* + * Re-initialize the buffer. Requires instruction length and positioning. + */ + if (CidetCoreAssembleLength(pThis)) + { + pThis->CodeBuf.cb = pThis->cbInstr; + pThis->CodeBuf.off = CIDET_CODE_BUF_SIZE - PAGE_SIZE - pThis->cbInstr; + if (pThis->pfnReInitCodeBuf(pThis, &pThis->CodeBuf)) + { + pThis->CodeBuf.fActive = true; + + /* + * Update the RIP and CS values in the input and expected contexts. + */ + pThis->InCtx.rip = pThis->CodeBuf.uEffBufAddr + pThis->CodeBuf.offActive - pThis->CodeBuf.uSegBase; + pThis->ExpectedCtx.rip = pThis->InCtx.rip + pThis->cbInstr; /** @todo account for expected traps. */ + if (pThis->CodeBuf.uSeg != UINT32_MAX) + { + pThis->InCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg; + pThis->ExpectedCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg; + } + return true; + } + else + pThis->cSkippedReInitCodeBuf++; + } + else + pThis->cSkippedAssemble++; + return false; +} + + +#ifdef CIDET_DEBUG_DISAS +/** + * @callback_method_impl{FNDISREADBYTES} + */ +static DECLCALLBACK(int) cidetCoreDisReadBytes(PDISSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead) +{ + PCIDETCORE pThis = (PCIDETCORE)pDis->pvUser; + memcpy(&pDis->abInstr[offInstr], &pThis->abInstr[offInstr], cbMaxRead); + pDis->cbCachedInstr = offInstr + cbMaxRead; + return VINF_SUCCESS; +} +#endif + + +bool CidetCoreSetupCodeBuf(PCIDETCORE pThis, unsigned iSubTest) +{ + if (CidetCoreAssemble(pThis)) + { + //CIDET_DPRINTF(("%04u: %.*Rhxs\n", i, pThis->cbInstr, pThis->abInstr)); +#ifdef CIDET_DEBUG_DISAS + DISCPUSTATE Dis; + char szInstr[80] = {0}; + uint32_t cbInstr; + int rcDis = DISInstrToStrEx(pThis->InCtx.rip, + CIDETMODE_IS_64BIT(pThis->bMode) ? DISCPUMODE_64BIT + : CIDETMODE_IS_32BIT(pThis->bMode) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT, + cidetCoreDisReadBytes, + pThis, + DISOPTYPE_ALL, + &Dis, + &cbInstr, + szInstr, sizeof(szInstr)); + CIDET_DPRINTF(("%04u: %s", iSubTest, szInstr)); + Assert(cbInstr == pThis->cbInstr); +#else + RT_NOREF_PV(iSubTest); +#endif + if (pThis->pfnSetupCodeBuf(pThis, &pThis->CodeBuf, pThis->abInstr)) + { + return true; + } + pThis->cSkippedSetupCodeBuf++; + } + else + pThis->cSkippedAssemble++; + return false; +} + + +/** + * Compares the output with the output expectations. + * + * @returns true if ok, false if not (calls pfnFailure too). + * @param pThis The core state structure. + */ +bool CidetCoreCheckResults(PCIDETCORE pThis) +{ + if (memcmp(&pThis->ActualCtx, &pThis->ExpectedCtx, CIDETCPUCTX_COMPARE_SIZE) == 0) + return true; + + unsigned cDiffs = 0; +#define IF_FIELD_DIFFERS_SET_ERROR(a_Field, a_Fmt) \ + if (pThis->ActualCtx.a_Field != pThis->ExpectedCtx.a_Field) \ + { \ + CidetCoreSetError(pThis, #a_Field " differs: got %#llx expected %#llx", \ + pThis->ActualCtx.a_Field, pThis->ExpectedCtx.a_Field); \ + cDiffs++; \ + } else do { } while (0) + + IF_FIELD_DIFFERS_SET_ERROR(rip, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(rfl, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xAX], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBX], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xCX], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDX], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSP], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBP], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSI], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDI], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x8], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x10], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x11], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x12], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x13], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x14], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x15], "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_CS], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_SS], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_DS], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_ES], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_FS], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_GS], "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(uXcpt, "%#04x"); + IF_FIELD_DIFFERS_SET_ERROR(uErr, "%#04llx"); + IF_FIELD_DIFFERS_SET_ERROR(cr2, "%#010llx"); +#ifndef CIDET_REDUCED_CTX + IF_FIELD_DIFFERS_SET_ERROR(tr, "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(ldtr, "%#06x"); + IF_FIELD_DIFFERS_SET_ERROR(cr0, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(cr3, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(cr4, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(cr8, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr0, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr1, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr2, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr3, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr6, "%#010llx"); + IF_FIELD_DIFFERS_SET_ERROR(dr7, "%#010llx"); +#endif + +AssertMsgFailed(("cDiffs=%d\n", cDiffs)); + Assert(cDiffs > 0); + return cDiffs == 0; +} + + +bool CidetCoreTest_Basic(PCIDETCORE pThis) +{ + /* + * Iterate all encodings. + */ + if (!CidetCoreSetupFirstBaseEncoding(pThis)) + return CidetCoreSetError(pThis, "CidetCoreSetupFirstBaseEncoding failed"); + unsigned cExecuted = 0; + unsigned cSkipped = 0; + do + { + /* + * Iterate data buffer configurations (one iteration if none). + */ + if (CidetCoreSetupFirstMemoryOperandConfig(pThis)) + { + do + { + /* + * Iterate code buffer configurations. + */ + if (!CidetCoreSetupFirstCodeBufferConfig(pThis)) + return CidetCoreSetError(pThis, "CidetCoreSetupFirstMemoryOperandConfig failed"); + do + { + /* + * Set up inputs and expected outputs, then emit the test code. + */ + pThis->InCtx = pThis->InTemplateCtx; + pThis->InCtx.fTrickyStack = pThis->fHasStackRegInMrmRmBase || pThis->fHasStackRegInMrmReg; + pThis->ExpectedCtx = pThis->InCtx; + if ( CidetCoreReInitCodeBuf(pThis) + && CidetCoreSetupInOut(pThis) + && CidetCoreSetupCodeBuf(pThis, cSkipped + cExecuted) + ) + { + if (pThis->pfnExecute(pThis)) + { + cExecuted++; + + /* + * Check the result against our expectations. + */ + CidetCoreCheckResults(pThis); + /** @todo check result. */ + + } + else + cSkipped++; + } + else + cSkipped++; + } while (CidetCoreSetupNextCodeBufferConfig(pThis)); + } while (CidetCoreSetupNextMemoryOperandConfig(pThis)); + } + else + cSkipped++; + } while (CidetCoreSetupNextBaseEncoding(pThis)); + + CIDET_DPRINTF(("CidetCoreTest_Basic: cExecuted=%u cSkipped=%u\n" + " cSkippedSetupInOut =%u\n" + " cSkippedReInitDataBuf =%u\n" + " cSkippedSetupDataBuf =%u\n" + " cSkippedDataBufWrtRip =%u\n" + " cSkippedAssemble =%u\n" + " cSkippedReInitCodeBuf =%u\n" + " cSkippedSetupCodeBuf =%u\n" + " cSkippedSameBaseIndexRemainder =%u\n" + " cSkippedOnlyIndexRemainder =%u\n" + " cSkippedDirectAddressingOverflow =%u\n" + , + cExecuted, cSkipped, + pThis->cSkippedSetupInOut, + pThis->cSkippedReInitDataBuf, + pThis->cSkippedSetupDataBuf, + pThis->cSkippedDataBufWrtRip, + pThis->cSkippedAssemble, + pThis->cSkippedReInitCodeBuf, + pThis->cSkippedSetupCodeBuf, + pThis->cSkippedSameBaseIndexRemainder, + pThis->cSkippedOnlyIndexRemainder, + pThis->cSkippedDirectAddressingOverflow + )); + + return true; +} + + +bool CidetCoreTestInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr) +{ + AssertReleaseMsgReturn(RT_VALID_PTR(pThis), ("%p\n", pThis), false); + AssertReleaseReturn(pThis->u32Magic == CIDETCORE_MAGIC, false); + AssertReleaseReturn(pThis->cCodeBufConfigs > 0, false); + + if (!CideCoreSetInstruction(pThis, pInstr)) + return CidetCoreSetError(pThis, "CideCoreSetInstruction failed"); + + bool fResult = CidetCoreTest_Basic(pThis); + + return fResult; +} + diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp new file mode 100644 index 00000000..4cbbd5db --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp @@ -0,0 +1,297 @@ +/* $Id: cidet-instr-1.cpp $ */ +/** @file + * CPU Instruction Decoding & Execution Tests - First bunch of instructions. + */ + +/* + * 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 <VBox/err.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* + * Shorter defines for the EFLAGS to save table space. + */ +#undef CF +#undef PF +#undef AF +#undef ZF +#undef SF +#undef OF + +#define CF X86_EFL_CF +#define PF X86_EFL_PF +#define AF X86_EFL_AF +#define ZF X86_EFL_ZF +#define SF X86_EFL_SF +#define OF X86_EFL_OF + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct CIDET2IN1OUTWITHFLAGSU8ENTRY +{ + uint8_t uIn1; + uint8_t uIn2; + uint16_t fEFlagsIn; + uint8_t uOut; + uint16_t fEFlagsOut; +} CIDET2IN1OUTWITHFLAGSU8ENTRY; +typedef CIDET2IN1OUTWITHFLAGSU8ENTRY const *PCCIDET2IN1OUTWITHFLAGSU8ENTRY; + +typedef struct CIDET2IN1OUTWITHFLAGSU16ENTRY +{ + uint16_t uIn1; + uint16_t uIn2; + uint16_t fEFlagsIn; + uint16_t uOut; + uint16_t fEFlagsOut; +} CIDET2IN1OUTWITHFLAGSU16ENTRY; +typedef CIDET2IN1OUTWITHFLAGSU16ENTRY const *PCCIDET2IN1OUTWITHFLAGSU16ENTRY; + +typedef struct CIDET2IN1OUTWITHFLAGSU32ENTRY +{ + uint32_t uIn1; + uint32_t uIn2; + uint16_t fEFlagsIn; + uint32_t uOut; + uint16_t fEFlagsOut; +} CIDET2IN1OUTWITHFLAGSU32ENTRY; +typedef CIDET2IN1OUTWITHFLAGSU32ENTRY const *PCCIDET2IN1OUTWITHFLAGSU32ENTRY; + +typedef struct CIDET2IN1OUTWITHFLAGSU64ENTRY +{ + uint64_t uIn1; + uint64_t uIn2; + uint16_t fEFlagsIn; + uint64_t uOut; + uint16_t fEFlagsOut; +} CIDET2IN1OUTWITHFLAGSU64ENTRY; +typedef CIDET2IN1OUTWITHFLAGSU64ENTRY const *PCCIDET2IN1OUTWITHFLAGSU64ENTRY; + +typedef struct CIDET2IN1OUTWITHFLAGS +{ + PCCIDET2IN1OUTWITHFLAGSU8ENTRY pa8Entries; + PCCIDET2IN1OUTWITHFLAGSU16ENTRY pa16Entries; + PCCIDET2IN1OUTWITHFLAGSU32ENTRY pa32Entries; + PCCIDET2IN1OUTWITHFLAGSU64ENTRY pa64Entries; + uint16_t c8Entries; + uint16_t c16Entries; + uint16_t c32Entries; + uint16_t c64Entries; + uint32_t fRelevantEFlags; +} CIDET2IN1OUTWITHFLAGS; + +#define CIDET2IN1OUTWITHFLAGS_INITIALIZER(a_fRelevantEFlags) \ + { \ + &s_a8Results[0], &s_a16Results[0], &s_a32Results[0], &s_a64Results[0], \ + RT_ELEMENTS(s_a8Results), RT_ELEMENTS(s_a16Results), RT_ELEMENTS(s_a32Results), RT_ELEMENTS(s_a64Results), \ + (a_fRelevantEFlags) \ + } + + +/** + * Generic worker for a FNCIDETSETUPINOUT function with two GPR/MEM registers, + * storing result in the first and flags. + * + * @returns See FNCIDETSETUPINOUT. + * @param pThis The core CIDET state structure. The InCtx + * and ExpectedCtx members will be modified. + * @param fInvalid When set, get the next invalid operands that will + * cause exceptions/faults. + * @param pResults The result collection. + */ +static int CidetGenericIn2Out1WithFlags(PCIDETCORE pThis, bool fInvalid, CIDET2IN1OUTWITHFLAGS const *pResults) +{ + int rc; + + Assert(pThis->idxMrmRegOp < 2); + Assert(pThis->idxMrmRmOp < 2); + Assert(pThis->idxMrmRmOp != pThis->idxMrmRegOp); + AssertCompile(RT_ELEMENTS(pThis->aiInOut) >= 4); + + if (!fInvalid) + { + if ( !pThis->fHasRegCollisionDirect + && !pThis->fHasRegCollisionMem) + { + pThis->InCtx.rfl &= ~(uint64_t)pResults->fRelevantEFlags; + pThis->ExpectedCtx.rfl &= ~(uint64_t)pResults->fRelevantEFlags; + switch (pThis->aOperands[0].cb) + { + case 1: + { + uint16_t idx = ++pThis->aiInOut[0] % pResults->c8Entries; + PCCIDET2IN1OUTWITHFLAGSU8ENTRY pEntry = &pResults->pa8Entries[idx]; + rc = idx ? VINF_SUCCESS : VINF_EOF; + + *pThis->aOperands[0].In.pu8 = pEntry->uIn1; + *pThis->aOperands[1].In.pu8 = pEntry->uIn2; + pThis->InCtx.rfl |= pEntry->fEFlagsIn; + + *pThis->aOperands[0].Expected.pu8 = pEntry->uOut; + *pThis->aOperands[1].Expected.pu8 = pEntry->uIn2; + pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut; + break; + } + + case 2: + { + uint16_t idx = ++pThis->aiInOut[1] % pResults->c16Entries; + PCCIDET2IN1OUTWITHFLAGSU16ENTRY pEntry = &pResults->pa16Entries[idx]; + rc = idx ? VINF_SUCCESS : VINF_EOF; + + *pThis->aOperands[0].In.pu16 = pEntry->uIn1; + *pThis->aOperands[1].In.pu16 = pEntry->uIn2; + pThis->InCtx.rfl |= pEntry->fEFlagsIn; + + *pThis->aOperands[0].Expected.pu16 = pEntry->uOut; + *pThis->aOperands[1].Expected.pu16 = pEntry->uIn2; + pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut; + break; + } + + case 4: + { + uint16_t idx = ++pThis->aiInOut[2] % pResults->c32Entries; + PCCIDET2IN1OUTWITHFLAGSU32ENTRY pEntry = &pResults->pa32Entries[idx]; + rc = idx ? VINF_SUCCESS : VINF_EOF; + + *pThis->aOperands[0].In.pu32 = pEntry->uIn1; + *pThis->aOperands[1].In.pu32 = pEntry->uIn2; + pThis->InCtx.rfl |= pEntry->fEFlagsIn; + + *pThis->aOperands[0].Expected.pu32 = pEntry->uOut; + if (!pThis->aOperands[0].fIsMem) + pThis->aOperands[0].Expected.pu32[1] = 0; + *pThis->aOperands[1].Expected.pu32 = pEntry->uIn2; + pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut; + break; + } + + case 8: + { + uint16_t idx = ++pThis->aiInOut[3] % pResults->c64Entries; + PCCIDET2IN1OUTWITHFLAGSU64ENTRY pEntry = &pResults->pa64Entries[idx]; + rc = idx ? VINF_SUCCESS : VINF_EOF; + + *pThis->aOperands[0].In.pu64 = pEntry->uIn1; + *pThis->aOperands[1].In.pu64 = pEntry->uIn2; + pThis->InCtx.rfl |= pEntry->fEFlagsIn; + + *pThis->aOperands[0].Expected.pu64 = pEntry->uOut; + *pThis->aOperands[1].Expected.pu64 = pEntry->uIn2; + pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut; + break; + } + + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + } + else + rc = VERR_NOT_SUPPORTED; + } + else + rc = VERR_NO_DATA; + return rc; +} + + +static DECLCALLBACK(int) cidetInOutAdd(PCIDETCORE pThis, bool fInvalid) +{ + static const CIDET2IN1OUTWITHFLAGSU8ENTRY s_a8Results[] = + { + { UINT8_C(0x00), UINT8_C(0x00), 0, UINT8_C(0x00), ZF | PF }, + { UINT8_C(0xff), UINT8_C(0x01), 0, UINT8_C(0x00), CF | ZF | AF | PF }, + { UINT8_C(0x7f), UINT8_C(0x80), 0, UINT8_C(0xff), SF | PF }, + { UINT8_C(0x01), UINT8_C(0x01), 0, UINT8_C(0x02), 0 }, + }; + static const CIDET2IN1OUTWITHFLAGSU16ENTRY s_a16Results[] = + { + { UINT16_C(0x0000), UINT16_C(0x0000), 0, UINT16_C(0x0000), ZF | PF }, + { UINT16_C(0xfefd), UINT16_C(0x0103), 0, UINT16_C(0x0000), CF | ZF | AF | PF }, + { UINT16_C(0x8e7d), UINT16_C(0x7182), 0, UINT16_C(0xffff), SF | PF }, + { UINT16_C(0x0001), UINT16_C(0x0001), 0, UINT16_C(0x0002), 0 }, + }; + static const CIDET2IN1OUTWITHFLAGSU32ENTRY s_a32Results[] = + { + { UINT32_C(0x00000000), UINT32_C(0x00000000), 0, UINT32_C(0x00000000), ZF | PF }, + { UINT32_C(0xfefdfcfb), UINT32_C(0x01020305), 0, UINT32_C(0x00000000), CF | ZF | AF | PF }, + { UINT32_C(0x8efdfcfb), UINT32_C(0x71020304), 0, UINT32_C(0xffffffff), SF | PF }, + { UINT32_C(0x00000001), UINT32_C(0x00000001), 0, UINT32_C(0x00000002), 0 }, + }; + static const CIDET2IN1OUTWITHFLAGSU64ENTRY s_a64Results[] = + { + { UINT64_C(0x0000000000000000), UINT64_C(0x0000000000000000), 0, UINT64_C(0x0000000000000000), ZF | PF }, + { UINT64_C(0xfefdfcfbfaf9f8f7), UINT64_C(0x0102030405060709), 0, UINT64_C(0x0000000000000000), CF | ZF | AF | PF }, + { UINT64_C(0x7efdfcfbfaf9f8f7), UINT64_C(0x8102030405060708), 0, UINT64_C(0xffffffffffffffff), SF | PF }, + { UINT64_C(0x0000000000000001), UINT64_C(0x0000000000000001), 0, UINT64_C(0x0000000000000002), 0 }, + }; + static const CIDET2IN1OUTWITHFLAGS s_Results = CIDET2IN1OUTWITHFLAGS_INITIALIZER(CF | PF | AF | SF | OF); + return CidetGenericIn2Out1WithFlags(pThis, fInvalid, &s_Results); +} + + +/** First bunch of instructions. */ +const CIDETINSTR g_aCidetInstructions1[] = +{ +#if 1 + { + "add Eb,Gb", cidetInOutAdd, 1, {0x00, 0, 0}, 0, 2, + { CIDET_OF_K_GPR | CIDET_OF_Z_BYTE | CIDET_OF_M_RM | CIDET_OF_A_RW, + CIDET_OF_K_GPR | CIDET_OF_Z_BYTE | CIDET_OF_M_REG | CIDET_OF_A_R, + 0, 0 }, CIDET_IF_MODRM + }, +#endif +#if 1 + { + "add Ev,Gv", cidetInOutAdd, 1, {0x01, 0, 0}, 0, 2, + { CIDET_OF_K_GPR | CIDET_OF_Z_VAR_WDQ | CIDET_OF_M_RM | CIDET_OF_A_RW, + CIDET_OF_K_GPR | CIDET_OF_Z_VAR_WDQ | CIDET_OF_M_REG | CIDET_OF_A_R, + 0, 0 }, CIDET_IF_MODRM + }, +#endif +}; +/** Number of instruction in the g_aInstructions1 array. */ +const uint32_t g_cCidetInstructions1 = RT_ELEMENTS(g_aCidetInstructions1); + diff --git a/src/VBox/ValidationKit/utils/cpu/cidet.h b/src/VBox/ValidationKit/utils/cpu/cidet.h new file mode 100644 index 00000000..2273b7b0 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet.h @@ -0,0 +1,1092 @@ +/* $Id: cidet.h $ */ +/** @file + * CPU Instruction Decoding & Execution Tests - C/C++ Header. + */ + +/* + * 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 + */ + +#ifndef VBOX_INCLUDED_SRC_cpu_cidet_h +#define VBOX_INCLUDED_SRC_cpu_cidet_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> +#include <iprt/x86.h> + + +/** @name CIDET - Operand flags. + * @{ */ +#define CIDET_OF_FIXED_MASK UINT32_C(0x0000001f) /**< Fixed register/whatever mask. */ + +#define CIDET_OF_Z_SHIFT 8 /**< Size shift. */ +#define CIDET_OF_Z_MASK UINT32_C(0x00000f00) /**< Size mask. */ +#define CIDET_OF_Z_NONE UINT32_C(0x00000000) /**< Unused zero value. */ +#define CIDET_OF_Z_BYTE UINT32_C(0x00000100) /**< Byte size. */ +#define CIDET_OF_Z_WORD UINT32_C(0x00000200) /**< Word (2 bytes) size. */ +#define CIDET_OF_Z_DWORD UINT32_C(0x00000300) /**< Double word (4 bytes) size. */ +#define CIDET_OF_Z_QWORD UINT32_C(0x00000400) /**< Quad word (8 bytes) size. */ +#define CIDET_OF_Z_TBYTE UINT32_C(0x00000500) /**< Ten byte (10 bytes) size - aka TWORD. */ +#define CIDET_OF_Z_OWORD UINT32_C(0x00000600) /**< Octa word (16 bytes) size - aka DQWORD. */ +#define CIDET_OF_Z_YWORD UINT32_C(0x00000700) /**< Yxx sized, i.e. 32 bytes. */ +#define CIDET_OF_Z_ZWORD UINT32_C(0x00000800) /**< Zxx sized, i.e. 64 bytes. */ +#define CIDET_OF_Z_VAR_WDQ UINT32_C(0x00000900) /**< Variable size depending on size prefix (2, 4, or 8 bytes). */ +#define CIDET_OF_Z_SPECIAL UINT32_C(0x00000f00) /**< Special size, see instruction flags or smth. */ + +#define CIDET_OF_K_MASK UINT32_C(0x0000f000) /**< Kind of operand. */ +#define CIDET_OF_K_NONE UINT32_C(0x00000000) /**< Unused zero value. */ +#define CIDET_OF_K_GPR UINT32_C(0x00001000) /**< General purpose register. Includes memory when used with CIDET_OF_M_RM. */ +#define CIDET_OF_K_SREG UINT32_C(0x00002000) /**< Segment register. */ +#define CIDET_OF_K_CR UINT32_C(0x00003000) /**< Control register. */ +#define CIDET_OF_K_SSE UINT32_C(0x00004000) /**< SSE register. */ +#define CIDET_OF_K_AVX UINT32_C(0x00005000) /**< AVX register. */ +#define CIDET_OF_K_AVX512 UINT32_C(0x00006000) /**< AVX-512 register. */ +#define CIDET_OF_K_AVXFUTURE UINT32_C(0x00007000) /**< Reserved for future AVX register set. */ +#define CIDET_OF_K_VRX_TST_MASK UINT32_C(0x0000c000) /**< Used for testing for VRX register kind, see CIDET_OF_K_IS_VRX. */ +#define CIDET_OF_K_VRX_TST_RES UINT32_C(0x00004000) /**< Used for testing for VRX register kind, see CIDET_OF_K_IS_VRX. */ +#define CIDET_OF_K_FPU UINT32_C(0x00008000) /**< FPU register. */ +#define CIDET_OF_K_MMX UINT32_C(0x00009000) /**< MMX register. */ +#define CIDET_OF_K_TEST UINT32_C(0x0000a000) /**< Test register. */ +#define CIDET_OF_K_IMM UINT32_C(0x0000d000) /**< Immediate. */ +#define CIDET_OF_K_MEM UINT32_C(0x0000e000) /**< Memory. */ +#define CIDET_OF_K_SPECIAL UINT32_C(0x0000f000) /**< Special. */ +/** Check if @a a_fOp is a general purpose register. */ +#define CIDET_OF_K_IS_GPR(a_fOp) ( ((a_fOp) & CIDET_OF_K_MASK) == CIDET_OF_K_GPR ) +/** Check if @a a_fOp is a XMM (SSE), YMM (AVX), ZMM (AVX-512) or similar register. */ +#define CIDET_OF_K_IS_VRX(a_fOp) ( ((a_fOp) & CIDET_OF_K_VRX_TST_MASK) == CIDET_OF_K_VRX_TST_RES ) +/** Check if @a a_fOp1 and @a a_fOp2 specify the same kind of register, + * treating SSE, AVX, AVX-512 and AVX-future as the same kind and ignoring the + * special register kind. */ +#define CIDET_OF_K_IS_SAME(a_fOp1, a_fOp2) \ + ( ((a_fOp1) & CIDET_OF_K_MASK) == ((a_fOp2) & CIDET_OF_K_MASK) \ + ? ((a_fOp1) & CIDET_OF_K_MASK) != CIDET_OF_K_SPECIAL \ + : (CIDET_OF_K_IS_VRX(a_fOp1) && CIDET_OF_K_IS_VRX(a_fOp2)) ) + +#define CIDET_OF_M_RM_ONLY_R UINT32_C(0x00010000) +#define CIDET_OF_M_RM_ONLY_M UINT32_C(0x00020000) +#define CIDET_OF_M_RM (CIDET_OF_M_RM_ONLY_R | CIDET_OF_M_RM_ONLY_M) +#define CIDET_OF_M_REG UINT32_C(0x00040000) + +#define CIDET_OF_A_R UINT32_C(0x00080000) /**< Read access. */ +#define CIDET_OF_A_W UINT32_C(0x00100000) /**< Write access. */ +#define CIDET_OF_A_RW UINT32_C(0x00180000) /**< Read & write access. */ + +/** The operand defaults to 64-bit width in 64-bit mode, making 32-bit width + * inaccessible. */ +#define CIDET_OF_DEFAULT_64BIT UINT32_C(0x40000000) +/** Operand always uses the ES segment for memory accesses. */ +#define CIDET_OF_ALWAYS_SEG_ES UINT32_C(0x80000000) +/** @} */ + + +/** @name CIDET - Instruction flags. + * @{ */ +#define CIDET_IF_MODRM RT_BIT_64(0) /**< ModR/M encoded. */ +#define CIDET_IF_PRIVILEGED RT_BIT_64(1) /**< Privileged. */ +/** @} */ + + +/** + * Callback function for setting up the input and expected output CPU contexts. + * + * @returns VBox status code. + * @retval VINF_EOF when static test data wraps (first entry is returned). + * @retval VERR_NO_DATA if @a fInvalid is set and there are no invalid operand + * values for this instruction. + * @retval VERR_NOT_SUPPORTED if something in the setup prevents us from + * comming up with working set of inputs and outputs. + * + * @param pThis The core CIDET state structure. The InCtx + * and ExpectedCtx members will be modified. + * @param fInvalid When set, get the next invalid operands that will + * cause exceptions/faults. + */ +typedef DECLCALLBACKTYPE(int, FNCIDETSETUPINOUT,(struct CIDETCORE *pThis, bool fInvalid)); +/** Pointer to a FNCIDETSETUPINOUT function. */ +typedef FNCIDETSETUPINOUT *PFNCIDETSETUPINOUT; + + +/** + * Instruction test descriptor. + */ +typedef struct CIDETINSTR +{ + /** The mnemonic (kind of). */ + const char *pszMnemonic; + /** Setup input and outputs. */ + PFNCIDETSETUPINOUT pfnSetupInOut; + /** Number of opcode bytes. */ + uint8_t cbOpcode; + /** Opcode byte(s). */ + uint8_t abOpcode[3]; + /** Mandatory prefix (zero if not applicable). */ + uint8_t bMandatoryPrefix; + /** Number of operands. */ + uint8_t cOperands; + /** Operand flags. */ + uint32_t afOperands[4]; + /** Flags. */ + uint64_t fFlags; +} CIDETINSTR; +/** Pointer to an instruction test descriptor. */ +typedef CIDETINSTR const *PCCIDETINSTR; + + +/** + * CPU Context with a few extra bits for expectations and results. + */ +typedef struct CIDETCPUCTX +{ + uint64_t rip; + uint64_t rfl; + uint64_t aGRegs[16]; + uint16_t aSRegs[6]; + +#ifndef CIDET_REDUCED_CTX + uint16_t tr; + uint16_t ldtr; + uint64_t cr0; +#else + uint16_t au16Padding[2]; +#endif + uint64_t cr2; +#ifndef CIDET_REDUCED_CTX + uint64_t cr3; + uint64_t cr4; + uint64_t cr8; + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr6; + uint64_t dr7; +#endif + + uint64_t uErr; /**< Exception error code. UINT64_MAX if not applicable. (Not for input context.) */ + uint32_t uXcpt; /**< Exception number. UINT32_MAX if no exception. (Not for input context.) */ + + uint32_t fIgnoredRFlags; /**< Only for expected result. */ + bool fTrickyStack; /**< Set if the stack might be bad. May come at the cost of accurate flags (32-bit). */ +} CIDETCPUCTX; +typedef CIDETCPUCTX *PCIDETCPUCTX; +typedef CIDETCPUCTX const *PCCIDETCPUCTX; + +/** Number of bytes of CIDETCPUCTX that can be compared quickly using memcmp. + * Anything following these bytes are not relevant to the compare. */ +#define CIDETCPUCTX_COMPARE_SIZE RT_UOFFSETOF(CIDETCPUCTX, fIgnoredRFlags) + + +/** @name CPU mode + bits + environment. + * @{ */ +#define CIDETMODE_BIT_MASK UINT8_C(0x0e) /**< The instruction bit count. Results in byte size when masked. */ +#define CIDETMODE_BIT_16 UINT8_C(0x02) /**< 16-bit instructions. */ +#define CIDETMODE_BIT_32 UINT8_C(0x04) /**< 32-bit instructions. */ +#define CIDETMODE_BIT_64 UINT8_C(0x08) /**< 64-bit instructions. */ +#define CIDETMODE_MODE_MASK UINT8_C(0x70) /**< CPU mode mask. */ +#define CIDETMODE_MODE_RM UINT8_C(0x00) /**< Real mode. */ +#define CIDETMODE_MODE_PE UINT8_C(0x10) /**< Protected mode without paging. */ +#define CIDETMODE_MODE_PP UINT8_C(0x20) /**< Paged protected mode. */ +#define CIDETMODE_MODE_PAE UINT8_C(0x30) /**< PAE protected mode (paged). */ +#define CIDETMODE_MODE_LM UINT8_C(0x40) /**< Long mode (paged). */ +#define CIDETMODE_ENV_MASK UINT8_C(0x81) /**< Execution environment. */ +#define CIDETMODE_ENV_NORMAL UINT8_C(0x01) /**< Normal environment. */ +#define CIDETMODE_ENV_V86 UINT8_C(0x80) /**< V8086 environment. */ +#define CIDETMODE_RM (CIDETMODE_MODE_RM | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PE_16 (CIDETMODE_MODE_PE | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PE_32 (CIDETMODE_MODE_PE | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PE_V86 (CIDETMODE_MODE_PE | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86) +#define CIDETMODE_PP_16 (CIDETMODE_MODE_PP | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PP_32 (CIDETMODE_MODE_PP | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PP_V86 (CIDETMODE_MODE_PP | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86) +#define CIDETMODE_PAE_16 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PAE_32 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_PAE_V86 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86) +#define CIDETMODE_LM_16 (CIDETMODE_MODE_LM | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_LM_32 (CIDETMODE_MODE_LM | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL) +#define CIDETMODE_LM_64 (CIDETMODE_MODE_LM | CIDETMODE_BIT_64 | CIDETMODE_ENV_NORMAL) +/** Test if @a a_bMode is a 16-bit mode. */ +#define CIDETMODE_IS_16BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_16 ) +/** Test if @a a_bMode is a 32-bit mode. */ +#define CIDETMODE_IS_32BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_32 ) +/** Test if @a a_bMode is a 64-bit mode. */ +#define CIDETMODE_IS_64BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_64 ) +/** Get the instruction bit count. */ +#define CIDETMODE_GET_BIT_COUNT(a_bMode) ( CIDETMODE_GET_BYTE_COUNT(a_bMode) * 8 ) +/** Get the instruction byte count. */ +#define CIDETMODE_GET_BYTE_COUNT(a_bMode) ( (a_bMode) & CIDETMODE_BIT_MASK ) +/** Test if @a a_bMode long mode. */ +#define CIDETMODE_IS_LM(a_bMode) ( ((a_bMode) & CIDETMODE_MODE_MASK) == CIDETMODE_MODE_LM ) +/** Test if @a a_bMode some kind of protected mode. */ +#define CIDETMODE_IS_PROT(a_bMode) ( ((a_bMode) & CIDETMODE_MODE_MASK) >= CIDETMODE_MODE_PE ) + +/** @} */ + + +/** @name Test Configuration Flags. + * @{ */ +#define CIDET_TESTCFG_SEG_PRF_CS UINT64_C(0x0000000000000001) +#define CIDET_TESTCFG_SEG_PRF_SS UINT64_C(0x0000000000000002) +#define CIDET_TESTCFG_SEG_PRF_DS UINT64_C(0x0000000000000004) +#define CIDET_TESTCFG_SEG_PRF_ES UINT64_C(0x0000000000000008) +#define CIDET_TESTCFG_SEG_PRF_FS UINT64_C(0x0000000000000010) +#define CIDET_TESTCFG_SEG_PRF_GS UINT64_C(0x0000000000000020) +#define CIDET_TESTCFG_SEG_PRF_MASK UINT64_C(0x000000000000003f) +/** @} */ + +/** */ +typedef enum CIDETREG +{ + kCidetReg_Gpr_Invalid = 0, + + kCidetReg_Gpr_al, + kCidetReg_Gpr_cl, + kCidetReg_Gpr_dl, + kCidetReg_Gpr_bl, + kCidetReg_Gpr_spl, + kCidetReg_Gpr_bpl, + kCidetReg_Gpr_sil, + kCidetReg_Gpr_dil, + kCidetReg_Gpr_r8b, + kCidetReg_Gpr_r9b, + kCidetReg_Gpr_r10b, + kCidetReg_Gpr_r11b, + kCidetReg_Gpr_r12b, + kCidetReg_Gpr_r13b, + kCidetReg_Gpr_r14b, + kCidetReg_Gpr_r15b, + kCidetReg_Gpr_ah, + kCidetReg_Gpr_ch, + kCidetReg_Gpr_dh, + kCidetReg_Gpr_bh, +#define kCidetReg_Gpr_Byte_First kCidetReg_Gpr_al +#define kCidetReg_Gpr_Byte_First_Upper kCidetReg_Gpr_ah +#define kCidetReg_Gpr_Byte_Last kCidetReg_Gpr_bh + + kCidetReg_Gpr_ax, + kCidetReg_Gpr_cx, + kCidetReg_Gpr_dx, + kCidetReg_Gpr_bx, + kCidetReg_Gpr_sp, + kCidetReg_Gpr_bp, + kCidetReg_Gpr_si, + kCidetReg_Gpr_di, + kCidetReg_Gpr_r8w, + kCidetReg_Gpr_r9w, + kCidetReg_Gpr_r10w, + kCidetReg_Gpr_r11w, + kCidetReg_Gpr_r12w, + kCidetReg_Gpr_r13w, + kCidetReg_Gpr_r14w, + kCidetReg_Gpr_r15w, +#define kCidetReg_Gpr_Word_First kCidetReg_Gpr_ax +#define kCidetReg_Gpr_Word_Last kCidetReg_Gpr_r15w + + kCidetReg_Gpr_eax, + kCidetReg_Gpr_ecx, + kCidetReg_Gpr_edx, + kCidetReg_Gpr_ebx, + kCidetReg_Gpr_esp, + kCidetReg_Gpr_ebp, + kCidetReg_Gpr_esi, + kCidetReg_Gpr_edi, + kCidetReg_Gpr_r8d, + kCidetReg_Gpr_r9d, + kCidetReg_Gpr_r10d, + kCidetReg_Gpr_r11d, + kCidetReg_Gpr_r12d, + kCidetReg_Gpr_r13d, + kCidetReg_Gpr_r14d, + kCidetReg_Gpr_r15d, +#define kCidetReg_Gpr_DWord_First kCidetReg_Gpr_eax +#define kCidetReg_Gpr_DWord_Last kCidetReg_Gpr_r15d + + kCidetReg_Gpr_rax, + kCidetReg_Gpr_rcx, + kCidetReg_Gpr_rdx, + kCidetReg_Gpr_rbx, + kCidetReg_Gpr_rsp, + kCidetReg_Gpr_rbp, + kCidetReg_Gpr_rsi, + kCidetReg_Gpr_rdi, + kCidetReg_Gpr_r8, + kCidetReg_Gpr_r9, + kCidetReg_Gpr_r10, + kCidetReg_Gpr_r11, + kCidetReg_Gpr_r12, + kCidetReg_Gpr_r13, + kCidetReg_Gpr_r14, + kCidetReg_Gpr_r15, +#define kCidetReg_Gpr_QWord_First kCidetReg_Gpr_rax +#define kCidetReg_Gpr_QWord_Last kCidetReg_Gpr_r15 + + kCidetReg_Seg_es, + kCidetReg_Seg_cs, + kCidetReg_Seg_ss, + kCidetReg_Seg_ds, + kCidetReg_Seg_fs, + kCidetReg_Seg_gs, + kCidetReg_Seg_Inv6, + kCidetReg_Seg_Inv7, +#define kCidetReg_Seg_First kCidetReg_Seg_es +#define kCidetReg_Seg_Last kCidetReg_Seg_gs +#define kCidetReg_Seg_Last_Inv kCidetReg_Seg_Inv7 + + kCidetReg_Misc_ip, + kCidetReg_Misc_eip, + kCidetReg_Misc_rip, + kCidetReg_Misc_flags, + kCidetReg_Misc_eflags, + kCidetReg_Misc_rflags, + kCidetReg_Misc_tr, + kCidetReg_Misc_ldtr, + kCidetReg_Misc_gdtr, + kCidetReg_Misc_idtr, + + kCidetReg_Ctrl_cr0, + kCidetReg_Ctrl_cr1, + kCidetReg_Ctrl_cr2, + kCidetReg_Ctrl_cr3, + kCidetReg_Ctrl_cr4, + kCidetReg_Ctrl_cr5, + kCidetReg_Ctrl_cr6, + kCidetReg_Ctrl_cr7, + kCidetReg_Ctrl_cr8, + kCidetReg_Ctrl_cr9, + kCidetReg_Ctrl_cr10, + kCidetReg_Ctrl_cr11, + kCidetReg_Ctrl_cr12, + kCidetReg_Ctrl_cr13, + kCidetReg_Ctrl_cr14, + kCidetReg_Ctrl_cr15, +#define kCidetReg_Ctrl_First kCidetReg_Ctrl_cr0 +#define kCidetReg_Ctrl_Last kCidetReg_Ctrl_cr15 +#define CIDETREG_CTRL_IS_VALID(a_iReg) ( (a_iReg) == kCidetReg_Ctrl_cr0 \ + && (a_iReg) == kCidetReg_Ctrl_cr2 \ + && (a_iReg) == kCidetReg_Ctrl_cr3 \ + && (a_iReg) == kCidetReg_Ctrl_cr4 \ + && (a_iReg) == kCidetReg_Ctrl_cr8 ) + + kCidetReg_Dbg_dr0, + kCidetReg_Dbg_dr1, + kCidetReg_Dbg_dr2, + kCidetReg_Dbg_dr3, + kCidetReg_Dbg_dr4, + kCidetReg_Dbg_dr5, + kCidetReg_Dbg_dr6, + kCidetReg_Dbg_dr7, + kCidetReg_Dbg_dr8, + kCidetReg_Dbg_dr9, + kCidetReg_Dbg_dr10, + kCidetReg_Dbg_dr11, + kCidetReg_Dbg_dr12, + kCidetReg_Dbg_dr13, + kCidetReg_Dbg_dr14, + kCidetReg_Dbg_dr15, +#define kCidetReg_Dbg_First kCidetReg_Dbg_dr0 +#define kCidetReg_Dbg_Last kCidetReg_Dbg_dr15 +#define CIDETREG_DBG_IS_VALID(a_iReg) ((a_iReg) < kCidetReg_Dbg_dr8 && (a_iReg) >= kCidetReg_Dbg_First) + + kCidetReg_Test_tr0, + kCidetReg_Test_tr1, + kCidetReg_Test_tr2, + kCidetReg_Test_tr3, + kCidetReg_Test_tr4, + kCidetReg_Test_tr5, + kCidetReg_Test_tr6, + kCidetReg_Test_tr7, + kCidetReg_Test_tr8, + kCidetReg_Test_tr9, + kCidetReg_Test_tr10, + kCidetReg_Test_tr11, + kCidetReg_Test_tr12, + kCidetReg_Test_tr13, + kCidetReg_Test_tr14, + kCidetReg_Test_tr15, +#define kCidetReg_Test_First kCidetReg_Test_tr0 +#define kCidetReg_Test_Last kCidetReg_Test_tr15 + + kCidetReg_Fpu_st0, + kCidetReg_Fpu_st1, + kCidetReg_Fpu_st2, + kCidetReg_Fpu_st3, + kCidetReg_Fpu_st4, + kCidetReg_Fpu_st5, + kCidetReg_Fpu_st6, + kCidetReg_Fpu_st7, +#define kCidetReg_Fpu_First kCidetReg_Mmx_st0 +#define kCidetReg_Fpu_Last kCidetReg_Mmx_st7 + + kCidetReg_FpuMisc_cs, + kCidetReg_FpuMisc_ip, + kCidetReg_FpuMisc_ds, + kCidetReg_FpuMisc_dp, + kCidetReg_FpuMisc_fop, + kCidetReg_FpuMisc_ftw, + kCidetReg_FpuMisc_fsw, + kCidetReg_FpuMisc_fcw, + kCidetReg_FpuMisc_mxcsr_mask, + kCidetReg_FpuMisc_mxcsr, + + kCidetReg_Mmx_mm0, + kCidetReg_Mmx_mm1, + kCidetReg_Mmx_mm2, + kCidetReg_Mmx_mm3, + kCidetReg_Mmx_mm4, + kCidetReg_Mmx_mm5, + kCidetReg_Mmx_mm6, + kCidetReg_Mmx_mm7, +#define kCidetReg_Mmx_First kCidetReg_Mmx_mm0 +#define kCidetReg_Mmx_Last kCidetReg_Mmx_mm7 + + kCidetReg_Sse_xmm0, + kCidetReg_Sse_xmm1, + kCidetReg_Sse_xmm2, + kCidetReg_Sse_xmm3, + kCidetReg_Sse_xmm4, + kCidetReg_Sse_xmm5, + kCidetReg_Sse_xmm6, + kCidetReg_Sse_xmm7, + kCidetReg_Sse_xmm8, + kCidetReg_Sse_xmm9, + kCidetReg_Sse_xmm10, + kCidetReg_Sse_xmm11, + kCidetReg_Sse_xmm12, + kCidetReg_Sse_xmm13, + kCidetReg_Sse_xmm14, + kCidetReg_Sse_xmm15, + kCidetReg_Sse_xmm16, + kCidetReg_Sse_xmm17, + kCidetReg_Sse_xmm18, + kCidetReg_Sse_xmm19, + kCidetReg_Sse_xmm20, + kCidetReg_Sse_xmm21, + kCidetReg_Sse_xmm22, + kCidetReg_Sse_xmm23, + kCidetReg_Sse_xmm24, + kCidetReg_Sse_xmm25, + kCidetReg_Sse_xmm26, + kCidetReg_Sse_xmm27, + kCidetReg_Sse_xmm28, + kCidetReg_Sse_xmm29, + kCidetReg_Sse_xmm30, + kCidetReg_Sse_xmm31, +#define kCidetReg_Sse_First kCidetReg_Mmx_Xmm0 +#define kCidetReg_Sse_Last kCidetReg_Mmx_Xmm15 +#define kCidetReg_Sse_Last_Avx512 kCidetReg_Mmx_Xmm31 + + kCidetReg_Avx_Ymm0, + kCidetReg_Avx_Ymm1, + kCidetReg_Avx_Ymm2, + kCidetReg_Avx_Ymm3, + kCidetReg_Avx_Ymm4, + kCidetReg_Avx_Ymm5, + kCidetReg_Avx_Ymm6, + kCidetReg_Avx_Ymm7, + kCidetReg_Avx_Ymm8, + kCidetReg_Avx_Ymm9, + kCidetReg_Avx_Ymm10, + kCidetReg_Avx_Ymm11, + kCidetReg_Avx_Ymm12, + kCidetReg_Avx_Ymm13, + kCidetReg_Avx_Ymm14, + kCidetReg_Avx_Ymm15, + kCidetReg_Avx_Ymm16, + kCidetReg_Avx_Ymm17, + kCidetReg_Avx_Ymm18, + kCidetReg_Avx_Ymm19, + kCidetReg_Avx_Ymm20, + kCidetReg_Avx_Ymm21, + kCidetReg_Avx_Ymm22, + kCidetReg_Avx_Ymm23, + kCidetReg_Avx_Ymm24, + kCidetReg_Avx_Ymm25, + kCidetReg_Avx_Ymm26, + kCidetReg_Avx_Ymm27, + kCidetReg_Avx_Ymm28, + kCidetReg_Avx_Ymm29, + kCidetReg_Avx_Ymm30, + kCidetReg_Avx_Ymm31, +#define kCidetReg_Avx_First kCidetReg_Avx_Ymm0 +#define kCidetReg_Avx_Last kCidetReg_Avx_Ymm15 +#define kCidetReg_Avx_Last_Avx512 kCidetReg_Avx_Ymm31 + + kCidetReg_Avx512_Zmm0, + kCidetReg_Avx512_Zmm1, + kCidetReg_Avx512_Zmm2, + kCidetReg_Avx512_Zmm3, + kCidetReg_Avx512_Zmm4, + kCidetReg_Avx512_Zmm5, + kCidetReg_Avx512_Zmm6, + kCidetReg_Avx512_Zmm7, + kCidetReg_Avx512_Zmm8, + kCidetReg_Avx512_Zmm9, + kCidetReg_Avx512_Zmm10, + kCidetReg_Avx512_Zmm11, + kCidetReg_Avx512_Zmm12, + kCidetReg_Avx512_Zmm13, + kCidetReg_Avx512_Zmm14, + kCidetReg_Avx512_Zmm15, + kCidetReg_Avx512_Zmm16, + kCidetReg_Avx512_Zmm17, + kCidetReg_Avx512_Zmm18, + kCidetReg_Avx512_Zmm19, + kCidetReg_Avx512_Zmm20, + kCidetReg_Avx512_Zmm21, + kCidetReg_Avx512_Zmm22, + kCidetReg_Avx512_Zmm23, + kCidetReg_Avx512_Zmm24, + kCidetReg_Avx512_Zmm25, + kCidetReg_Avx512_Zmm26, + kCidetReg_Avx512_Zmm27, + kCidetReg_Avx512_Zmm28, + kCidetReg_Avx512_Zmm29, + kCidetReg_Avx512_Zmm30, + kCidetReg_Avx512_Zmm31, +#define kCidetReg_Avx512_First kCidetReg_Avx512_Zmm0 +#define kCidetReg_Avx512_Last kCidetReg_Avx512_Zmm31 + + kCidetReg_End +} CIDETREG; + + +/** @name CIDETBUF_XXX - buffer flags. + * @{ */ +#define CIDETBUF_PROT_MASK UINT32_C(0x0000000f) /**< Page protection mask. */ +#define CIDETBUF_PROT_RWX UINT32_C(0x00000001) /**< Read + write + execute. */ +#define CIDETBUF_PROT_RWNX UINT32_C(0x00000002) /**< Read + write + no execute. */ +#define CIDETBUF_PROT_RX UINT32_C(0x00000003) /**< Read + execute. */ +#define CIDETBUF_PROT_RNX UINT32_C(0x00000004) /**< Read + no execute. */ +#define CIDETBUF_PROT_RWX_1NP UINT32_C(0x00000005) /**< Read + write + execute; 1 page not present. */ +#define CIDETBUF_PROT_RWX_1RWNX UINT32_C(0x00000006) /**< Read + write + execute; 1 page read + write + no execute. */ +#define CIDETBUF_PROT_RWX_1RNX UINT32_C(0x00000007) /**< Read + write + execute; 1 page read + no execute. */ +#define CIDETBUF_PROT_RWX_1RWXS UINT32_C(0x00000008) /**< Read + write + execute; 1 page read + execute + supervisor. */ + +#define CIDETBUF_LOC_MASK UINT32_C(0x000000f0) /**< Location mask. */ +/** Buffer located at top and start of the 32-bit address space. */ +#define CIDETBUF_LOC_32BIT_WRAP UINT32_C(0x00000010) +/** Buffer located at the low canonical boundrary (AMD64). */ +#define CIDETBUF_LOC_CANON_LO UINT32_C(0x00000020) +/** Buffer located at the high canonical boundrary (AMD64). */ +#define CIDETBUF_LOC_CANON_HI UINT32_C(0x00000030) + +/** Segment protection mask. */ +#define CIDETBUF_SEG_MASK UINT32_C(0x00000f00) +#define CIDETBUF_SEG_EO UINT32_C(0x00000100) /**< Execute only */ +#define CIDETBUF_SEG_ER UINT32_C(0x00000200) /**< Execute + read */ +#define CIDETBUF_SEG_EO_CONF UINT32_C(0x00000300) /**< Execute only + conforming. */ +#define CIDETBUF_SEG_ER_CONF UINT32_C(0x00000400) /**< Execute + read + conforming. */ +#define CIDETBUF_SEG_RO UINT32_C(0x00000500) /**< Read only. */ +#define CIDETBUF_SEG_RW UINT32_C(0x00000600) /**< Read + write. */ +#define CIDETBUF_SEG_RO_DOWN UINT32_C(0x00000700) /**< Read only + expand down. */ +#define CIDETBUF_SEG_RW_DOWN UINT32_C(0x00000800) /**< Read + write + expand down. */ + +#define CIDETBUF_DPL_MASK UINT32_C(0x00003000) /**< DPL mask. */ +#define CIDETBUF_DPL_0 UINT32_C(0x00000000) /**< DPL=0. */ +#define CIDETBUF_DPL_1 UINT32_C(0x00001000) /**< DPL=1. */ +#define CIDETBUF_DPL_2 UINT32_C(0x00002000) /**< DPL=2. */ +#define CIDETBUF_DPL_3 UINT32_C(0x00003000) /**< DPL=3. */ +#define CIDETBUF_DPL_SAME UINT32_C(0x00004000) /**< Same DPL as the execution environment. */ + +#define CIDETBUF_SEG_LIMIT_BASE_CAP UINT32_C(0x00008000) /**< Capability to change segment limit and base. */ + +#define CIDETBUF_KIND_DATA UINT32_C(0x00000000) /**< Data buffer. */ +#define CIDETBUF_KIND_CODE UINT32_C(0x80000000) /**< Code buffer. */ +/** Checks if @a a_fFlags describes a code buffer. */ +#define CIDETBUF_IS_CODE(a_fFlags) (((a_fFlags) & CIDETBUF_KIND_CODE) != 0) +/** Checks if @a a_fFlags describes a data buffer. */ +#define CIDETBUF_IS_DATA(a_fFlags) (((a_fFlags) & CIDETBUF_KIND_CODE) == 0) +/** @} */ + +/** Code buffer size. (At least two pages.) */ +#define CIDET_CODE_BUF_SIZE (PAGE_SIZE * 2) +/** Data buffer size. (At least two pages.) */ +#define CIDET_DATA_BUF_SIZE (PAGE_SIZE * 3) + + +/** + * Detailed expected exception. + * + * This is used to internally in the core to calculate the expected exception + * considering all the things that may cause exceptions. + */ +typedef enum CIDETEXPECTXCPT +{ + kCidetExpectXcpt_Invalid = 0, + /** No exception expected. */ + kCidetExpectXcpt_None, + + /** Page not present. */ + kCidetExpectXcpt_PageNotPresent, + /** Write access to a non-writable page. */ + kCidetExpectXcpt_PageNotWritable, + /** Executable access to a non-executable page. */ + kCidetExpectXcpt_PageNotExecutable, + /** Access to supervisor page from user mode code. */ + kCidetExpectXcpt_PagePrivileged, +#define kCidetExpectXcpt_First_PageFault kCidetExpectXcpt_PageNotPresent +#define kCidetExpectXcpt_Last_PageFault kCidetExpectXcpt_PagePrivileged + + /** Read or write access to an execute only segment. */ + kCidetExpectXcpt_SegExecuteOnly, + /** Write to a read only or execute+read segment. */ + kCidetExpectXcpt_SegNotWritable, + /** Exceeded the limit of a non-stack access. */ + kCidetExpectXcpt_SegExceededLimit, + /** Non-canonical address via any segment other than the stack. */ + kCidetExpectXcpt_AddrNotCanonical, + /** Misaligned 16 or 32 byte SSE or AVX operand. */ + kCidetExpectXcpt_MisalignedSseAvx, + /** Privileged instruction. */ + kCidetExpectXcpt_PrivilegedInstruction, +#define kCidetExpectXcpt_First_GeneralProtectionFault kCidetExpectXcpt_SegExecuteOnly +#define kCidetExpectXcpt_Last_GeneralProtectionFault kCidetExpectXcpt_PrivilegedInstruction + + /** Exceeded the limit of a stack access. */ + kCidetExpectXcpt_StackExceededLimit, + /** Non-canonical stack address. */ + kCidetExpectXcpt_StackAddrNotCanonical, +#define kCidetExpectXcpt_First_StackFault kCidetExpectXcpt_StackExceededLimit +#define kCidetExpectXcpt_Last_StackFault kCidetExpectXcpt_StackAddrNotCanonical + + /** Misaligned memory operand (and alignment checking is in effect) if AC is + * enabled (executing in ring-3). */ + kCidetExpectXcpt_MisalignedIfAcEnabled, + /** Misaligned 16 byte memory operand resulting in \#AC if ring-3 and + * enable, otherwise \#GP(0). */ + kCidetExpectXcpt_Misaligned16ByteAcEnabledOrGp, +#define kCidetExpectXcpt_First_AlignmentCheckFault kCidetExpectXcpt_MisalignedIfAcEnabled +#define kCidetExpectXcpt_Last_AlignmentCheckFault kCidetExpectXcpt_Misaligned16ByteAcEnabledOrGp + + kCidetExpectXcpt_End +} CIDETEXPECTXCPT; + + +/** + * Buffer configuration. + */ +typedef struct CIDETBUFCFG +{ + /** The name of this buffer configuration. */ + const char *pszName; + /** The buffer flags (CIDETBUF_XXX) */ + uint32_t fFlags; +} CIDETBUFCFG; +/** Pointer to a constant buffer configuration. */ +typedef CIDETBUFCFG const *PCCIDETBUFCFG; + + +/** + * CIDET buffer for code or data. + * + * ASSUMES page aligned buffers. + */ +typedef struct CIDETBUF +{ + /** @name Owned & modified by the front end. + * @{ */ + /** Effective buffer address. */ + uint64_t uEffBufAddr; + /** The segment base address. */ + uint64_t uSegBase; + /** The active segment limit (see also cbSegLimit). UINT64_MAX if flat. */ + uint64_t cbActiveSegLimit; + /** This specifies the selector to use if a non-flat segment limit or special + * segment flags was requested via pfnSetupBuf. UINT32_MAX if any segment is + * selector works. */ + uint32_t uSeg; + /** The off value at the last pfnReinitBuf call. */ + uint16_t offActive; + /** The cb value at the last pfnReinitBuf call. */ + uint16_t cbActive; + /** Prologue (or front fence) size. */ + uint16_t cbPrologue; + /** Epilogue (or tail fence) size. */ + uint16_t cbEpilogue; + /** @} */ + + /** @name Set by the core before pfnReinitBuf call. + * @{ */ + /** Pointer to the buffer config. */ + PCCIDETBUFCFG pCfg; + /** The configuration index. */ + uint32_t idxCfg; + /** The offset into the buffer of the data / code. */ + uint16_t off; + /** The number of bytes of data / code. */ + uint16_t cb; + /** The segment limit relative to the start of the buffer (last byte included + * in count). UINT16_MAX if maximum segment size should be used. */ + uint16_t cbSegLimit; + /** Desired segment base offset. + * This is for checking where the alignment checks are performed. */ + uint8_t offSegBase; + + /** Set if this buffer is actively being used. */ + bool fActive : 1; + /** The operand index (if data), 7 if not active. */ + uint8_t idxOp : 3; + /** Code: Set if the expected exception is supposed to occur on the + * following insturction, not the instruction unter test. */ + bool fXcptAfterInstruction : 1; + /** Set if the instruction will read from the buffer. */ + bool fRead : 1; + /** Set if the instruction will write to the buffer. */ + bool fWrite : 1; + /** The expected exception. */ + CIDETEXPECTXCPT enmExpectXcpt; + /** @} */ +} CIDETBUF; +/** Pointer to a CIDET buffer for code or data. */ +typedef CIDETBUF *PCIDETBUF; + + +/** + * CPU Instruction Decoding & Execution Testing (CIDET) state. + */ +typedef struct CIDETCORE +{ + /** Magic number (CIDETCORE_MAGIC). */ + uint32_t u32Magic; + + /** The target CPU mode / environment. */ + uint8_t bMode; + /** The target ring. */ + uint8_t iRing; + /** Unused padding bytes. */ + uint8_t abPadding1[2]; + + /** Test configuration. */ + uint64_t fTestCfg; + + /** Code buffer configurations to test. + * The first buffer must be a normal buffer that does not cause any problems. */ + PCCIDETBUFCFG paCodeBufConfigs; + /** The number of code buffer configurations to test (pafCodeBufConfigs). */ + uint32_t cCodeBufConfigs; + /** The number of data buffer configurations to test (pafDataBufConfigs). */ + uint32_t cDataBufConfigs; + /** Data buffer configurations to test. + * The first buffer must be a normal buffer that does not cause any problems. */ + PCCIDETBUFCFG paDataBufConfigs; + + /** The instruction currently under testing. */ + PCCIDETINSTR pCurInstr; + + /** Primary data buffer. */ + CIDETBUF DataBuf; + /** Secondary data buffer. */ + CIDETBUF DataBuf2; + + /** Handle to the random number source. */ + RTRAND hRand; + + /** + * Re-initializes one of the data buffers. + * + * @returns true on succes, false if the request cannot be satisfied. + * @param pThis The core state. + * @param pBuf Pointer to the buffer structure. + */ + DECLCALLBACKMEMBER(bool, pfnReInitDataBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf)); + + /** + * Copies bytes into the data buffer and sets it up for execution. + * + * @returns true on succes, false if the request cannot be satisfied. + * @param pThis The core state. + * @param pBuf Pointer to the buffer structure. + * @param pvSrc The source bytes (size and destination offset + * given in pfnReinitBuf call). + */ + DECLCALLBACKMEMBER(bool, pfnSetupDataBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf, void const *pvSrc)); + + /** + * Compares buffer content after test execution. + * + * This also checks any fill bytes in the buffer that the front end may + * have put up. The front end will double buffer the content of supposedly + * inaccessible pages as well as non-existing pages to simplify things for + * the core code. + * + * @returns true if equal, false if not. + * @param pThis The core state. + * @param pBuf Pointer to the buffer structure. + * @param pvExpected Pointer to the expected source bytes (size and + * buffer offset given in pfnReinitBuf call). + */ + DECLCALLBACKMEMBER(bool, pfnIsBufEqual,(struct CIDETCORE *pThis, struct CIDETBUF *pBuf, void const *pvExpected)); + + /** + * Re-initializes the code buffer. + * + * @returns true on succes, false if the request cannot be satisfied. + * @param pThis The core state. + * @param pBuf Pointer to the CodeBuf member. The off and cb + * members represent what the core wants to + * execute. + */ + DECLCALLBACKMEMBER(bool, pfnReInitCodeBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf)); + + /** + * Emit code into the code buffer, making everything ready for pfnExecute. + * + * @returns VBox status code. + * @param pThis Pointer to the core structure. + * @param pBuf Pointer to the CodeBuf member. + * @param pvInstr Pointer to the encoded instruction bytes. + */ + DECLCALLBACKMEMBER(bool, pfnSetupCodeBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf, void const *pvInstr)); + + /** + * Executes the code indicated by InCtx, returning the result in ActualCtx. + * + * @returns true if execute, false if skipped. + * @param pThis Pointer to the core structure. + */ + DECLCALLBACKMEMBER(bool, pfnExecute,(struct CIDETCORE *pThis)); + + /** + * Report a test failure. + * + * @param pThis Pointer to the core structure. + * @param pszFormat Format string containing failure details. + * @param va Arguments referenced in @a pszFormat. + */ + DECLCALLBACKMEMBER(void, pfnFailure,(struct CIDETCORE *pThis, const char *pszFormat, va_list va)); + + /** Array of indexes for use by FNCIDETSETUPINOUT. + * Reset when changing instruction or switching between valid and invalid + * inputs. */ + uint32_t aiInOut[4]; + + /** @name Copyied and extracted instruction information. + * @{ */ + /** The flags (CIDET_OF_XXX) for the MODRM.REG operand, 0 if not applicable. */ + uint32_t fMrmRegOp; + /** The flags (CIDET_OF_XXX) for the MODRM.RM operand, 0 if not applicable. */ + uint32_t fMrmRmOp; + /** Instruction flags (CIDETINSTR::fFlags). */ + uint64_t fInstrFlags; + /** Number of operands (CIDETINSTR::cOperands). */ + uint8_t cOperands; + /** Number of memory operands (set by CidetCoreSetupFirstMemoryOperandConfig). */ + uint8_t cMemoryOperands : 3; + /** Set if we're working on a MOD R/M byte. */ + bool fUsesModRm : 1; + /** The index of the MODRM.REG operand, 7 if not applicable. */ + uint8_t idxMrmRegOp : 3; + /** The index of the MODRM.RM operand, 7 if not applicable. */ + uint8_t idxMrmRmOp : 3; + /** Set if the SIB byte uses VEX registers for indexing. */ + bool fUsesVexIndexRegs : 1; + /** @} */ + + /** @name Basic encoding knobs, wheels and indicators. + * @{ */ + /** Set if we're working on a SIB byte. */ + bool fSib : 1; + /** Required segment prefix (X86_SREG_XXX), X86_SREG_COUNT if not. */ + uint8_t uSegPrf : 3; + /** The address size prefix. */ + bool fAddrSizePrf : 1; + /** The operand size prefix. */ + bool fOpSizePrf : 1; + /** The REX.W prefix value. */ + bool fRexW : 1; + /** The REX.R prefix value. */ + bool fRexR : 1; + /** The REX.X prefix value. */ + bool fRexX : 1; + /** The REX.B prefix value. */ + bool fRexB : 1; + /** Set if a REX prefix is required with or without flags (for byte regs). */ + bool fRex : 1; + /** Use VEX encoding. */ + bool fVex : 1; + /** Use EVEX encoding. */ + bool fEvex : 1; + /** Indicator: Effective addressing mode in bytes (2, 4, 8). */ + uint8_t cbAddrMode : 4; + /** Indicator: Set if there is an operand accessing memory. */ + bool fHasMemoryOperand : 1; + /** Indicator: Set if a register is used in two or more operands, and one of + * them being for addressing. */ + bool fHasRegCollisionMem : 1; + /** Indicator: Helper indicator for tracking SIB.BASE collision. */ + bool fHasRegCollisionMemBase : 1; + /** Indicator: Helper indicator for tracking SIB.INDEX collision. */ + bool fHasRegCollisionMemIndex : 1; + /** Indicator: Set if a register is used directly in more than one operand. */ + bool fHasRegCollisionDirect : 1; + + /** Indicator: Set if MODRM.REG is the stack register. */ + bool fHasStackRegInMrmReg : 1; + /** Indicator: Set if MODRM.RM or SIB.BASE is the stack register. */ + bool fHasStackRegInMrmRmBase: 1; + + /** Indicator: High byte-register specified by MODRM.REG. */ + bool fHasHighByteRegInMrmReg : 1; + /** Indicator: High byte-register specified by MODRM.RM. */ + bool fHasHighByteRegInMrmRm : 1; + /** Indicator: Set if REX prefixes are incompatible with the byte-register + * specified by MODRM.REG. */ + bool fNoRexPrefixMrmReg : 1; + /** Indicator: Set if REX prefixes are incompatible with the byte-register + * specified by MODRM.RM. */ + bool fNoRexPrefixMrmRm : 1; + /** Indicator: fNoRexPrefixMrmReg || fNoRexPrefixMrmMr. */ + bool fNoRexPrefix : 1; + /** The MOD R/M byte we're working on (if fUsesModRm is set). */ + uint8_t bModRm; + /** The SIB/VSIB byte we're working on (if fSib is set). */ + uint8_t bSib; + /** @} */ + + /** The effective instruction address. (See InCtx.rip and InCtx.cs for the + * rest of the instruction addressing stuff.) */ + uint64_t uInstrEffAddr; + + /** Operand information, mainly for the FNCIDETSETUPINOUT and similar. */ + struct + { + /** The operand flags copied from (CIDETINSTR::afOperands). */ + uint32_t fFlags; + /** The encoded register number, if register, UINT8_MAX if not. */ + uint8_t iReg; + /** The actual operand size (encoded). */ + uint8_t cb; + /** Set if immediate value. */ + bool fIsImmediate : 1; + /** Set if memory access. */ + bool fIsMem : 1; + /** Set if addressing is relative to RIP. */ + bool fIsRipRelative : 1; + /** Set if it's a high byte register. */ + bool fIsHighByteRegister : 1; + /** Size of the disposition, 0 if none. */ + uint8_t cbMemDisp; + /** Base register, UINT8_MAX if not applicable. */ + uint8_t iMemBaseReg; + /** Index register, UINT8_MAX if not applicable. */ + uint8_t iMemIndexReg; + /** Index register, 1 if not applicable. */ + uint8_t uMemScale; + /** Effective segment register, UINT8_MAX if not memory access. */ + uint8_t iEffSeg; + /** Segment offset if memory access. Undefined if not memory access. */ + uint64_t offSeg; + /** The effective address if memory access. */ + uint64_t uEffAddr; + /** Immediate or displacement value. */ + uint64_t uImmDispValue; + /** Base register value, undefined if irrelevant. */ + uint64_t uMemBaseRegValue; + /** Index register value, undefined if irrelevant. */ + uint64_t uMemIndexRegValue; + /** Points to where the input data for this operand should be placed, + * when possible. In the fIsMem = true case, it either points directly + * to the input buffer or to a temporary one. While in the other case, + * it'll point into InCtx when possible. */ + RTPTRUNION In; + /** Points to where the expected output data for this operand should be + * stored, when possible. In the fIsMem = false case, it'll point into + * ExpectedCtx when possible. */ + RTPTRUNION Expected; + /** Pointer to the data buffer for this operand. */ + PCIDETBUF pDataBuf; + } aOperands[4]; + + /** Buffer where we assemble the instruction. */ + uint8_t abInstr[45]; + /** The size of the instruction in abInstr. */ + uint8_t cbInstr; + /** Offset of the instruction into the buffer. */ + uint16_t offInstr; + /** Current code buffer. */ + CIDETBUF CodeBuf; + + /** The input context. Initalized by driver and FNCIDETSETUPINOUT. */ + CIDETCPUCTX InCtx; + /** The expected output context. */ + CIDETCPUCTX ExpectedCtx; + /** The actual output context. */ + CIDETCPUCTX ActualCtx; + /** Template input context, initialized when setting the mode. */ + CIDETCPUCTX InTemplateCtx; + + /** Input and expected output temporary memory buffers. */ + uint8_t abBuf[0x2000]; + + + /** Number of skipped tests because of pfnSetupInOut failures. */ + uint32_t cSkippedSetupInOut; + /** Number of skipped tests because of pfnReInitDataBuf failures. */ + uint32_t cSkippedReInitDataBuf; + /** Number of skipped tests because of pfnSetupDataBuf failures. */ + uint32_t cSkippedSetupDataBuf; + /** Number of skipped tests because RIP relative addressing constraints. */ + uint32_t cSkippedDataBufWrtRip; + /** Number of skipped tests because of assemble failures. */ + uint32_t cSkippedAssemble; + /** Number of skipped tests because of pfnReInitCodeBuf failures. */ + uint32_t cSkippedReInitCodeBuf; + /** Number of skipped tests because of pfnSetupCodeBuf failures. */ + uint32_t cSkippedSetupCodeBuf; + /** Number of skipped tests because the base and index registers are the same + * one and there was a remainder when trying to point to the data buffer. */ + uint32_t cSkippedSameBaseIndexRemainder; + /** Number of skipped tests because index-only addressing left a remainder. */ + uint32_t cSkippedOnlyIndexRemainder; + /** Number of skipped tests because of direct addressing overflowed. */ + uint32_t cSkippedDirectAddressingOverflow; + + +} CIDETCORE; +/** Pointer to the CIDET core state. */ +typedef CIDETCORE *PCIDETCORE; + +/** Magic number for CIDETCORE (Lee Konitz). */ +#define CIDETCORE_MAGIC UINT32_C(0x19271013) + + +int CidetCoreInit(PCIDETCORE pThis, RTRAND hRand); +void CidetCoreDelete(PCIDETCORE pThis); +int CidetCoreSetTargetMode(PCIDETCORE pThis, uint8_t bMode); +uint32_t CidetCoreGetOperandSize(PCIDETCORE pThis, uint8_t iOp); +bool CidetCoreTestInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr); + + +extern const CIDETINSTR g_aCidetInstructions1[]; +extern const uint32_t g_cCidetInstructions1; + +#endif /* !VBOX_INCLUDED_SRC_cpu_cidet_h */ + diff --git a/src/VBox/ValidationKit/utils/cpu/cidet.mac b/src/VBox/ValidationKit/utils/cpu/cidet.mac new file mode 100644 index 00000000..d55333df --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet.mac @@ -0,0 +1,75 @@ +; $Id: cidet.mac $ ; +;; @file +; CPU Instruction Decoding & Execution Tests - Assembly Header. +; + +; +; 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 +; + + +%ifndef ___cidet_mac___ +%define ___cidet_mac___ + +struc CIDETCPUCTX + .rip resq 1 + .rfl resq 1 + .aGRegs resq 16 + .aSRegs resw 6 + +%ifndef CIDET_REDUCED_CTX + .tr resw 1 + .ldtr resw 1 + .cr0 resq 1 +%else + .au16Padding resw 2 +%endif + .cr2 resq 1 +%ifndef CIDET_REDUCED_CTX + .cr3 resq 1 + .cr4 resq 1 + .cr8 resq 1 + .dr0 resq 1 + .dr1 resq 1 + .dr2 resq 1 + .dr3 resq 1 + .dr6 resq 1 + .dr7 resq 1 +%endif + + .uErr resq 1 + .uXcpt resd 1 + + .fIgnoredRFlags resd 1 + .fTrickyStack resb 1 +endstruc + +%endif + diff --git a/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp b/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp new file mode 100644 index 00000000..889cb600 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp @@ -0,0 +1,223 @@ +/* $Id: cpu-alloc-all-mem.cpp $ */ +/** @file + * Allocate all memory we can get and then quit. + */ + +/* + * Copyright (C) 2011-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 <iprt/test.h> + +#include <iprt/asm.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/time.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct TSTALLOC +{ + /** The page sequence number. */ + size_t iPageSeq; + /** The allocation sequence number. */ + size_t iAllocSeq; + /** The allocation size. */ + size_t cb; + /** Pointer to the ourselves (paranoid). */ + void *pv; + /** Linked list node. */ + RTLISTNODE Node; + +} TSTALLOC; +typedef TSTALLOC *PTSTALLOC; + + +static bool checkList(PRTLISTNODE pHead) +{ + size_t iPageSeq = 0; + size_t iAllocSeq = 0; + PTSTALLOC pCur; + RTListForEach(pHead, pCur, TSTALLOC, Node) + { + RTTESTI_CHECK_RET(pCur->iAllocSeq == iAllocSeq, false); + RTTESTI_CHECK_RET(pCur->pv == pCur, false); + + size_t const *pu = (size_t const *)pCur; + size_t const *puEnd = pu + pCur->cb / sizeof(size_t); + while (pu != puEnd) + { + RTTESTI_CHECK_RET(*pu == iPageSeq, false); + iPageSeq++; + pu += PAGE_SIZE / sizeof(size_t); + } + iAllocSeq++; + } + return true; +} + + +static void doTest(RTTEST hTest) +{ + RTTestSub(hTest, "Allocate all memory"); + + RTLISTANCHOR AllocHead; + PTSTALLOC pCur; + uint64_t cNsElapsed = 0; + size_t cbPrint = 0; + uint64_t uPrintTS = 0; + size_t cbTotal = 0; +#if ARCH_BITS == 64 + size_t const cbOneStart = 64 * _1M; + size_t const cbOneMin = 4 * _1M; +#else + size_t const cbOneStart = 16 * _1M; + size_t const cbOneMin = 4 * _1M; +#endif + size_t cbOne = cbOneStart; + size_t cAllocs = 0; + uint32_t iPageSeq = 0; + RTListInit(&AllocHead); + + for (;;) + { + /* + * Allocate a chunk and make sure all the pages are there. + */ + uint64_t const uStartTS = RTTimeNanoTS(); + pCur = (PTSTALLOC)RTMemPageAlloc(cbOne); + if (pCur) + { + size_t *pu = (size_t *)pCur; + size_t *puEnd = pu + cbOne / sizeof(size_t); + while (pu != puEnd) + { + *pu = iPageSeq++; + pu += PAGE_SIZE / sizeof(size_t); + } + uint64_t const uEndTS = RTTimeNanoTS(); + uint64_t const cNsThis = uEndTS - uStartTS; + + /* + * Update the statistics. + */ + cNsElapsed += cNsThis; + cbTotal += cbOne; + cAllocs++; + + /* + * Link the allocation. + */ + pCur->iAllocSeq = cAllocs - 1; + pCur->pv = pCur; + pCur->cb = cbOne; + RTListAppend(&AllocHead, &pCur->Node); + + /* + * Print progress info? + */ + if ( uEndTS - uPrintTS >= RT_NS_1SEC_64*10 +#if ARCH_BITS == 64 + || cbTotal - cbPrint >= _4G +#else + || cbTotal - cbPrint >= _2G +#endif + ) + { + cbPrint = cbTotal; + uPrintTS = uEndTS; + + uint32_t cMBPerSec = (uint32_t)((long double)cbTotal / ((long double)cNsElapsed / RT_NS_1SEC) / _1M); + RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%'zu bytes in %'llu ns - %'u MB/s\n", + cbTotal, cNsElapsed, cMBPerSec); + RTTESTI_CHECK_RETV(checkList(&AllocHead)); + } + } + else + { + /* + * Try again with a smaller request. + */ + RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Failed to allocate %'zu bytes (after %'zu bytes)\n", cbOne, cbTotal); + if (cbOne <= cbOneMin) + break; + cbOne = cbOneMin; + } + } + + RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Verifying...\n"); + RTTESTI_CHECK_RETV(checkList(&AllocHead)); + RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "... detected no corruption.\n"); + + /* + * Free up some memory before displaying the results. + */ + size_t i = 0; + PTSTALLOC pPrev; + RTListForEachReverseSafe(&AllocHead, pCur, pPrev, TSTALLOC, Node) + { + RTMemPageFree(pCur->pv, pCur->cb); + if (++i > 32) + break; + } + + RTTestValue(hTest, "amount", cbTotal, RTTESTUNIT_BYTES); + RTTestValue(hTest, "time", cNsElapsed, RTTESTUNIT_NS); + uint32_t cMBPerSec = (uint32_t)((long double)cbTotal / ((long double)cNsElapsed / RT_NS_1SEC) / _1M); + RTTestValue(hTest, "speed", cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC); + RTTestSubDone(hTest); +} + + +int main(int argc, char **argv) +{ + RTTEST hTest; + RTEXITCODE rcExit = RTTestInitAndCreate("memallocall", &hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(hTest); + + NOREF(argv); + if (argc == 1) + doTest(hTest); + else + RTTestFailed(hTest, "This test takes no arguments!"); + + return RTTestSummaryAndDestroy(hTest); +} + diff --git a/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp b/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp new file mode 100644 index 00000000..1331c3ce --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp @@ -0,0 +1,205 @@ +/* $Id: cpu-numa.cpp $ */ +/** @file + * numa - NUMA / memory benchmark. + */ + +/* + * Copyright (C) 2011-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 <iprt/test.h> + +#include <iprt/asm.h> +//#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) +//# include <iprt/asm-amd64-x86.h> +//#endif +#include <iprt/mem.h> +#include <iprt/mp.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The number of threads to skip when testing. */ +static uint32_t g_cThreadsToSkip = 1; + +/** + * Gets the next online CPU. + * + * @returns Next CPU index or RTCPUSET_MAX_CPUS. + * @param iCurCpu The current CPU (index). + */ +static int getNextCpu(unsigned iCurCpu) +{ + /* Skip to the next chip. */ + iCurCpu = (iCurCpu / g_cThreadsToSkip) * g_cThreadsToSkip; + iCurCpu += g_cThreadsToSkip; + + /* Skip offline cpus. */ + while ( iCurCpu < RTCPUSET_MAX_CPUS + && !RTMpIsCpuOnline(iCurCpu) ) + iCurCpu++; + + /* Make sure we're within bounds (in case of bad input). */ + if (iCurCpu > RTCPUSET_MAX_CPUS) + iCurCpu = RTCPUSET_MAX_CPUS; + return iCurCpu; +} + + +static void doTest(RTTEST hTest) +{ + NOREF(hTest); + uint32_t iAllocCpu = 0; + while (iAllocCpu < RTCPUSET_MAX_CPUS) + { + const uint32_t cbTestSet = _1M * 32; + const uint32_t cIterations = 384; + + /* + * Change CPU and allocate a chunk of memory. + */ + RTTESTI_CHECK_RC_OK_RETV(RTThreadSetAffinityToCpu(RTMpCpuIdFromSetIndex(iAllocCpu))); + + void *pvTest = RTMemPageAlloc(cbTestSet); /* may be leaked, who cares */ + RTTESTI_CHECK_RETV(pvTest != NULL); + memset(pvTest, 0xef, cbTestSet); + + /* + * Do the tests. + */ + uint32_t iAccessCpu = 0; + while (iAccessCpu < RTCPUSET_MAX_CPUS) + { + RTTESTI_CHECK_RC_OK_RETV(RTThreadSetAffinityToCpu(RTMpCpuIdFromSetIndex(iAccessCpu))); + + /* + * The write test. + */ + RTTimeNanoTS(); RTThreadYield(); + uint64_t u64StartTS = RTTimeNanoTS(); + for (uint32_t i = 0; i < cIterations; i++) + { + ASMCompilerBarrier(); /* paranoia */ + memset(pvTest, i, cbTestSet); + } + uint64_t const cNsElapsedWrite = RTTimeNanoTS() - u64StartTS; + uint64_t cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */ + / ((long double)cNsElapsedWrite / RT_NS_1SEC_64) /* seconds */ + / _1M /* MB */ ); + RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-write", iAllocCpu, iAccessCpu); + + /* + * The read test. + */ + memset(pvTest, 0, cbTestSet); + RTTimeNanoTS(); RTThreadYield(); + u64StartTS = RTTimeNanoTS(); + for (uint32_t i = 0; i < cIterations; i++) + { +#if 1 + size_t u = 0; + size_t volatile *puCur = (size_t volatile *)pvTest; + size_t volatile *puEnd = puCur + cbTestSet / sizeof(size_t); + while (puCur != puEnd) + u += *puCur++; +#else + ASMCompilerBarrier(); /* paranoia */ + void *pvFound = memchr(pvTest, (i & 127) + 1, cbTestSet); + RTTESTI_CHECK(pvFound == NULL); +#endif + } + uint64_t const cNsElapsedRead = RTTimeNanoTS() - u64StartTS; + cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */ + / ((long double)cNsElapsedRead / RT_NS_1SEC_64) /* seconds */ + / _1M /* MB */ ); + RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-read", iAllocCpu, iAccessCpu); + + /* + * The read/write test. + */ + RTTimeNanoTS(); RTThreadYield(); + u64StartTS = RTTimeNanoTS(); + for (uint32_t i = 0; i < cIterations; i++) + { + ASMCompilerBarrier(); /* paranoia */ + memcpy(pvTest, (uint8_t *)pvTest + cbTestSet / 2, cbTestSet / 2); + } + uint64_t const cNsElapsedRW = RTTimeNanoTS() - u64StartTS; + cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */ + / ((long double)cNsElapsedRW / RT_NS_1SEC_64) /* seconds */ + / _1M /* MB */ ); + RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-read-write", iAllocCpu, iAccessCpu); + + /* + * Total time. + */ + RTTestIValueF(cNsElapsedRead + cNsElapsedWrite + cNsElapsedRW, RTTESTUNIT_NS, + "cpu%02u-mem%02u-time", iAllocCpu, iAccessCpu); + + /* advance */ + iAccessCpu = getNextCpu(iAccessCpu); + } + + /* + * Clean up and advance to the next CPU. + */ + RTMemPageFree(pvTest, cbTestSet); + iAllocCpu = getNextCpu(iAllocCpu); + } +} + + +int main(int argc, char **argv) +{ + RTTEST hTest; + RTEXITCODE rcExit = RTTestInitAndCreate("numa-1", &hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(hTest); + +#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) + /** @todo figure basic topology. */ +#endif + if (argc == 2) + g_cThreadsToSkip = RTStrToUInt8(argv[1]); + + doTest(hTest); + + return RTTestSummaryAndDestroy(hTest); +} + diff --git a/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm b/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm new file mode 100644 index 00000000..d96194ad --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm @@ -0,0 +1,160 @@ +; $Id: exceptionsR3-asm.asm $ +;; @file +; exceptionsR3-asm.asm - assembly helpers. +; + +; +; Copyright (C) 2009-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 "iprt/asmdefs.mac" + + +;******************************************************************************* +;* Defined Constants And Macros * +;******************************************************************************* +%ifdef RT_ARCH_AMD64 + %define TST_XCPT_MAGIC 0123456789abcdef0h +%else + %define TST_XCPT_MAGIC 012345678h +%endif + +%macro tstXcptAsmProlog 0 + push xBP + push xDI + push xSI + push xBX + %ifdef RT_ARCH_X86 + push gs + push fs + push es + push ds + %endif + %ifdef RT_ARCH_AMD64 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + %endif + + mov xAX, TST_XCPT_MAGIC + mov xBX, xAX + mov xCX, xAX + mov xDX, xAX + mov xDI, xAX + mov xSI, xAX + mov xBP, xAX + %ifdef RT_ARCH_AMD64 + mov r8, xAX + mov r9, xAX + mov r10, xAX + mov r11, xAX + mov r12, xAX + mov r13, xAX + mov r14, xAX + mov r15, xAX + %endif +%endmacro + +%macro tstXcptAsmEpilog 0 + %ifdef RT_ARCH_AMD64 + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + %endif + %ifdef RT_ARCH_X86 + pop ds + pop es + pop fs + pop gs + %endif + pop xBX + pop xSI + pop xDI + pop xBP +%endmacro + + +BEGINCODE + +;; +BEGINPROC tstXcptAsmNullPtrRead +; tstXcptAsmProlog + xor eax, eax +GLOBALNAME tstXcptAsmNullPtrRead_PC + mov al, [xAX] +; tstXcptAsmEpilog + ret +ENDPROC tstXcptAsmNullPtrRead + + +;; +BEGINPROC tstXcptAsmNullPtrWrite + tstXcptAsmProlog + xor eax, eax +GLOBALNAME tstXcptAsmNullPtrWrite_PC + mov [xAX], al + tstXcptAsmEpilog + ret +ENDPROC tstXcptAsmNullPtrWrite + + +;; +BEGINPROC tstXcptAsmSysCall + tstXcptAsmProlog +GLOBALNAME tstXcptAsmSysCall_PC + syscall + tstXcptAsmEpilog + ret +ENDPROC tstXcptAsmSysCall + + +;; +BEGINPROC tstXcptAsmSysEnter + tstXcptAsmProlog +GLOBALNAME tstXcptAsmSysEnter_PC +%ifdef RT_ARCH_AMD64 + db 00fh, 034h ; test this on 64-bit, yasm complains... +%else + sysenter +%endif + tstXcptAsmEpilog + ret +ENDPROC tstXcptAsmSysEnter + diff --git a/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp b/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp new file mode 100644 index 00000000..474e0eeb --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp @@ -0,0 +1,272 @@ +/* $Id: exceptionsR3.cpp $ */ +/** @file + * exceptionsR3 - Tests various ring-3 CPU exceptions. + */ + +/* + * Copyright (C) 2009-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 <iprt/cdefs.h> +#include <iprt/ctype.h> +#include <iprt/getopt.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/test.h> +#include <iprt/x86.h> + +#include <setjmp.h> + +#ifndef RT_OS_WINDOWS +# define USE_SIGNALS +# include <signal.h> +# include <stdlib.h> +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Executes a simple test. */ +#define TST_XCPT(Trapper, iTrap, uErr) \ + do \ + { \ + RTTestISub(#Trapper); \ + tstXcptReset(); \ + if (!setjmp(g_JmpBuf)) \ + { \ + tstXcptAsm##Trapper(); \ + RTTestIFailed("%s didn't trap (line no %u)", #Trapper, __LINE__); \ + } \ + else if ( (iTrap) != tstXcptCurTrap() \ + || (uErr) != tstXcptCurErr() ) \ + RTTestIFailed("%s trapped with %#x/%#x, expected %#x/%#x (line no %u)", \ + #Trapper, tstXcptCurTrap(), tstXcptCurErr(), (iTrap), (uErr), __LINE__); \ + else \ + RTTestISubDone(); \ + } while (0) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Where to longjmp to when getting a signal/exception. */ +jmp_buf g_JmpBuf; +#ifdef USE_SIGNALS +/** Pending signal. + * -1 if no signal is pending. */ +int32_t volatile g_iSignal; +/** Pending signal info. */ +siginfo_t volatile g_SigInfo; +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +DECLASM(void) tstXcptAsmNullPtrRead(void); +DECLASM(void) tstXcptAsmNullPtrWrite(void); +DECLASM(void) tstXcptAsmSysEnter(void); +DECLASM(void) tstXcptAsmSysCall(void); + + + +#ifdef USE_SIGNALS +/** + * Generic signal handler. + */ +static void tstXcptSigHandler(int iSignal, siginfo_t *pSigInfo, void *pvCtx) +{ +#if 1 + 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 + if (g_iSignal == -1) + { + g_iSignal = iSignal; + if (pSigInfo) + memcpy((void *)&g_SigInfo, pSigInfo, sizeof(g_SigInfo)); + longjmp(g_JmpBuf, 1); + } + else + { + /* we're up the infamous creek... */ + _Exit(2); + } +} + +#elif defined(RT_OS_WINDOWS) +/** @todo */ +//# error "PORTME" + +#else +# error "PORTME" +#endif + + +/** Reset the current exception state and get ready for a new trap. */ +static void tstXcptReset(void) +{ +#ifdef USE_SIGNALS + g_iSignal = -1; + memset((void *)&g_SigInfo, 0, sizeof(g_SigInfo)); +#endif +} + + + +/** Get the current intel trap number. Returns -1 if none. */ +static int tstXcptCurTrap(void) +{ +#ifdef USE_SIGNALS + /** @todo this is just a quick sketch. */ + switch (g_iSignal) + { + case SIGBUS: +# ifdef RT_OS_DARWIN + if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/) + return X86_XCPT_PF; +# endif + return X86_XCPT_GP; + + case SIGSEGV: + return X86_XCPT_GP; + } +#endif + return -1; +} + + +/** Get the exception error code if applicable. */ +static uint32_t tstXcptCurErr(void) +{ +#ifdef USE_SIGNALS + /** @todo this is just a quick sketch. */ + switch (g_iSignal) + { + case SIGBUS: +# ifdef RT_OS_DARWIN + if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/) + return 0; +# endif + break; + + case SIGSEGV: + break; + } +#endif + return UINT32_MAX; +} + + +int main(int argc, char **argv) +{ + /* + * Prolog. + */ + RTTEST hTest; + int rc = RTTestInitAndCreate("exceptionsR3", &hTest); + if (rc) + return rc; + + /* + * Parse options. + */ + bool volatile fRawMode = false; + static const RTGETOPTDEF s_aOptions[] = + { + { "--raw-mode", 'r', RTGETOPT_REQ_NOTHING }, + }; + + RTGETOPTUNION ValUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + while ((rc = RTGetOpt(&GetState, &ValUnion))) + { + switch (rc) + { + case 'r': + fRawMode = true; + break; + + default: + return RTGetOptPrintError(rc, &ValUnion); + } + } + + /* + * Test setup. + */ +#ifdef USE_SIGNALS + struct sigaction Act; + RT_ZERO(Act); + Act.sa_sigaction = tstXcptSigHandler; + Act.sa_flags = SA_SIGINFO; + sigfillset(&Act.sa_mask); + + 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); + +#else + /** @todo Implement this using structured exception handling on Windows and + * OS/2. */ +#endif + + /* + * The tests. + */ + RTTestBanner(hTest); + TST_XCPT(NullPtrRead, X86_XCPT_PF, 0); + TST_XCPT(NullPtrWrite, X86_XCPT_PF, 0); + if (fRawMode) + { + TST_XCPT(SysEnter, X86_XCPT_GP, 0); + TST_XCPT(SysCall, X86_XCPT_UD, 0); + } + + /* + * Epilog. + */ + return RTTestSummaryAndDestroy(hTest); +} + diff --git a/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm b/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm new file mode 100644 index 00000000..751c8808 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm @@ -0,0 +1,162 @@ +; $Id: rdtsc-asm.asm $ +;; @file +; RDTSC test, assembly code +; + +; +; Copyright (C) 2009-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 "iprt/asmdefs.mac" +%include "iprt/x86.mac" + + +;********************************************************************************************************************************* +;* Global Variables * +;********************************************************************************************************************************* +BEGINDATA +;; +; Where DoTscReads() returns the rdtsc values. +; +; @note The results are 32-bit value pairs in x86 mode and 64-bit pairs in +; AMD64 mode. +GLOBALNAME g_aRdTscResults +%ifdef RT_ARCH_AMD64 + dq 0, 0 + dq 0, 0 ; first value stored + dq 0, 0 + dq 0, 0 + dq 0, 0 + dq 0, 0 + dq 0, 0 +%else + dq 0, 0 + dd 0, 0 ; first value stored + dd 0, 0 + dd 0, 0 +%endif + + +BEGINCODE + +;; Takes no arguments, returns number of values read into g_aRdTscResults. +BEGINPROC DoTscReads + push xBP + mov xBP, xSP +%ifdef RT_ARCH_AMD64 + mov rax, 0feedfacecafebabeh + mov rdx, 0cafebabefeedfaceh + mov r8, 0deadbeefdeadbeefh + mov r9, 0deadbeefdeadbeefh + mov r10, 0deadbeefdeadbeefh + mov r11, 0deadbeefdeadbeefh + push rbx + push r12 + push r13 + push r14 + push r15 + + ; Read 6x TSC into registers. + rdtsc + mov r8, rax + mov r9, rdx + rdtsc + mov r10, rax + mov r11, rdx + rdtsc + mov r12, rax + mov r13, rdx + rdtsc + mov r14, rax + mov r15, rdx + rdtsc + mov rbx, rax + mov rcx, rdx + rdtsc + + ; Store the values (64-bit). + mov [NAME(g_aRdTscResults) + 10h xWrtRIP], r8 + mov [NAME(g_aRdTscResults) + 18h xWrtRIP], r9 + mov [NAME(g_aRdTscResults) + 20h xWrtRIP], r10 + mov [NAME(g_aRdTscResults) + 28h xWrtRIP], r11 + mov [NAME(g_aRdTscResults) + 30h xWrtRIP], r12 + mov [NAME(g_aRdTscResults) + 38h xWrtRIP], r13 + mov [NAME(g_aRdTscResults) + 40h xWrtRIP], r14 + mov [NAME(g_aRdTscResults) + 48h xWrtRIP], r15 + mov [NAME(g_aRdTscResults) + 50h xWrtRIP], rbx + mov [NAME(g_aRdTscResults) + 58h xWrtRIP], rcx + mov [NAME(g_aRdTscResults) + 60h xWrtRIP], rax + mov [NAME(g_aRdTscResults) + 68h xWrtRIP], rdx + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + + mov eax, 6 +%else + mov eax, 0feedfaceh + mov edx, 0cafebabeh + push esi + push edi + push ebx + + ; Read 3x TSC into registers. + rdtsc + mov ebx, eax + mov ecx, edx + rdtsc + mov esi, eax + mov edi, edx + rdtsc + + ; Store values. + mov [NAME(g_aRdTscResults) + 08h], ebx + mov [NAME(g_aRdTscResults) + 0ch], ecx + mov [NAME(g_aRdTscResults) + 10h], esi + mov [NAME(g_aRdTscResults) + 14h], edi + mov [NAME(g_aRdTscResults) + 18h], eax + mov [NAME(g_aRdTscResults) + 1ch], edx + + pop ebx + pop edi + pop esi + + mov eax, 3 +%endif + leave + ret +ENDPROC DoTscReads + diff --git a/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp b/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp new file mode 100644 index 00000000..81405f3d --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp @@ -0,0 +1,294 @@ +/* $Id: rdtsc.cpp $ */ +/** @file + * rdtsc - Test if three consecutive rdtsc instructions return different values. + */ + +/* + * Copyright (C) 2009-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 <iprt/errcore.h> +#include <iprt/initterm.h> +#include <iprt/message.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/time.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RDTSCRESULT +{ + RTCCUINTREG uLow, uHigh; +} RDTSCRESULT; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern "C" RDTSCRESULT g_aRdTscResults[]; /* rdtsc-asm.asm */ + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +/** + * Does 3 (32-bit) or 6 (64-bit) fast TSC reads and stores the result + * in g_aRdTscResults, starting with the 2nd entry. + * + * Starting the result storing at g_aRdTscResults[1] make it easy to do the + * comparisons in a loop. + * + * @returns Number of results read into g_aRdTscResults[1] and onwards. + */ +DECLASM(uint32_t) DoTscReads(void); + + + + +int main(int argc, char **argv) +{ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + /* + * Tunables. + */ + uint64_t offJumpThreshold = _4G * 2; + unsigned cMaxLoops = 10000000; + unsigned cStatusEvery = 2000000; + unsigned cMinSeconds = 0; + + for (int i = 1; i < argc; i++) + { + const char *psz = argv[i]; + if (*psz == '-') + { + psz++; + char chOpt; + while ((chOpt = *psz++) != '\0') + { + /* Option value. */ + const char *pszValue = NULL; + uint64_t uValue = 0; + switch (chOpt) + { + case 'l': + case 's': + case 'm': + if (*psz == '\0') + { + if (i + 1 >= argc) + return RTMsgSyntax("The %c option requires a value", chOpt); + pszValue = argv[++i]; + } + else + pszValue = psz + (*psz == ':' || *psz == '='); + switch (chOpt) + { + case 'l': + case 's': + case 'm': + { + char *pszNext = NULL; + rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &uValue); + if (RT_FAILURE(rc)) + return RTMsgSyntax("Bad number: %s (%Rrc)", pszValue, rc); + if (pszNext && *pszNext != '\0') + { + if (*pszNext == 'M'&& pszNext[1] == '\0') + uValue *= _1M; + else if (*pszNext == 'K' && pszNext[1] == '\0') + uValue *= _1K; + else if (*pszNext == 'G' && pszNext[1] == '\0') + uValue *= _1G; + else + return RTMsgSyntax("Bad value format for option %c: %s", chOpt, pszValue); + } + break; + } + } + break; + } + + /* handle the option. */ + switch (chOpt) + { + case 'l': + cMaxLoops = uValue; + break; + + case 'm': + cMinSeconds = uValue; + break; + + case 's': + cStatusEvery = uValue; + break; + + case 'h': + case '?': + RTPrintf("usage: rdtsc [-l <loops>] [-s <loops-between-status>]\n" + " [-m <minimum-seconds-to-run>]\n"); + return RTEXITCODE_SUCCESS; + + default: + return RTMsgSyntax("Unknown option %c (argument %d)\n", chOpt, i); + } + } + } + else + return RTMsgSyntax("argument %d (%s): not an option\n", i, psz); + } + + /* + * Do the job. + */ + uint64_t const nsTsStart = RTTimeNanoTS(); + unsigned cOuterLoops = 0; + unsigned cLoopsToNextStatus = cStatusEvery; + unsigned cRdTscInstructions = 0; + unsigned cBackwards = 0; + unsigned cSame = 0; + unsigned cBadValues = 0; + unsigned cJumps = 0; + uint64_t offMaxJump = 0; + uint64_t offMinIncr = UINT64_MAX; + uint64_t offMaxIncr = 0; + + g_aRdTscResults[0] = g_aRdTscResults[DoTscReads() - 1]; + + for (;;) + { + for (unsigned iLoop = 0; iLoop < cMaxLoops; iLoop++) + { + uint32_t const cResults = DoTscReads(); + cRdTscInstructions += cResults; + + for (uint32_t i = 0; i < cResults; i++) + { + uint64_t uPrev = RT_MAKE_U64((uint32_t)g_aRdTscResults[i ].uLow, (uint32_t)g_aRdTscResults[i ].uHigh); + uint64_t uCur = RT_MAKE_U64((uint32_t)g_aRdTscResults[i + 1].uLow, (uint32_t)g_aRdTscResults[i + 1].uHigh); + if (RT_LIKELY(uCur != uPrev)) + { + int64_t offDelta = uCur - uPrev; + if (RT_LIKELY(offDelta >= 0)) + { + if (RT_LIKELY((uint64_t)offDelta < offJumpThreshold)) + { + if ((uint64_t)offDelta < offMinIncr) + offMinIncr = offDelta; + if ((uint64_t)offDelta > offMaxIncr && i != 0) + offMaxIncr = offDelta; + } + else + { + cJumps++; + if ((uint64_t)offDelta > offMaxJump) + offMaxJump = offDelta; + RTPrintf("%u/%u: Jump: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop, + (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow, + (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow); + } + } + else + { + cBackwards++; + RTPrintf("%u/%u: Back: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop, + (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow, + (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow); + } + } + else + { + cSame++; + RTPrintf("%u/%u: Same: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop, + (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow, + (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow); + } +#if ARCH_BITS == 64 + if ((g_aRdTscResults[i + 1].uLow >> 32) || (g_aRdTscResults[i + 1].uHigh >> 32)) + cBadValues++; +#endif + } + + /* Copy the last value for the next iteration. */ + g_aRdTscResults[0] = g_aRdTscResults[cResults]; + + /* Display status. */ + if (RT_LIKELY(--cLoopsToNextStatus > 0)) + { /* likely */ } + else + { + cLoopsToNextStatus = cStatusEvery; + RTPrintf("%u/%u: %#010x`%08x\n", cOuterLoops, iLoop, + (unsigned)g_aRdTscResults[cResults].uHigh, (unsigned)g_aRdTscResults[cResults].uLow); + } + } + + /* + * Check minimum number of seconds. + */ + cOuterLoops++; + if (!cMinSeconds) + break; + uint64_t nsElapsed = RTTimeNanoTS() - nsTsStart; + if (nsElapsed >= cMinSeconds * RT_NS_1SEC_64) + break; + } + + /* + * Summary. + */ + if (cBackwards == 0 && cSame == 0 && cJumps == 0 && cBadValues == 0) + { + RTPrintf("rdtsc: Success (%u RDTSC over %u*%u loops, deltas: %#x`%08x..%#x`%08x)\n", + cRdTscInstructions, cOuterLoops, cMaxLoops, + (unsigned)(offMinIncr >> 32), (unsigned)offMinIncr, (unsigned)(offMaxIncr >> 32), (unsigned)offMaxIncr); + return RTEXITCODE_SUCCESS; + } + RTPrintf("RDTSC instructions: %u\n", cRdTscInstructions); + RTPrintf("Loops: %u * %u => %u\n", cMaxLoops, cOuterLoops, cOuterLoops * cMaxLoops); + RTPrintf("Backwards: %u\n", cBackwards); + RTPrintf("Jumps: %u\n", cJumps); + RTPrintf("Max jumps: %#010x`%08x\n", (unsigned)(offMaxJump >> 32), (unsigned)offMaxJump); + RTPrintf("Same value: %u\n", cSame); + RTPrintf("Bad values: %u\n", cBadValues); + RTPrintf("Min increment: %#010x`%08x\n", (unsigned)(offMinIncr >> 32), (unsigned)offMinIncr); + RTPrintf("Max increment: %#010x`%08x\n", (unsigned)(offMaxIncr >> 32), (unsigned)offMaxIncr); + return RTEXITCODE_FAILURE; +} + diff --git a/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm b/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm new file mode 100644 index 00000000..49a29e7a --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm @@ -0,0 +1,162 @@ +; $Id: xmmsaving-asm.asm $ +;; @file +; xmmsaving - assembly helpers. +; + +; +; Copyright (C) 2009-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 +; + + +%include "iprt/asmdefs.mac" +%include "VBox/vmm/stam.mac" + + +BEGINCODE + + +;; +; DECLASM(int) XmmSavingTestLoadSet(const MYXMMREGSET *pSet, const MYXMMREGSET *pPrevSet, PRTUINT128U pBadVal); +; +; @returns 0 on success, 1-based register number on failure. +; @param pSet The new set. +; @param pPrevSet The previous set. Can be NULL. +; @param pBadVal Where to store the actual register value on failure. +; +BEGINPROC XmmSavingTestLoadSet + push xBP + mov xBP, xSP + sub xSP, 32 ; Space for storing an XMM register (in TEST_REG). + and xSP, ~31 ; Align it. + + ; Unify register/arguments. +%ifdef ASM_CALL64_GCC + mov r8, rdx ; pBadVal + mov xCX, rdi ; pSet + mov xDX, rsi ; pPrevSet +%endif +%ifdef RT_ARCH_X86 + mov xCX, [ebp + 8] ; pSet + mov xDX, [ebp + 12] ; pPrevSet +%endif + + test xDX, xDX + jz near .just_load + + ; Check that the old set is still correct. +%macro TEST_REG 1, + movdqa [xSP], xmm %+ %1 + mov xAX, [xDX + %1 * 8] + cmp [xSP], xAX + jne %%bad + mov xAX, [xDX + %1 * 8 + xCB] + cmp [xSP + xCB], xAX +%ifdef RT_ARCH_X86 + jne %%bad + mov xAX, [xDX + %1 * 8 + xCB*2] + cmp [xSP + xCB*2], xAX + jne %%bad + mov xAX, [xDX + %1 * 8 + xCB*3] + cmp [xSP + xCB*3], xAX +%endif + je %%next +%%bad: + mov eax, %1 + 1 + jmp .return_copy_badval +%%next: +%endmacro + + TEST_REG 0 + TEST_REG 1 + TEST_REG 2 + TEST_REG 3 + TEST_REG 4 + TEST_REG 5 + TEST_REG 6 + TEST_REG 7 +%ifdef RT_ARCH_AMD64 + TEST_REG 8 + TEST_REG 9 + TEST_REG 10 + TEST_REG 11 + TEST_REG 12 + TEST_REG 13 + TEST_REG 14 + TEST_REG 15 +%endif + + ; Load the new state. +.just_load: + movdqu xmm0, [xCX + 0*8] + movdqu xmm1, [xCX + 1*8] + movdqu xmm2, [xCX + 2*8] + movdqu xmm3, [xCX + 3*8] + movdqu xmm4, [xCX + 4*8] + movdqu xmm5, [xCX + 5*8] + movdqu xmm6, [xCX + 6*8] + movdqu xmm7, [xCX + 7*8] +%ifdef RT_ARCH_AMD64 + movdqu xmm8, [xCX + 8*8] + movdqu xmm9, [xCX + 9*8] + movdqu xmm10, [xCX + 10*8] + movdqu xmm11, [xCX + 11*8] + movdqu xmm12, [xCX + 12*8] + movdqu xmm13, [xCX + 13*8] + movdqu xmm14, [xCX + 14*8] + movdqu xmm15, [xCX + 15*8] +%endif + xor eax, eax + jmp .return + +.return_copy_badval: + ; don't touch eax here. +%ifdef RT_ARCH_X86 + mov edx, [ebp + 16] + mov ecx, [esp] + mov [edx ], ecx + mov ecx, [esp + 4] + mov [edx + 4], ecx + mov ecx, [esp + 8] + mov [edx + 8], ecx + mov ecx, [esp + 12] + mov [edx + 12], ecx +%else + mov rdx, [rsp] + mov rcx, [rsp + 8] + mov [r8], rdx + mov [r8 + 8], rcx +%endif + jmp .return + +.return: + leave + ret +ENDPROC XmmSavingTestLoadSet + diff --git a/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp b/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp new file mode 100644 index 00000000..a8d377f8 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp @@ -0,0 +1,130 @@ +/* $Id: xmmsaving.cpp $ */ +/** @file + * xmmsaving - Test that all XMM register state is handled correctly and + * not corrupted the VMM. + */ + +/* + * Copyright (C) 2009-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 <iprt/test.h> +#include <iprt/x86.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct MYXMMREGSET +{ + RTUINT128U aRegs[16]; +} MYXMMREGSET; + + +DECLASM(int) XmmSavingTestLoadSet(const MYXMMREGSET *pSet, const MYXMMREGSET *pPrevSet, PRTUINT128U pBadVal); + + +static void XmmSavingTest(void) +{ + RTTestISub("xmm saving and restoring"); + + /* Create the test sets. */ + static MYXMMREGSET s_aSets[256]; + for (unsigned s = 0; s < RT_ELEMENTS(s_aSets); s++) + { + for (unsigned r = 0; r < RT_ELEMENTS(s_aSets[s].aRegs); r++) + { + unsigned x = (s << 4) | r; + s_aSets[s].aRegs[r].au32[0] = x | UINT32_C(0x12345000); + s_aSets[s].aRegs[r].au32[1] = (x << 8) | UINT32_C(0x88700011); + s_aSets[s].aRegs[r].au32[2] = (x << 16) | UINT32_C(0xe000dcba); + s_aSets[s].aRegs[r].au32[3] = (x << 20) | UINT32_C(0x00087654); + } + } + + /* Do the actual testing. */ + const MYXMMREGSET *pPrev2 = NULL; + const MYXMMREGSET *pPrev = NULL; + for (int i = 0; i < 1000000; i++) + { + if ((i % 50000) == 0) + { + RTTestIPrintf(RTTESTLVL_ALWAYS, "."); + pPrev = pPrev2 = NULL; /* May be trashed by the above call. */ + } + for (unsigned s = 0; s < RT_ELEMENTS(s_aSets); s++) + { + RTUINT128U BadVal; + const MYXMMREGSET *pSet = &s_aSets[s]; + int r = XmmSavingTestLoadSet(pSet, pPrev, &BadVal); + if (r-- != 0) + { + RTTestIFailed("i=%d s=%d r=%d", i, s, r); + RTTestIFailureDetails("XMM%-2d = %08x,%08x,%08x,%08x\n", + r, + BadVal.au32[0], + BadVal.au32[1], + BadVal.au32[2], + BadVal.au32[3]); + RTTestIFailureDetails("Expected %08x,%08x,%08x,%08x\n", + pPrev->aRegs[r].au32[0], + pPrev->aRegs[r].au32[1], + pPrev->aRegs[r].au32[2], + pPrev->aRegs[r].au32[3]); + if (pPrev2) + RTTestIFailureDetails("PrevPrev %08x,%08x,%08x,%08x\n", + pPrev2->aRegs[r].au32[0], + pPrev2->aRegs[r].au32[1], + pPrev2->aRegs[r].au32[2], + pPrev2->aRegs[r].au32[3]); + return; + } + pPrev2 = pPrev; + pPrev = pSet; + } + } + RTTestISubDone(); +} + + +int main() +{ + RTTEST hTest; + int rc = RTTestInitAndCreate("xmmsaving", &hTest); + if (rc) + return rc; + XmmSavingTest(); + return RTTestSummaryAndDestroy(hTest); +} + |