summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/testcase/tstMicro.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/VMM/testcase/tstMicro.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/testcase/tstMicro.cpp')
-rw-r--r--src/VBox/VMM/testcase/tstMicro.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/VBox/VMM/testcase/tstMicro.cpp b/src/VBox/VMM/testcase/tstMicro.cpp
new file mode 100644
index 00000000..f4dae73b
--- /dev/null
+++ b/src/VBox/VMM/testcase/tstMicro.cpp
@@ -0,0 +1,404 @@
+/* $Id: tstMicro.cpp $ */
+/** @file
+ * Micro Testcase, profiling special CPU operations.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/vmm/vm.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/cpum.h>
+#include <VBox/vmm/pdmapi.h>
+#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/pgm.h>
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+
+#include "tstMicro.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define TESTCASE "tstVMM"
+
+static const char *GetDescription(TSTMICROTEST enmTest)
+{
+ switch (enmTest)
+ {
+ case TSTMICROTEST_OVERHEAD: return "Overhead";
+ case TSTMICROTEST_INVLPG_0: return "invlpg [0]";
+ case TSTMICROTEST_INVLPG_EIP: return "invlpg [EIP]";
+ case TSTMICROTEST_INVLPG_ESP: return "invlpg [ESP]";
+ case TSTMICROTEST_CR3_RELOAD: return "cr3 reload";
+ case TSTMICROTEST_WP_DISABLE: return "CR0.WP <- 0";
+ case TSTMICROTEST_WP_ENABLE: return "CR0.WP <- 1";
+
+ case TSTMICROTEST_PF_R0: return "R0 #PG (NULL)";
+ case TSTMICROTEST_PF_R1: return "R1 #PG (NULL)";
+ case TSTMICROTEST_PF_R2: return "R2 #PG (NULL)";
+ case TSTMICROTEST_PF_R3: return "R3 #PG (NULL)";
+
+ default:
+ {
+ static char sz[64];
+ RTStrPrintf(sz, sizeof(sz), "%d?", enmTest);
+ return sz;
+ }
+ }
+}
+
+
+static void PrintHeaderInstr(void)
+{
+ RTPrintf(TESTCASE ": %-25s %10s %10s %10s\n",
+ "Test name",
+ "Min",
+ "Avg",
+ "Max");
+}
+
+static void PrintResultInstr(PTSTMICRO pTst, TSTMICROTEST enmTest, int rc, uint64_t cMinTicks, uint64_t cAvgTicks, uint64_t cMaxTicks)
+{
+ if (RT_FAILURE(rc))
+ RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu - %Rrc cr2=%x err=%x eip=%x!\n",
+ GetDescription(enmTest),
+ cMinTicks,
+ cAvgTicks,
+ cMaxTicks,
+ rc,
+ pTst->u32CR2,
+ pTst->u32ErrCd,
+ pTst->u32EIP);
+ else
+ RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu\n",
+ GetDescription(enmTest),
+ cMinTicks,
+ cAvgTicks,
+ cMaxTicks);
+}
+
+static void PrintHeaderTraps(void)
+{
+ RTPrintf(TESTCASE ": %-25s %10s %10s %10s %10s %10s\n",
+ "Test name",
+ "Total",
+ "ToRx",
+ "Trap",
+ "ToRxTrap",
+ "int42-done");
+}
+
+static void PrintResultTrap(PTSTMICRO pTst, TSTMICROTEST enmTest, int rc)
+{
+ if (RT_FAILURE(rc))
+ RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu %10llu %10llu - %Rrc cr2=%x err=%x eip=%x!\n",
+ GetDescription(enmTest),
+ pTst->aResults[enmTest].cTotalTicks,
+ pTst->aResults[enmTest].cToRxFirstTicks,
+ pTst->aResults[enmTest].cTrapTicks,
+ pTst->aResults[enmTest].cToRxTrapTicks,
+ pTst->aResults[enmTest].cToR0Ticks,
+ rc,
+ pTst->u32CR2,
+ pTst->u32ErrCd,
+ pTst->u32EIP);
+ else
+ RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu %10llu %10llu\n",
+ GetDescription(enmTest),
+ pTst->aResults[enmTest].cTotalTicks,
+ pTst->aResults[enmTest].cToRxFirstTicks,
+ pTst->aResults[enmTest].cTrapTicks,
+ pTst->aResults[enmTest].cToRxTrapTicks,
+ pTst->aResults[enmTest].cToR0Ticks);
+}
+
+
+/**
+ * 'Allocate' selectors for 32-bit code/data in rings 0-3.
+ *
+ * 0060 - r0 code
+ * 0068 - r0 data
+ *
+ * 1060 - r1 code
+ * 1068 - r1 data
+ *
+ * 2060 - r2 code
+ * 2068 - r2 data
+ *
+ * 3060 - r3 code
+ * 3068 - r3 data
+ *
+ */
+static void SetupSelectors(PVM pVM)
+{
+ /*
+ * Find the GDT - This is a HACK :-)
+ */
+ RTRCPTR RCPtr = CPUMGetHyperGDTR(VMMGetCpu0(pVM), NULL);
+ PX86DESC paGDTEs = (PX86DESC)MMHyperRCToR3(pVM, RCPtr);
+
+ for (unsigned i = 0; i <= 3; i++)
+ {
+ RTSEL Sel = (i << 12) + 0x60;
+
+ /* 32-bit code selector. */
+ PX86DESC pGDTE = &paGDTEs[Sel >> X86_SEL_SHIFT];
+ pGDTE->au32[0] = pGDTE->au32[1] = 0;
+ pGDTE->Gen.u16LimitLow = 0xffff;
+ pGDTE->Gen.u4LimitHigh = 0xf;
+ pGDTE->Gen.u1Granularity= 1;
+ pGDTE->Gen.u1Present = 1;
+ pGDTE->Gen.u2Dpl = i;
+ pGDTE->Gen.u1DefBig = 1;
+ pGDTE->Gen.u1DescType = 1; /* !system */
+ pGDTE->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
+
+ /* 32-bit data selector. */
+ pGDTE++;
+ pGDTE->au32[0] = pGDTE->au32[1] = 0;
+ pGDTE->Gen.u16LimitLow = 0xffff;
+ pGDTE->Gen.u4LimitHigh = 0xf;
+ pGDTE->Gen.u1Granularity= 1;
+ pGDTE->Gen.u1Present = 1;
+ pGDTE->Gen.u2Dpl = i;
+ pGDTE->Gen.u1DefBig = 1;
+ pGDTE->Gen.u1DescType = 1; /* !system */
+ pGDTE->Gen.u4Type = X86_SEL_TYPE_RW_ACC;
+ }
+}
+
+
+static DECLCALLBACK(int) doit(PVM pVM)
+{
+ RTPrintf(TESTCASE ": testing...\n");
+ SetupSelectors(pVM);
+
+ /*
+ * Loading the module and resolve the entry point.
+ */
+ int rc = PDMR3LdrLoadRC(pVM, NULL, "tstMicroRC.gc");
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": Failed to load tstMicroRC.gc, rc=%Rra\n", rc);
+ return rc;
+ }
+ RTRCPTR RCPtrEntry;
+ rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRC", &RCPtrEntry);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRC' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
+ return rc;
+ }
+ RTRCPTR RCPtrStart;
+ rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRCAsmStart", &RCPtrStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRCAsmStart' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
+ return rc;
+ }
+ RTRCPTR RCPtrEnd;
+ rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRCAsmEnd", &RCPtrEnd);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRCAsmEnd' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
+ return rc;
+ }
+
+ /*
+ * Allocate and initialize the instance data.
+ */
+ PTSTMICRO pTst;
+ rc = MMHyperAlloc(pVM, RT_ALIGN_Z(sizeof(*pTst), PAGE_SIZE), PAGE_SIZE, MM_TAG_VM, (void **)&pTst);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": Failed to resolve allocate instance memory (%d bytes), rc=%Rra\n", sizeof(*pTst), rc);
+ return rc;
+ }
+ pTst->RCPtr = MMHyperR3ToRC(pVM, pTst);
+ pTst->RCPtrStack = MMHyperR3ToRC(pVM, &pTst->au8Stack[sizeof(pTst->au8Stack) - 32]);
+
+ /* the page must be writable from user mode */
+ rc = PGMMapModifyPage(pVM, pTst->RCPtr, sizeof(*pTst), X86_PTE_US | X86_PTE_RW, ~(uint64_t)(X86_PTE_US | X86_PTE_RW));
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": PGMMapModifyPage -> rc=%Rra\n", rc);
+ return rc;
+ }
+
+ /* all the code must be executable from R3. */
+ rc = PGMMapModifyPage(pVM, RCPtrStart, RCPtrEnd - RCPtrStart + PAGE_SIZE, X86_PTE_US, ~(uint64_t)X86_PTE_US);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(TESTCASE ": PGMMapModifyPage -> rc=%Rra\n", rc);
+ return rc;
+ }
+ DBGFR3PagingDumpEx(pVM->pUVM, 0 /*idCpu*/, DBGFPGDMP_FLAGS_CURRENT_CR3 | DBGFPGDMP_FLAGS_CURRENT_MODE
+ | DBGFPGDMP_FLAGS_SHADOW | DBGFPGDMP_FLAGS_HEADER | DBGFPGDMP_FLAGS_PRINT_CR3,
+ 0 /*cr3*/, 0 /*u64FirstAddr*/, UINT64_MAX /*u64LastAddr*/, 99 /*cMaxDepth*/, NULL);
+
+#if 0
+ /*
+ * Disassemble the assembly...
+ */
+ RTGCPTR GCPtr = RCPtrStart;
+ while (GCPtr < RCPtrEnd)
+ {
+ size_t cb = 0;
+ char sz[256];
+ int rc = DBGFR3DisasInstrEx(pVM, CPUMGetHyperCS(pVM), GCPtr, 0, sz, sizeof(sz), &cb);
+ if (RT_SUCCESS(rc))
+ RTLogPrintf("%s\n", sz);
+ else
+ {
+ RTLogPrintf("%RGv rc=%Rrc\n", GCPtr, rc);
+ cb = 1;
+ }
+ GCPtr += cb;
+ }
+#endif
+
+#ifdef VBOX_WITH_RAW_MODE
+ /*
+ * Do the profiling.
+ */
+ /* execute the instruction profiling tests */
+ PrintHeaderInstr();
+ int i;
+ for (i = TSTMICROTEST_OVERHEAD; i < TSTMICROTEST_TRAP_FIRST; i++)
+ {
+ TSTMICROTEST enmTest = (TSTMICROTEST)i;
+ uint64_t cMin = UINT64_MAX;
+ uint64_t cMax = 0;
+ uint64_t cTotal = 0;
+ unsigned cSamples = 0;
+ rc = VINF_SUCCESS;
+ for (int c = 0; c < 100; c++)
+ {
+ int rc2 = VMMR3CallRC(pVM, RCPtrEntry, 2, pTst->RCPtr, enmTest);
+ if (RT_SUCCESS(rc2))
+ {
+ uint64_t u64 = pTst->aResults[enmTest].cTotalTicks;
+ if (cMin > u64)
+ cMin = u64;
+ if (cMax < u64)
+ cMax = u64;
+ cTotal += u64;
+ cSamples++;
+ }
+ else if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ uint64_t cAvg = cTotal / (cSamples ? cSamples : 1);
+ pTst->aResults[enmTest].cTotalTicks = cAvg;
+ PrintResultInstr(pTst, enmTest, rc, cMin, cAvg, cMax);
+ /* store the overhead */
+ if (enmTest == TSTMICROTEST_OVERHEAD)
+ pTst->u64Overhead = cMin;
+ }
+#endif
+
+
+#ifdef VBOX_WITH_RAW_MODE
+ /* execute the trap/cycle profiling tests. */
+ RTPrintf("\n");
+ PrintHeaderTraps();
+ /* don't disable rdtsc in R1/R2/R3! */
+ CPUMR3SetCR4Feature(pVM, 0, ~X86_CR4_TSD);
+ for (i = TSTMICROTEST_TRAP_FIRST; i < TSTMICROTEST_MAX; i++)
+ {
+ TSTMICROTEST enmTest = (TSTMICROTEST)i;
+ rc = VMMR3CallRC(pVM, RCPtrEntry, 2, pTst->RCPtr, enmTest);
+ PrintResultTrap(pTst, enmTest, rc);
+ }
+#endif
+
+ RTPrintf(TESTCASE ": done!\n");
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
+{
+ RT_NOREF1(envp);
+ int rcRet = 0; /* error count. */
+
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+
+ /*
+ * Create empty VM.
+ */
+ PVM pVM;
+ PUVM pUVM;
+ int rc = VMR3Create(1, NULL, NULL, NULL, NULL, NULL, &pVM, &pUVM);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do testing.
+ */
+ rc = VMR3ReqCallVoidWaitU(pUVM, VMCPUID_ANY, (PFNRT)doit, 1, pVM);
+ AssertRC(rc);
+ STAMR3Dump(pUVM, "*");
+
+ /*
+ * Cleanup.
+ */
+ rc = VMR3PowerOff(pUVM);
+ if (!RT_SUCCESS(rc))
+ {
+ RTPrintf(TESTCASE ": error: failed to power off vm! rc=%Rrc\n", rc);
+ rcRet++;
+ }
+ rc = VMR3Destroy(pUVM);
+ if (!RT_SUCCESS(rc))
+ {
+ RTPrintf(TESTCASE ": error: failed to destroy vm! rc=%Rrc\n", rc);
+ rcRet++;
+ }
+ VMR3ReleaseUVM(pUVM);
+ }
+ else
+ {
+ RTPrintf(TESTCASE ": fatal error: failed to create vm! rc=%Rrc\n", rc);
+ rcRet++;
+ }
+
+ return rcRet;
+}
+
+
+#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
+/**
+ * Main entry point.
+ */
+int main(int argc, char **argv, char **envp)
+{
+ return TrustedMain(argc, argv, envp);
+}
+#endif
+