summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/utils/cpu
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/ValidationKit/utils/cpu
parentInitial commit. (diff)
downloadvirtualbox-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.kmk84
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-app.cpp1376
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-appA.asm319
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-core.cpp2368
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp297
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet.h1092
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet.mac75
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp223
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp205
-rw-r--r--src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm160
-rw-r--r--src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp272
-rw-r--r--src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm162
-rw-r--r--src/VBox/ValidationKit/utils/cpu/rdtsc.cpp294
-rw-r--r--src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm162
-rw-r--r--src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp130
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);
+}
+