summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/Support/testcase
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/Support/testcase')
-rw-r--r--src/VBox/HostDrivers/Support/testcase/Makefile.kmk158
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp69
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp196
-rw-r--r--src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp63
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp118
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp356
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp104
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstInit.cpp61
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstInt.cpp240
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstLow.cpp164
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp440
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstPage.cpp101
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstPin.cpp223
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp128
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp232
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp649
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp235
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp162
18 files changed, 3699 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Support/testcase/Makefile.kmk b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
new file mode 100644
index 00000000..039557ac
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
@@ -0,0 +1,158 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the SUPLib testcases.
+#
+
+#
+# Copyright (C) 2006-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
+
+# We need the VMM/Config.kmk one for the VMM_COMMON_DEFS variable.
+ifndef VBOX_VMM_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/src/VBox/VMM/Config.kmk
+endif
+
+PROGRAMS += \
+ SUPInstall \
+ SUPUninstall \
+ SUPLoggerCtl
+ifdef VBOX_WITH_TESTCASES
+ if defined(VBOX_WITH_HARDENING)
+ PROGRAMS += \
+ tstSupVerify
+ endif
+ if !defined(VBOX_WITH_HARDENING) || "$(KBUILD_TARGET)" != "win"
+ PROGRAMS += \
+ tstPage \
+ tstContiguous \
+ tstInit \
+ tstInt \
+ tstLow \
+ tstPin \
+ tstGetPagingMode \
+ tstSupLoadModule \
+ tstSupSem \
+ tstSupSem-Zombie \
+ tstSupTscDelta
+ endif
+ PROGRAMS.win += \
+ tstNtQueryStuff
+endif # VBOX_WITH_TESTCASES
+
+SUPInstall_TEMPLATE = VBoxR3Exe
+SUPInstall_SOURCES = SUPInstall.cpp
+SUPInstall_LIBS = $(LIB_RUNTIME)
+
+SUPUninstall_TEMPLATE = VBoxR3Exe
+SUPUninstall_SOURCES = SUPUninstall.cpp
+SUPUninstall_LIBS = $(LIB_RUNTIME)
+
+SUPLoggerCtl_TEMPLATE = VBoxR3Exe
+SUPLoggerCtl_SOURCES = SUPLoggerCtl.cpp
+SUPLoggerCtl_LIBS = $(LIB_RUNTIME)
+
+tstInt_TEMPLATE = VBoxR3Exe
+tstInt_DEFS = $(VMM_COMMON_DEFS)
+tstInt_SOURCES = tstInt.cpp
+tstInt_LIBS = $(LIB_RUNTIME)
+
+tstContiguous_TEMPLATE = VBoxR3TstExe
+tstContiguous_SOURCES = tstContiguous.cpp
+
+tstInit_TEMPLATE = VBoxR3TstExe
+tstInit_SOURCES = tstInit.cpp
+
+tstLow_TEMPLATE = VBoxR3TstExe
+tstLow_SOURCES = tstLow.cpp
+
+tstNtQueryStuff_TEMPLATE = VBoxR3TstExe
+tstNtQueryStuff_SDKS = VBoxNtDll
+tstNtQueryStuff_SOURCES = tstNtQueryStuff.cpp
+
+tstPin_TEMPLATE = VBoxR3TstExe
+tstPin_SOURCES = tstPin.cpp
+
+tstPage_TEMPLATE = VBoxR3TstExe
+tstPage_SOURCES = tstPage.cpp
+
+#
+# tstGIP-2
+#
+ifdef VBOX_WITH_TESTCASES
+ if defined(VBOX_WITH_HARDENING) && "$(KBUILD_TARGET)" == "win"
+ PROGRAMS += tstGIP-2Hardened
+ DLLS += tstGIP-2
+ else
+ PROGRAMS += tstGIP-2
+ endif
+endif
+
+tstGIP-2Hardened_TEMPLATE = VBoxR3HardenedTstExe
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ tstGIP-2Hardened_DEFS = PROGRAM_NAME_STR="tstGIP-2"
+else
+ tstGIP-2Hardened_DEFS = PROGRAM_NAME_STR=\"tstGIP-2\"
+endif
+tstGIP-2Hardened_SOURCES = ../SUPR3HardenedMainTemplateTestcase.cpp
+tstGIP-2Hardened_NAME = tstGIP-2
+
+if defined(VBOX_WITH_HARDENING) && "$(KBUILD_TARGET)" == "win"
+ tstGIP-2_TEMPLATE := VBoxR3HardenedTstDll
+else
+ tstGIP-2_TEMPLATE := VBoxR3TstExe
+endif
+tstGIP-2_SOURCES = tstGIP-2.cpp
+
+tstGetPagingMode_TEMPLATE = VBoxR3TstExe
+tstGetPagingMode_SOURCES = tstGetPagingMode.cpp
+
+tstSupLoadModule_TEMPLATE = VBoxR3TstExe
+tstSupLoadModule_SOURCES = tstSupLoadModule.cpp
+
+tstSupSem_TEMPLATE = VBoxR3TstExe
+tstSupSem_SOURCES = tstSupSem.cpp
+
+tstSupSem-Zombie_TEMPLATE = VBoxR3TstExe
+tstSupSem-Zombie_SOURCES = tstSupSem-Zombie.cpp
+
+tstSupTscDelta_TEMPLATE = VBoxR3TstExe
+tstSupTscDelta_SOURCES = tstSupTscDelta.cpp
+
+# For testing supR3HardenedVerifyFile on windows.
+tstSupVerify_TEMPLATE = VBoxR3TstExe
+tstSupVerify_SOURCES = tstSupVerify.cpp
+
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp
new file mode 100644
index 00000000..246ff7a1
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp
@@ -0,0 +1,69 @@
+/* $Id: SUPInstall.cpp $ */
+/** @file
+ * SUPInstall - Driver Install
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments");
+ NOREF(argv);
+
+ int rc = SUPR3Install();
+ if (RT_SUCCESS(rc))
+ {
+ if (rc == VINF_SUCCESS)
+ RTMsgInfo("Installed successfully!");
+ else if (rc == VINF_ALREADY_INITIALIZED)
+ RTMsgInfo("Already loaded.");
+ else if (rc == VWRN_ALREADY_EXISTS)
+ RTMsgInfo("Service already existed; started successfully.");
+ else
+ RTMsgInfo("Unexpected status: %Rrc", rc);
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "installation failed. rc=%Rrc", rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp b/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp
new file mode 100644
index 00000000..61a6e718
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPLoggerCtl.cpp
@@ -0,0 +1,196 @@
+/* $Id: SUPLoggerCtl.cpp $ */
+/** @file
+ * SUPLoggerCtl - Support Driver Logger Control.
+ */
+
+/*
+ * 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 <VBox/sup.h>
+#include <iprt/buildconfig.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+
+
+/**
+ * Prints the usage.
+ * @returns 1.
+ */
+static int usage(void)
+{
+ RTPrintf("usage: SUPLoggerCtl [-f|--flags <flags-settings>] \\\n"
+ " [-g|--groups <groups-settings>] \\\n"
+ " [-d|--dest <destination-specifiers>] \\\n"
+ " [-l|--which <release|debug>] \\\n"
+ " [-o|--what <set|create|destroy>]\n"
+ " or: SUPLoggerCtl <-h|--help>\n"
+ "\n"
+ );
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+
+ /*
+ * Options are mandatory.
+ */
+ if (argc <= 1)
+ return usage();
+
+ /*
+ * Parse the options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--flags", 'f', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--dest", 'd', RTGETOPT_REQ_STRING },
+ { "--what", 'o', RTGETOPT_REQ_STRING },
+ { "--which", 'l', RTGETOPT_REQ_STRING },
+ };
+
+ const char *pszFlags = "";
+ const char *pszGroups = "";
+ const char *pszDest = "";
+ SUPLOGGER enmWhich = SUPLOGGER_DEBUG;
+ enum
+ {
+ kSupLoggerCtl_Set, kSupLoggerCtl_Create, kSupLoggerCtl_Destroy
+ } enmWhat = kSupLoggerCtl_Set;
+
+ int ch;
+ RTGETOPTUNION Val;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &Val)))
+ {
+ switch (ch)
+ {
+ case 'f':
+ pszFlags = Val.psz;
+ break;
+
+ case 'g':
+ pszGroups = Val.psz;
+ break;
+
+ case 'd':
+ pszDest = Val.psz;
+ break;
+
+ case 'o':
+ if (!strcmp(Val.psz, "set"))
+ enmWhat = kSupLoggerCtl_Set;
+ else if (!strcmp(Val.psz, "create"))
+ enmWhat = kSupLoggerCtl_Create;
+ else if (!strcmp(Val.psz, "destroy"))
+ enmWhat = kSupLoggerCtl_Destroy;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unknown operation '%s'.\n", Val.psz);
+ return 1;
+ }
+ break;
+
+ case 'l':
+ if (!strcmp(Val.psz, "debug"))
+ enmWhich = SUPLOGGER_DEBUG;
+ else if (!strcmp(Val.psz, "release"))
+ enmWhich = SUPLOGGER_RELEASE;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unknown logger '%s'.\n", Val.psz);
+ return 1;
+ }
+ break;
+
+ case 'h':
+ return usage();
+
+ case 'V':
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return 0;
+
+ case VINF_GETOPT_NOT_OPTION:
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: Unexpected argument '%s'.\n", Val.psz);
+ return 1;
+
+ default:
+ return RTGetOptPrintError(ch, &Val);
+ }
+ }
+
+ /*
+ * Make sure the support library is initialized.
+ */
+ int rc = SUPR3Init(NULL /*ppSession*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the requested job.
+ */
+ switch (enmWhat)
+ {
+ case kSupLoggerCtl_Set:
+ rc = SUPR3LoggerSettings(enmWhich, pszFlags, pszGroups, pszDest);
+ break;
+ case kSupLoggerCtl_Create:
+ rc = SUPR3LoggerCreate(enmWhich, pszFlags, pszGroups, pszDest);
+ break;
+ case kSupLoggerCtl_Destroy:
+ rc = SUPR3LoggerDestroy(enmWhich);
+ break;
+ default:
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ RTPrintf("SUPLoggerCtl: Success\n");
+ else
+ RTStrmPrintf(g_pStdErr, "SUPLoggerCtl: error: rc=%Rrc\n", rc);
+ }
+ else
+ RTStrmPrintf(g_pStdErr, "SUPR3Init: error: rc=%Rrc\n", rc);
+
+ return RT_SUCCESS(rc) ? 0 : 1;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp
new file mode 100644
index 00000000..64b47795
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp
@@ -0,0 +1,63 @@
+/* $Id: SUPUninstall.cpp $ */
+/** @file
+ * SUPUninstall - Driver Uninstall.
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ int rc = SUPR3Uninstall();
+ if (RT_SUCCESS(rc))
+ {
+ RTMsgInfo("uninstalled successfully");
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "uninstallation failed. rc=%Rrc", rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp
new file mode 100644
index 00000000..6f1e8290
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp
@@ -0,0 +1,118 @@
+/* $Id: tstContiguous.cpp $ */
+/** @file
+ * SUP Testcase - Contiguous Memory Interface (ring-3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ RTPrintf("tstContiguous: SUPR3Init -> rc=%Rrc\n", rc);
+ rcRet += rc != 0;
+ if (!rc)
+ {
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ RTHCPHYS HCPhys;
+ void *pv = SUPR3ContAlloc(8, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ memset(pv, 0xff, PAGE_SIZE * 8);
+ pv = SUPR3ContAlloc(5, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ memset(pv, 0x7f, PAGE_SIZE * 5);
+ rc = SUPR3ContFree(pv, 5);
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("tstContiguous: SUPR3ContFree failed! rc=%Rrc\n", rc);
+
+ void *apv[128];
+ for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
+ {
+ apv[i] = SUPR3ContAlloc(1 + (i % 11), NULL, &HCPhys);
+ if (!apv[i])
+ {
+ RTPrintf("tstContiguous: i=%d: failed to allocate %d pages", i, 1 + (i % 11));
+#if defined(RT_ARCH_X86) && defined(RT_OS_LINUX)
+ /* With 32-bit address spaces it's sometimes difficult
+ * to find bigger chunks of contiguous memory */
+ if (i % 11 > 7)
+ RTPrintf(" => ignoring (32-bit host)");
+ else
+#endif
+ rcRet++;
+ RTPrintf("\n");
+ }
+ }
+ for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
+ if (apv[i])
+ {
+ rc = SUPR3ContFree(apv[i], 1 + (i % 11));
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("tstContiguous: i=%d SUPR3ContFree failed! rc=%Rrc\n", i, rc);
+ }
+ }
+ else
+ RTPrintf("tstContiguous: SUPR3ContAlloc (2nd) failed!\n");
+ }
+ else
+ RTPrintf("tstContiguous: SUPR3ContAlloc failed!\n");
+
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("tstContiguous: SUPR3Term -> rc=%Rrc\n", rc);
+ rcRet += rc != 0;
+ }
+
+ return rcRet ? 1 : 0;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp
new file mode 100644
index 00000000..a183ca27
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp
@@ -0,0 +1,356 @@
+/* $Id: tstGIP-2.cpp $ */
+/** @file
+ * SUP Testcase - Global Info Page interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/thread.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/x86.h>
+
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ /*
+ * Parse args
+ */
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--iterations", 'i', RTGETOPT_REQ_INT32 },
+ { "--hex", 'h', RTGETOPT_REQ_NOTHING },
+ { "--decimal", 'd', RTGETOPT_REQ_NOTHING },
+ { "--spin", 's', RTGETOPT_REQ_NOTHING },
+ { "--reference", 'r', RTGETOPT_REQ_UINT64 }, /* reference value of CpuHz, display the
+ * CpuHz deviation in a separate column. */
+ { "--notestmode", 't', RTGETOPT_REQ_NOTHING } /* don't run GIP in test-mode (atm, test-mode
+ * implies updating GIP CpuHz even when invariant) */
+ };
+
+ bool fHex = true;
+ bool fSpin = false;
+ bool fCompat = true;
+ bool fTestMode = true;
+ int ch;
+ uint32_t cIterations = 40;
+ uint64_t uCpuHzRef = UINT64_MAX;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'i':
+ cIterations = ValueUnion.u32;
+ break;
+
+ case 'd':
+ fHex = false;
+ break;
+
+ case 'h':
+ fHex = true;
+ break;
+
+ case 's':
+ fSpin = true;
+ break;
+
+ case 'r':
+ uCpuHzRef = ValueUnion.u64;
+ break;
+
+ case 't':
+ fTestMode = false;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Init
+ */
+ PSUPDRVSESSION pSession = NIL_RTR0PTR;
+ int rc = SUPR3Init(&pSession);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_pSUPGlobalInfoPage)
+ {
+ uint64_t uCpuHzOverallDeviation = 0;
+ uint32_t cCpuHzNotCompat = 0;
+ int64_t iCpuHzMaxDeviation = 0;
+ int32_t cCpuHzOverallDevCnt = 0;
+ uint32_t cCpuHzChecked = 0;
+
+ /* Pick current CpuHz as the reference if none was specified. */
+ if (uCpuHzRef == UINT64_MAX)
+ uCpuHzRef = SUPGetCpuHzFromGip(g_pSUPGlobalInfoPage);
+
+ if ( fTestMode
+ && g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ SUPR3GipSetFlags(SUPGIP_FLAGS_TESTING_ENABLE, UINT32_MAX);
+
+ RTPrintf("tstGIP-2: u32Mode=%d (%s) fTestMode=%RTbool u32Version=%#x fGetGipCpu=%#RX32 cPages=%#RX32\n",
+ g_pSUPGlobalInfoPage->u32Mode,
+ SUPGetGIPModeName(g_pSUPGlobalInfoPage),
+ fTestMode,
+ g_pSUPGlobalInfoPage->u32Version,
+ g_pSUPGlobalInfoPage->fGetGipCpu,
+ g_pSUPGlobalInfoPage->cPages);
+ RTPrintf("tstGIP-2: cCpus=%d cPossibleCpus=%d cPossibleCpuGroups=%d cPresentCpus=%d cOnlineCpus=%d idCpuMax=%#x\n",
+ g_pSUPGlobalInfoPage->cCpus,
+ g_pSUPGlobalInfoPage->cPossibleCpus,
+ g_pSUPGlobalInfoPage->cPossibleCpuGroups,
+ g_pSUPGlobalInfoPage->cPresentCpus,
+ g_pSUPGlobalInfoPage->cOnlineCpus,
+ g_pSUPGlobalInfoPage->idCpuMax);
+ RTPrintf("tstGIP-2: u32UpdateHz=%RU32 u32UpdateIntervalNS=%RU32 u64NanoTSLastUpdateHz=%RX64 u64CpuHz=%RU64 uCpuHzRef=%RU64\n",
+ g_pSUPGlobalInfoPage->u32UpdateHz,
+ g_pSUPGlobalInfoPage->u32UpdateIntervalNS,
+ g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz,
+ g_pSUPGlobalInfoPage->u64CpuHz,
+ uCpuHzRef);
+ for (uint32_t iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_INVALID)
+ {
+ SUPGIPCPU const *pGipCpu = &g_pSUPGlobalInfoPage->aCPUs[iCpu];
+ RTPrintf("tstGIP-2: aCPU[%3u]: enmState=%d iCpuSet=%-3u idCpu=%#010x iCpuGroup=%-2u iCpuGroupMember=%-3u idApic=%#06x\n",
+ iCpu, pGipCpu->enmState, pGipCpu->iCpuSet, pGipCpu->idCpu, pGipCpu->iCpuGroup,
+ pGipCpu->iCpuGroupMember, pGipCpu->idApic);
+ }
+
+ RTPrintf(fHex
+ ? "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz %sTSC Interval History...\n"
+ : "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz %sTSC Interval History...\n",
+ uCpuHzRef ? " CpuHz deviation Compat " : "");
+ static SUPGIPCPU s_aaCPUs[2][RTCPUSET_MAX_CPUS];
+ for (uint32_t i = 0; i < cIterations; i++)
+ {
+ /* Copy the data. */
+ memcpy(&s_aaCPUs[i & 1][0], &g_pSUPGlobalInfoPage->aCPUs[0], g_pSUPGlobalInfoPage->cCpus * sizeof(g_pSUPGlobalInfoPage->aCPUs[0]));
+
+ /* Display it & find something to spin on. */
+ uint32_t u32TransactionId = 0;
+ uint32_t volatile *pu32TransactionId = NULL;
+ for (unsigned iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ char szCpuHzDeviation[32];
+ PSUPGIPCPU pPrevCpu = &s_aaCPUs[!(i & 1)][iCpu];
+ PSUPGIPCPU pCpu = &s_aaCPUs[i & 1][iCpu];
+ if (uCpuHzRef)
+ {
+ /* Only CPU 0 is updated for invariant & sync modes, see supdrvGipUpdate(). */
+ if ( iCpu == 0
+ || g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_ASYNC_TSC)
+ {
+ /* Wait until the history validation code takes effect. */
+ if (pCpu->u32TransactionId > 23 + (8 * 2) + 1)
+ {
+ int64_t iCpuHzDeviation = pCpu->u64CpuHz - uCpuHzRef;
+ uint64_t uCpuHzDeviation = RT_ABS(iCpuHzDeviation);
+ bool fCurHzCompat = SUPIsTscFreqCompatibleEx(uCpuHzRef, pCpu->u64CpuHz, false /*fRelax*/);
+ if (uCpuHzDeviation <= 999999999)
+ {
+ if (RT_ABS(iCpuHzDeviation) > RT_ABS(iCpuHzMaxDeviation))
+ iCpuHzMaxDeviation = iCpuHzDeviation;
+ uCpuHzOverallDeviation += uCpuHzDeviation;
+ cCpuHzOverallDevCnt++;
+ uint32_t uPct = (uint32_t)(uCpuHzDeviation * 100000 / uCpuHzRef + 5);
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%10RI64%3d.%02d%% %RTbool ",
+ iCpuHzDeviation, uPct / 1000, (uPct % 1000) / 10, fCurHzCompat);
+ }
+ else
+ {
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%17s %RTbool ", "?",
+ fCurHzCompat);
+ }
+
+ if (!fCurHzCompat)
+ ++cCpuHzNotCompat;
+ fCompat &= fCurHzCompat;
+ ++cCpuHzChecked;
+ }
+ else
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%25s ", "priming");
+ }
+ else
+ RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%25s ", "");
+ }
+ else
+ szCpuHzDeviation[0] = '\0';
+ RTPrintf(fHex
+ ? "tstGIP-2: %4d/%d: %016llx %09llx %016llx %08x %d %08x %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n"
+ : "tstGIP-2: %4d/%d: %016llu %09llu %016llu %010u %d %010u %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n",
+ i, iCpu,
+ pCpu->u64NanoTS,
+ i ? pCpu->u64NanoTS - pPrevCpu->u64NanoTS : 0,
+ pCpu->u64TSC,
+ pCpu->u32UpdateIntervalTSC,
+ pCpu->iTSCHistoryHead,
+ pCpu->u32TransactionId,
+ pCpu->u64CpuHz,
+ szCpuHzDeviation,
+ pCpu->au32TSCHistory[0],
+ pCpu->au32TSCHistory[1],
+ pCpu->au32TSCHistory[2],
+ pCpu->au32TSCHistory[3],
+ pCpu->au32TSCHistory[4],
+ pCpu->au32TSCHistory[5],
+ pCpu->au32TSCHistory[6],
+ pCpu->au32TSCHistory[7],
+ pCpu->cErrors);
+ if (!pu32TransactionId)
+ {
+ pu32TransactionId = &g_pSUPGlobalInfoPage->aCPUs[iCpu].u32TransactionId;
+ u32TransactionId = pCpu->u32TransactionId;
+ }
+ }
+
+ /* Wait a bit / spin. */
+ if (!fSpin)
+ RTThreadSleep(9);
+ else
+ {
+ if (pu32TransactionId)
+ {
+ uint32_t uTmp;
+ while ( u32TransactionId == (uTmp = *pu32TransactionId)
+ || (uTmp & 1))
+ ASMNopPause();
+ }
+ else
+ RTThreadSleep(1);
+ }
+ }
+
+ /*
+ * Display TSC deltas.
+ *
+ * First iterative over the APIC ID array to get mostly consistent CPUID to APIC ID mapping.
+ * Then iterate over the offline CPUs. It is possible that there's a race between the online/offline
+ * states between the two iterations, but that cannot be helped from ring-3 anyway and not a biggie.
+ */
+ RTPrintf("tstGIP-2: TSC deltas:\n");
+ RTPrintf("tstGIP-2: idApic: i64TSCDelta\n");
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_pSUPGlobalInfoPage->aiCpuFromApicId); i++)
+ {
+ uint16_t iCpu = g_pSUPGlobalInfoPage->aiCpuFromApicId[i];
+ if (iCpu != UINT16_MAX)
+ RTPrintf("tstGIP-2: %#7x: %6lld (grp=%#04x mbr=%#05x set=%d cpu=%#05x)\n",
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic, g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroup, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroupMember,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuSet, iCpu);
+ }
+
+ for (uint32_t iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
+ if (g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic == UINT16_MAX)
+ RTPrintf("tstGIP-2: offline: %6lld (grp=%#04x mbr=%#05x set=%d cpu=%#05x)\n",
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroup,
+ g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuGroupMember, g_pSUPGlobalInfoPage->aCPUs[iCpu].iCpuSet, iCpu);
+
+ RTPrintf("tstGIP-2: enmUseTscDelta=%d fGetGipCpu=%#x\n",
+ g_pSUPGlobalInfoPage->enmUseTscDelta, g_pSUPGlobalInfoPage->fGetGipCpu);
+ if (uCpuHzRef)
+ {
+ if (cCpuHzOverallDevCnt)
+ {
+ uint32_t uPct = (uint32_t)(uCpuHzOverallDeviation * 100000 / cCpuHzOverallDevCnt / uCpuHzRef + 5);
+ RTPrintf("tstGIP-2: Average CpuHz deviation: %d.%02d%%\n",
+ uPct / 1000, (uPct % 1000) / 10);
+
+ uint32_t uMaxPct = (uint32_t)(RT_ABS(iCpuHzMaxDeviation) * 100000 / uCpuHzRef + 5);
+ RTPrintf("tstGIP-2: Maximum CpuHz deviation: %d.%02d%% (%RI64 ticks)\n",
+ uMaxPct / 1000, (uMaxPct % 1000) / 10, iCpuHzMaxDeviation);
+ }
+ else
+ {
+ RTPrintf("tstGIP-2: Average CpuHz deviation: ??.??\n");
+ RTPrintf("tstGIP-2: Average CpuHz deviation: ??.??\n");
+ }
+
+ RTPrintf("tstGIP-2: CpuHz compatibility: %RTbool (incompatible %u of %u times w/ %RU64 Hz - %s GIP)\n", fCompat,
+ cCpuHzNotCompat, cCpuHzChecked, uCpuHzRef, SUPGetGIPModeName(g_pSUPGlobalInfoPage));
+
+ if ( !fCompat
+ && g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_INVARIANT_TSC)
+ rc = -1;
+ }
+
+ /* Disable GIP test mode. */
+ if (fTestMode)
+ SUPR3GipSetFlags(0, ~SUPGIP_FLAGS_TESTING_ENABLE);
+ }
+ else
+ {
+ RTPrintf("tstGIP-2: g_pSUPGlobalInfoPage is NULL\n");
+ rc = -1;
+ }
+
+ SUPR3Term(false /*fForced*/);
+ }
+ else
+ RTPrintf("tstGIP-2: SUPR3Init failed: %Rrc\n", rc);
+ return !!rc;
+}
+
+#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
+/**
+ * Main entry point.
+ */
+int main(int argc, char **argv)
+{
+ return TrustedMain(argc, argv);
+}
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp
new file mode 100644
index 00000000..c3d0a5ef
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp
@@ -0,0 +1,104 @@
+/* $Id: tstGetPagingMode.cpp $ */
+/** @file
+ * SUP Testcase - Host paging mode interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ SUPPAGINGMODE enmMode = SUPR3GetPagingMode();
+ switch (enmMode)
+ {
+ case SUPPAGINGMODE_INVALID:
+ RTPrintf("SUPPAGINGMODE_INVALID\n");
+ break;
+ case SUPPAGINGMODE_32_BIT:
+ RTPrintf("SUPPAGINGMODE_32_BIT\n");
+ break;
+ case SUPPAGINGMODE_32_BIT_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_32_BIT_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_PAE:
+ RTPrintf("SUPPAGINGMODE_PAE\n");
+ break;
+ case SUPPAGINGMODE_PAE_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_PAE_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_PAE_NX:
+ RTPrintf("SUPPAGINGMODE_PAE_NX\n");
+ break;
+ case SUPPAGINGMODE_PAE_GLOBAL_NX:
+ RTPrintf("SUPPAGINGMODE_PAE_GLOBAL_NX\n");
+ break;
+ case SUPPAGINGMODE_AMD64:
+ RTPrintf("SUPPAGINGMODE_AMD64\n");
+ break;
+ case SUPPAGINGMODE_AMD64_GLOBAL:
+ RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL\n");
+ break;
+ case SUPPAGINGMODE_AMD64_NX:
+ RTPrintf("SUPPAGINGMODE_AMD64_NX\n");
+ break;
+ case SUPPAGINGMODE_AMD64_GLOBAL_NX:
+ RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL_NX\n");
+ break;
+ default:
+ RTPrintf("Unknown mode %d\n", enmMode);
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+
+ int rc2 = SUPR3Term(false /*fForced*/);
+ RTPrintf("SUPR3Term -> rc=%Rrc\n", rc2);
+ }
+ else
+ RTPrintf("SUPR3Init -> rc=%Rrc\n", rc);
+
+ return !RT_SUCCESS(rc);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstInit.cpp b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp
new file mode 100644
index 00000000..1a2a78f5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp
@@ -0,0 +1,61 @@
+/* $Id: tstInit.cpp $ */
+/** @file
+ * SUP Testcase - Support Library initialization and termination.
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ RTR3InitExe(argc, &argv, 0);
+ rc = SUPR3Init(NULL);
+ RTPrintf("tstInit: SUPR3Init -> rc=%Rrc\n", rc);
+ if (!rc)
+ {
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("tstInit: SUPR3Term -> rc=%Rrc\n", rc);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstInt.cpp b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp
new file mode 100644
index 00000000..094d5a3d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp
@@ -0,0 +1,240 @@
+/* $Id: tstInt.cpp $ */
+/** @file
+ * SUP Testcase - Test the interrupt gate feature of the support library.
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/vmm/gvmm.h>
+#include <VBox/vmm/vm.h>
+#include <iprt/errcore.h>
+#include <VBox/param.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#else
+# define ASMReadTSC RTTimeSystemNanoTS
+#endif
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/time.h>
+#include <iprt/path.h>
+
+
+int main(int argc, char **argv)
+{
+ int rcRet = 0;
+ int i;
+ int rc;
+ int cIterations = argc > 1 ? RTStrToUInt32(argv[1]) : 32;
+ if (cIterations == 0)
+ cIterations = 64;
+
+ /*
+ * Init.
+ */
+ RTR3InitExe(argc, &argv, 0);
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ rcRet += rc != 0;
+ RTPrintf("tstInt: SUPR3Init -> rc=%Rrc\n", rc);
+ char szFile[RTPATH_MAX];
+ if (!rc)
+ {
+ rc = RTPathExecDir(szFile, sizeof(szFile) - sizeof("/VMMR0.r0"));
+ }
+ char szAbsFile[RTPATH_MAX];
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szFile, "/VMMR0.r0");
+ rc = RTPathAbs(szFile, szAbsFile, sizeof(szAbsFile));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Load VMM code.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ rc = SUPR3LoadVMM(szAbsFile, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a tiny dummy VM so we can do NOP calls into it using the fast I/O control path.
+ */
+ GVMMCREATEVMREQ CreateVMReq;
+ CreateVMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ CreateVMReq.Hdr.cbReq = sizeof(CreateVMReq);
+ CreateVMReq.pSession = pSession;
+ CreateVMReq.pVMR0 = NIL_RTR0PTR;
+ CreateVMReq.pVMR3 = NULL;
+ CreateVMReq.cCpus = 1;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_GVMM_CREATE_VM, 0, &CreateVMReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ PVM pVM = CreateVMReq.pVMR3;
+ AssertRelease(RT_VALID_PTR(pVM));
+ AssertRelease(pVM->pVMR0ForCall == CreateVMReq.pVMR0);
+ AssertRelease(pVM->pSession == pSession);
+ AssertRelease(pVM->cCpus == 1);
+ pVM->enmVMState = VMSTATE_CREATED;
+ PVMR0 const pVMR0 = CreateVMReq.pVMR0;
+
+ rc = SUPR3SetVMForFastIOCtl(pVMR0);
+ if (!rc)
+ {
+ /*
+ * Call VMM code with invalid function.
+ */
+ for (i = cIterations; i > 0; i--)
+ {
+ rc = SUPR3CallVMMR0(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, NULL);
+ if (rc != VINF_SUCCESS)
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0 -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ RTPrintf("tstInt: Performed SUPR3CallVMMR0 %d times (rc=%Rrc)\n", cIterations, rc);
+
+ /*
+ * The fast path.
+ */
+ if (rc == VINF_SUCCESS)
+ {
+ RTTimeNanoTS();
+ uint64_t StartTS = RTTimeNanoTS();
+ uint64_t StartTick = ASMReadTSC();
+ uint64_t MinTicks = UINT64_MAX;
+ for (i = 0; i < 1000000; i++)
+ {
+ uint64_t OneStartTick = ASMReadTSC();
+ rc = SUPR3CallVMMR0Fast(pVMR0, VMMR0_DO_NOP, 0);
+ uint64_t Ticks = ASMReadTSC() - OneStartTick;
+ if (Ticks < MinTicks)
+ MinTicks = Ticks;
+
+ if (RT_UNLIKELY(rc != VINF_SUCCESS))
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0Fast -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ uint64_t Ticks = ASMReadTSC() - StartTick;
+ uint64_t NanoSecs = RTTimeNanoTS() - StartTS;
+
+ RTPrintf("tstInt: SUPR3CallVMMR0Fast - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
+ i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);
+
+ /*
+ * The ordinary path.
+ */
+ RTTimeNanoTS();
+ StartTS = RTTimeNanoTS();
+ StartTick = ASMReadTSC();
+ MinTicks = UINT64_MAX;
+ for (i = 0; i < 1000000; i++)
+ {
+ uint64_t OneStartTick = ASMReadTSC();
+ rc = SUPR3CallVMMR0Ex(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, 0, NULL);
+ uint64_t OneTicks = ASMReadTSC() - OneStartTick;
+ if (OneTicks < MinTicks)
+ MinTicks = OneTicks;
+
+ if (RT_UNLIKELY(rc != VINF_SUCCESS))
+ {
+ RTPrintf("tstInt: SUPR3CallVMMR0Ex -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
+ rcRet++;
+ break;
+ }
+ }
+ Ticks = ASMReadTSC() - StartTick;
+ NanoSecs = RTTimeNanoTS() - StartTS;
+
+ RTPrintf("tstInt: SUPR3CallVMMR0Ex - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
+ i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: SUPR3SetVMForFastIOCtl failed: %Rrc\n", rc);
+ rcRet++;
+ }
+
+ rc = SUPR3CallVMMR0Ex(pVMR0, 0 /*idCpu*/, VMMR0_DO_GVMM_DESTROY_VM, 0, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstInt: VMMR0_DO_GVMM_DESTROY_VM failed: %Rrc\n", rc);
+ rcRet++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: VMMR0_DO_GVMM_CREATE_VM failed\n");
+ rcRet++;
+ }
+
+ /*
+ * Unload VMM.
+ */
+ rc = SUPR3UnloadVMM();
+ if (rc)
+ {
+ RTPrintf("tstInt: SUPR3UnloadVMM failed with rc=%Rrc\n", rc);
+ rcRet++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstInt: SUPR3LoadVMM failed with rc=%Rrc%#RTeim\n", rc, &ErrInfo.Core);
+ rcRet++;
+ }
+
+ /*
+ * Terminate.
+ */
+ rc = SUPR3Term(false /*fForced*/);
+ rcRet += rc != 0;
+ RTPrintf("tstInt: SUPR3Term -> rc=%Rrc\n", rc);
+ }
+
+ return !!rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstLow.cpp b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp
new file mode 100644
index 00000000..032accdb
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp
@@ -0,0 +1,164 @@
+/* $Id: tstLow.cpp $ */
+/** @file
+ * SUP Testcase - Low (<4GB) Memory Allocate interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+
+ RTR3InitExe(argc, &argv, 0);
+ RTPrintf("tstLow: TESTING...\n");
+
+ rc = SUPR3Init(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ SUPPAGE aPages0[128];
+ void *pvPages0 = (void *)0x77777777;
+ memset(&aPages0[0], 0x8f, sizeof(aPages0));
+ rc = SUPR3LowAlloc(RT_ELEMENTS(aPages0), &pvPages0, NULL, aPages0);
+ if (RT_SUCCESS(rc))
+ {
+ /* check that the pages are below 4GB and valid. */
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ {
+ RTPrintf("%-4d: Phys=%RHp Reserved=%p\n", iPage, aPages0[iPage].Phys, aPages0[iPage].uReserved);
+ if (aPages0[iPage].uReserved != 0)
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages0[%d].uReserved=%#x expected 0!\n", iPage, aPages0[iPage].uReserved);
+ }
+ if ( aPages0[iPage].Phys >= _4G
+ || (aPages0[iPage].Phys & PAGE_OFFSET_MASK))
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages0[%d].Phys=%RHp!\n", iPage, aPages0[iPage].Phys);
+ }
+ }
+ if (!rcRet)
+ {
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ memset((char *)pvPages0 + iPage * PAGE_SIZE, iPage, PAGE_SIZE);
+ for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++)
+ for (uint8_t *pu8 = (uint8_t *)pvPages0 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++)
+ if (*pu8 != (uint8_t)iPage)
+ {
+ RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%u off=%#x\n",
+ *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK);
+ rcRet++;
+ }
+ }
+ SUPR3LowFree(pvPages0, RT_ELEMENTS(aPages0));
+ }
+ else
+ {
+ RTPrintf("SUPR3LowAlloc(%d,,) failed -> rc=%Rrc\n", RT_ELEMENTS(aPages0), rc);
+ rcRet++;
+ }
+
+ /*
+ * Allocate odd amounts in from 1 to 127.
+ */
+ for (unsigned cPages = 1; cPages <= 127; cPages++)
+ {
+ SUPPAGE aPages1[128];
+ void *pvPages1 = (void *)0x77777777;
+ memset(&aPages1[0], 0x8f, sizeof(aPages1));
+ rc = SUPR3LowAlloc(cPages, &pvPages1, NULL, aPages1);
+ if (RT_SUCCESS(rc))
+ {
+ /* check that the pages are below 4GB and valid. */
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ {
+ RTPrintf("%-4d::%-4d: Phys=%RHp Reserved=%p\n", cPages, iPage, aPages1[iPage].Phys, aPages1[iPage].uReserved);
+ if (aPages1[iPage].uReserved != 0)
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages1[%d].uReserved=%#x expected 0!\n", iPage, aPages1[iPage].uReserved);
+ }
+ if ( aPages1[iPage].Phys >= _4G
+ || (aPages1[iPage].Phys & PAGE_OFFSET_MASK))
+ {
+ rcRet++;
+ RTPrintf("tstLow: error: aPages1[%d].Phys=%RHp!\n", iPage, aPages1[iPage].Phys);
+ }
+ }
+ if (!rcRet)
+ {
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ memset((char *)pvPages1 + iPage * PAGE_SIZE, iPage, PAGE_SIZE);
+ for (unsigned iPage = 0; iPage < cPages; iPage++)
+ for (uint8_t *pu8 = (uint8_t *)pvPages1 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++)
+ if (*pu8 != (uint8_t)iPage)
+ {
+ RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%p off=%#x\n",
+ *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK);
+ rcRet++;
+ }
+ }
+ SUPR3LowFree(pvPages1, cPages);
+ }
+ else
+ {
+ RTPrintf("SUPR3LowAlloc(%d,,) failed -> rc=%Rrc\n", cPages, rc);
+ rcRet++;
+ }
+ }
+
+ }
+ else
+ {
+ RTPrintf("SUPR3Init -> rc=%Rrc\n", rc);
+ rcRet++;
+ }
+
+
+ return rcRet;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
new file mode 100644
index 00000000..9f181afd
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
@@ -0,0 +1,440 @@
+/* $Id: tstNtQueryStuff.cpp $ */
+/** @file
+ * SUP Testcase - Exploring some NT Query APIs.
+ */
+
+/*
+ * Copyright (C) 2006-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/nt/nt-and-windows.h>
+#include <iprt/test.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct FLAGDESC
+{
+ ULONG f;
+ const char *psz;
+} FLAGDESC;
+typedef const FLAGDESC *PCFLAGDESC;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+static HANDLE g_hProcess = NULL;
+
+
+static char *stringifyAppend(char *pszBuf, size_t *pcbBuf, const char *pszAppend, bool fWithSpace)
+{
+ size_t cchAppend = strlen(pszAppend);
+ if (cchAppend + 1 + fWithSpace <= *pcbBuf)
+ {
+ if (fWithSpace)
+ {
+ *pszBuf++ = ' ';
+ *pcbBuf += 1;
+ }
+ memcpy(pszBuf, pszAppend, cchAppend + 1);
+ *pcbBuf -= cchAppend;
+ pszBuf += cchAppend;
+ }
+
+ return pszBuf;
+}
+
+
+static char *stringifyAppendUnknownFlags(uint32_t fFlags, char *pszBuf, size_t *pcbBuf, bool fWithSpace)
+{
+ for (unsigned iBit = 0; iBit < 32; iBit++)
+ if (fFlags & RT_BIT_32(iBit))
+ {
+ char szTmp[32]; /* lazy bird */
+ RTStrPrintf(szTmp, sizeof(szTmp), "BIT(%d)", iBit);
+ pszBuf = stringifyAppend(pszBuf, pcbBuf, szTmp, fWithSpace);
+ fWithSpace = true;
+ }
+
+ return pszBuf;
+}
+
+
+static char *stringifyFlags(uint32_t fFlags, char *pszBuf, size_t cbBuf, PCFLAGDESC paFlagDesc, size_t cFlagDesc)
+{
+ char *pszBufStart = pszBuf;
+ if (fFlags)
+ {
+ for (size_t i = 0; i < cFlagDesc; i++)
+ {
+ if (fFlags & paFlagDesc[i].f)
+ {
+ fFlags &= ~paFlagDesc[i].f;
+ pszBuf = stringifyAppend(pszBuf, &cbBuf, paFlagDesc[i].psz, pszBuf != pszBufStart);
+ }
+ }
+
+ if (fFlags)
+ stringifyAppendUnknownFlags(fFlags, pszBuf, &cbBuf, pszBuf != pszBufStart);
+ }
+ else
+ {
+ pszBuf[0] = '0';
+ pszBuf[1] = '\0';
+ }
+ return pszBufStart;
+}
+
+
+static char *stringifyMemType(uint32_t fType, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aMemTypes[] =
+ {
+ { MEM_PRIVATE, "PRIVATE" },
+ { MEM_MAPPED, "MAPPED" },
+ { MEM_IMAGE, "IMAGE" },
+ };
+ return stringifyFlags(fType, pszBuf, cbBuf, s_aMemTypes, RT_ELEMENTS(s_aMemTypes));
+}
+
+
+static char *stringifyMemState(uint32_t fState, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aMemStates[] =
+ {
+ { MEM_FREE, "FREE" },
+ { MEM_COMMIT, "COMMIT" },
+ { MEM_RESERVE, "RESERVE" },
+ { MEM_DECOMMIT, "DECOMMMIT" },
+ };
+ return stringifyFlags(fState, pszBuf, cbBuf, s_aMemStates, RT_ELEMENTS(s_aMemStates));
+}
+
+
+static char *stringifyMemProt(uint32_t fProt, char *pszBuf, size_t cbBuf)
+{
+ static const FLAGDESC s_aProtections[] =
+ {
+ { PAGE_NOACCESS, "NOACCESS" },
+ { PAGE_READONLY, "READONLY" },
+ { PAGE_READWRITE, "READWRITE" },
+ { PAGE_WRITECOPY, "WRITECOPY" },
+ { PAGE_EXECUTE, "EXECUTE" },
+ { PAGE_EXECUTE_READ, "EXECUTE_READ" },
+ { PAGE_EXECUTE_READWRITE, "EXECUTE_READWRITE" },
+ { PAGE_EXECUTE_WRITECOPY, "EXECUTE_WRITECOPY" },
+ { PAGE_GUARD, "GUARD" },
+ { PAGE_NOCACHE, "NOCACHE" },
+ { PAGE_WRITECOMBINE, "WRITECOMBINE" },
+
+ };
+ return stringifyFlags(fProt, pszBuf, cbBuf, s_aProtections, RT_ELEMENTS(s_aProtections));
+}
+
+
+
+static void tstQueryVirtualMemory(void)
+{
+ RTTestISub("NtQueryVirtualMemory");
+
+ uintptr_t cbAdvance = 0;
+ uintptr_t uPtrWhere = 0;
+ for (;;)
+ {
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt = NtQueryVirtualMemory(g_hProcess,
+ (void const *)uPtrWhere,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p: rcNt=%#x\n", uPtrWhere, rcNt);
+ break;
+ }
+
+ /* stringify the memory state. */
+ char szMemType[1024];
+ char szMemState[1024];
+ char szMemProt[1024];
+ char szAllocProt[1024];
+
+ if ( MemInfo.AllocationBase != NULL
+ && MemInfo.AllocationBase == MemInfo.BaseAddress
+ && MemInfo.Protect != MemInfo.AllocationProtect)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "\n");
+
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p-%p %-8s %-8s %-12s",
+ MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
+ stringifyMemType(MemInfo.Type, szMemType, sizeof(szMemType)),
+ stringifyMemState(MemInfo.State, szMemState, sizeof(szMemState)),
+ stringifyMemProt(MemInfo.Protect, szMemProt, sizeof(szMemProt))
+ );
+ if ((uintptr_t)MemInfo.AllocationBase != 0)
+ {
+ if (MemInfo.AllocationBase != MemInfo.BaseAddress)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %p", MemInfo.AllocationBase);
+ else
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %s", stringifyMemProt(MemInfo.AllocationProtect, szAllocProt, sizeof(szAllocProt)));
+ }
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "\n");
+
+ if ((uintptr_t)MemInfo.BaseAddress != uPtrWhere)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " !Warning! Queried %p got BaseAddress=%p!\n",
+ uPtrWhere, MemInfo.BaseAddress);
+
+ /* Image or mapped, then try get a file name. */
+ if (MemInfo.Type == MEM_IMAGE || MemInfo.Type == MEM_MAPPED)
+ {
+ union
+ {
+ MEMORY_SECTION_NAME Core;
+ WCHAR awcPadding[UNICODE_STRING_MAX_CHARS + (sizeof(UNICODE_STRING_MAX_CHARS) + 1) / sizeof(WCHAR)];
+ } uBuf;
+ RT_ZERO(uBuf);
+ uBuf.Core.SectionFileName.Length = UNICODE_STRING_MAX_CHARS * 2;
+ uBuf.Core.SectionFileName.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ uBuf.Core.SectionFileName.Buffer = &uBuf.Core.NameBuffer[0];
+
+ cbActual = 0;
+ rcNt = NtQueryVirtualMemory(g_hProcess,
+ (void const *)uPtrWhere,
+ MemorySectionName,
+ &uBuf,
+ sizeof(uBuf),
+ &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, " %.*ls\n",
+ uBuf.Core.SectionFileName.Length / 2, uBuf.Core.SectionFileName.Buffer);
+ else
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%p: MemorySectionName - rcNt=%#x\n", uPtrWhere, rcNt);
+ RTTESTI_CHECK(rcNt == STATUS_FILE_INVALID && MemInfo.Type == MEM_MAPPED);
+ }
+ }
+
+ /* Advance. */
+ cbAdvance = MemInfo.RegionSize;
+ //cbAdvance = 0;
+ if (uPtrWhere + cbAdvance <= uPtrWhere)
+ break;
+ uPtrWhere += MemInfo.RegionSize;
+ }
+}
+
+
+static void tstQueryInformationProcess(void)
+{
+ RTTestISub("NtQueryInformationProcess");
+
+ NTSTATUS rcNt;
+
+ /* Basic info */
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ RT_ZERO(BasicInfo);
+ DWORD cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessBasicInformation,
+ &BasicInfo, sizeof(BasicInfo), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "BasicInfo:\n"
+ " UniqueProcessId = %#x (%6d)\n"
+ " InheritedFromUniqueProcessId = %#x (%6d)\n"
+ " ExitStatus = %#x\n"
+ " PebBaseAddress = %p\n"
+ " AffinityMask = %#zx\n"
+ " BasePriority = %#zx\n"
+ ,
+ BasicInfo.UniqueProcessId, BasicInfo.UniqueProcessId,
+ BasicInfo.InheritedFromUniqueProcessId, BasicInfo.InheritedFromUniqueProcessId,
+ BasicInfo.ExitStatus,
+ BasicInfo.PebBaseAddress,
+ BasicInfo.AffinityMask,
+ BasicInfo.BasePriority
+ );
+
+ /* Debugger present? */
+ DWORD_PTR uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessDebugPort,
+ &uPtr, sizeof(uPtr), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugPort: %p\n", uPtr);
+
+ /* Debug object handle, whatever that is... */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessDebugObjectHandle,
+ &uPtr, sizeof(uPtr), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugObjectHandle: %p\n", uPtr);
+ else if (rcNt == STATUS_PORT_NOT_SET)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessDebugObjectHandle: rcNt=%#x (STATUS_PORT_NOT_SET)\n", uPtr);
+ else
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+
+ /* 32-bit app on 64-bit host? */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessWow64Information,
+ &uPtr, sizeof(uPtr), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessWow64Information: %p\n", uPtr);
+
+ /* Process image name (NT). */
+ struct
+ {
+ UNICODE_STRING UniStr;
+ WCHAR awBuffer[UNICODE_STRING_MAX_CHARS];
+ } StrBuf;
+ RT_ZERO(StrBuf);
+ StrBuf.UniStr.Length = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.Buffer = &StrBuf.awBuffer[0];
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileName,
+ &StrBuf, sizeof(StrBuf), &cbActual);
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileName: len=%u\n %.*ls\n",
+ StrBuf.UniStr.Length, StrBuf.UniStr.Length, StrBuf.UniStr.Buffer);
+
+ /* Process image name (Win32) - Not available on Windows 2003. */
+ RT_ZERO(StrBuf);
+ StrBuf.UniStr.Length = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.MaximumLength = UNICODE_STRING_MAX_CHARS * 2;
+ StrBuf.UniStr.Buffer = &StrBuf.awBuffer[0];
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileNameWin32,
+ &StrBuf, sizeof(StrBuf), &cbActual);
+ if (rcNt != STATUS_INVALID_INFO_CLASS)
+ {
+ RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileNameWin32: len=%u\n %.*ls\n",
+ StrBuf.UniStr.Length, StrBuf.UniStr.Length, StrBuf.UniStr.Buffer);
+ }
+ else
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileNameWin32: Not supported (STATUS_INVALID_INFO_CLASS).\n");
+
+ /* Process image mapping - Not available on Windows 2003. */
+ uPtr = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessImageFileMapping,
+ &uPtr, sizeof(uPtr), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: %p\n", uPtr);
+ else if (rcNt == STATUS_OBJECT_TYPE_MISMATCH)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: rcNt=%#x (STATUS_OBJECT_TYPE_MISMATCH)\n", rcNt);
+ else if (rcNt == STATUS_INVALID_INFO_CLASS)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessImageFileMapping: Not supported (STATUS_INVALID_INFO_CLASS).\n");
+ else
+ RTTestIFailed("ProcessImageFileMapping: rcNt=%#x\n", rcNt);
+
+
+ /* Handles. Broken for 64-bit input. */
+ uint32_t u32 = UINT32_MAX;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessHandleCount,
+ &u32, sizeof(u32), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessHandleCount: %#x (%d)\n", u32, u32);
+ else
+ RTTestIFailed("ProcessHandleCount: rcNt=%#x\n", rcNt);
+
+ /* Execute flags. */
+#if 0 /* fails... wrong process handle? */
+ u32 = ~(DWORD_PTR)0;
+ cbActual = 0;
+ rcNt = NtQueryInformationProcess(g_hProcess,
+ ProcessExecuteFlags,
+ &u32, sizeof(u32), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "ProcessExecuteFlags: %#p\n", u32);
+ else
+ RTTestIFailed("ProcessExecuteFlags: rcNt=%#x\n", rcNt);
+#endif
+
+ /** @todo ProcessImageInformation */
+}
+
+
+int main(int argc, char **argv)
+{
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstNtQueryStuff", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+
+ g_hProcess = GetCurrentProcess();
+ if (argc >= 2 && argv[1][0] != '-')
+ {
+ const char *pszPid = argv[1];
+ uint32_t idPid = RTStrToInt32(pszPid);
+
+ uint32_t fAccess = PROCESS_QUERY_INFORMATION;
+ if (argc >= 3)
+ fAccess = RTStrToInt32(argv[2]);
+
+ g_hProcess = OpenProcess(fAccess, FALSE, idPid);
+ if (g_hProcess == NULL)
+ {
+ RTTestIFailed("Error %u opening process %u (%s)\n", GetLastError(), idPid, pszPid);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ }
+
+ tstQueryVirtualMemory();
+ tstQueryInformationProcess();
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstPage.cpp b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp
new file mode 100644
index 00000000..2104d56f
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp
@@ -0,0 +1,101 @@
+/* $Id: tstPage.cpp $ */
+/** @file
+ * SUP Testcase - Page allocation interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <string.h>
+
+
+int main(int argc, char **argv)
+{
+ int cErrors = 0;
+
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ int rc = SUPR3Init(NULL);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ void *pv;
+ rc = SUPR3PageAlloc(1, 0, &pv);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ memset(pv, 0xff, PAGE_SIZE);
+ rc = SUPR3PageFree(pv, 1);
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3PageFree() failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3PageAlloc(1,) failed rc=%Rrc\n", rc);
+
+ /*
+ * Big chunk.
+ */
+ rc = SUPR3PageAlloc(1023, 0, &pv);
+ cErrors += rc != 0;
+ if (!rc)
+ {
+ memset(pv, 0xfe, 1023 << PAGE_SHIFT);
+ rc = SUPR3PageFree(pv, 1023);
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3PageFree() failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3PageAlloc(1,) failed rc=%Rrc\n", rc);
+
+
+ //rc = SUPR3Term();
+ cErrors += rc != 0;
+ if (rc)
+ RTPrintf("tstPage: SUPR3Term failed rc=%Rrc\n", rc);
+ }
+ else
+ RTPrintf("tstPage: SUPR3Init failed rc=%Rrc\n", rc);
+
+ if (!cErrors)
+ RTPrintf("tstPage: SUCCESS\n");
+ else
+ RTPrintf("tstPage: FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstPin.cpp b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp
new file mode 100644
index 00000000..33d7c131
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp
@@ -0,0 +1,223 @@
+/* $Id: tstPin.cpp $ */
+/** @file
+ * SUP Testcase - Memory locking interface (ring 3).
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <VBox/param.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/string.h>
+
+#include "../SUPLibInternal.h"
+
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int rcRet = 0;
+ RTHCPHYS HCPhys;
+
+ RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ rc = SUPR3Init(NULL);
+ RTPrintf("SUPR3Init -> rc=%d\n", rc);
+ rcRet += rc != 0;
+ if (!rc)
+ {
+ /*
+ * Simple test.
+ */
+ void *pv;
+ rc = SUPR3PageAlloc(1, 0, &pv);
+ AssertRC(rc);
+ RTPrintf("pv=%p\n", pv);
+ SUPPAGE aPages[1];
+ rc = supR3PageLock(pv, 1, &aPages[0]);
+ RTPrintf("rc=%d pv=%p aPages[0]=%RHp\n", rc, pv, aPages[0]);
+ RTThreadSleep(1500);
+#if 0
+ RTPrintf("Unlocking...\n");
+ RTThreadSleep(250);
+ rc = SUPPageUnlock(pv);
+ RTPrintf("rc=%d\n", rc);
+ RTThreadSleep(1500);
+#endif
+
+ /*
+ * More extensive.
+ */
+ static struct
+ {
+ void *pv;
+ void *pvAligned;
+ SUPPAGE aPages[16];
+ } aPinnings[500];
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i++)
+ {
+ aPinnings[i].pv = NULL;
+ SUPR3PageAlloc(0x10000 >> PAGE_SHIFT, 0, &aPinnings[i].pv);
+ aPinnings[i].pvAligned = RT_ALIGN_P(aPinnings[i].pv, PAGE_SIZE);
+ rc = supR3PageLock(aPinnings[i].pvAligned, 0xf000 >> PAGE_SHIFT, &aPinnings[i].aPages[0]);
+ if (!rc)
+ {
+ RTPrintf("i=%d: pvAligned=%p pv=%p:\n", i, aPinnings[i].pvAligned, aPinnings[i].pv);
+ memset(aPinnings[i].pv, 0xfa, 0x10000);
+ unsigned c4GPluss = 0;
+ for (unsigned j = 0; j < (0xf000 >> PAGE_SHIFT); j++)
+ if (aPinnings[i].aPages[j].Phys >= _4G)
+ {
+ RTPrintf("%2d: vrt=%p phys=%RHp\n", j, (char *)aPinnings[i].pvAligned + (j << PAGE_SHIFT), aPinnings[i].aPages[j].Phys);
+ c4GPluss++;
+ }
+ RTPrintf("i=%d: c4GPluss=%d\n", i, c4GPluss);
+ }
+ else
+ {
+ RTPrintf("SUPPageLock -> rc=%d\n", rc);
+ rcRet++;
+ SUPR3PageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT);
+ aPinnings[i].pv = aPinnings[i].pvAligned = NULL;
+ break;
+ }
+ }
+
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2)
+ {
+ if (aPinnings[i].pvAligned)
+ {
+ rc = supR3PageUnlock(aPinnings[i].pvAligned);
+ if (rc)
+ {
+ RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", aPinnings[i].pvAligned, rc);
+ rcRet++;
+ }
+ memset(aPinnings[i].pv, 0xaf, 0x10000);
+ }
+ }
+
+ for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2)
+ {
+ if (aPinnings[i].pv)
+ {
+ memset(aPinnings[i].pv, 0xcc, 0x10000);
+ SUPR3PageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT);
+ aPinnings[i].pv = NULL;
+ }
+ }
+
+
+/* Support for allocating Ring-0 executable memory with contiguous physical backing isn't implemented on Solaris. */
+#if !defined(RT_OS_SOLARIS)
+ /*
+ * Allocate a bit of contiguous memory.
+ */
+ pv = SUPR3ContAlloc(RT_ALIGN_Z(15003, PAGE_SIZE) >> PAGE_SHIFT, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ RTPrintf("SUPR3ContAlloc(15003) -> HCPhys=%llx pv=%p\n", HCPhys, pv);
+ void *pv0 = pv;
+ memset(pv0, 0xaf, 15003);
+ pv = SUPR3ContAlloc(RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT, NULL, &HCPhys);
+ rcRet += pv == NULL || HCPhys == 0;
+ if (pv && HCPhys)
+ {
+ RTPrintf("SUPR3ContAlloc(12999) -> HCPhys=%llx pv=%p\n", HCPhys, pv);
+ memset(pv, 0xbf, 12999);
+ rc = SUPR3ContFree(pv, RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT);
+ rcRet += rc != 0;
+ if (rc)
+ RTPrintf("SUPR3ContFree failed! rc=%d\n", rc);
+ }
+ else
+ RTPrintf("SUPR3ContAlloc (2nd) failed!\n");
+ memset(pv0, 0xaf, 15003);
+ /* pv0 is intentionally not freed! */
+ }
+ else
+ RTPrintf("SUPR3ContAlloc failed!\n");
+#endif
+
+ /*
+ * Allocate a big chunk of virtual memory and then lock it.
+ */
+ #define BIG_SIZE 72*1024*1024
+ #define BIG_SIZEPP (BIG_SIZE + PAGE_SIZE)
+ pv = NULL;
+ SUPR3PageAlloc(BIG_SIZEPP >> PAGE_SHIFT, 0, &pv);
+ if (pv)
+ {
+ static SUPPAGE s_aPages[BIG_SIZE >> PAGE_SHIFT];
+ void *pvAligned = RT_ALIGN_P(pv, PAGE_SIZE);
+ rc = supR3PageLock(pvAligned, BIG_SIZE >> PAGE_SHIFT, &s_aPages[0]);
+ if (!rc)
+ {
+ /* dump */
+ RTPrintf("SUPPageLock(%p,%d,) succeeded!\n", pvAligned, BIG_SIZE);
+ memset(pv, 0x42, BIG_SIZEPP);
+ #if 0
+ for (unsigned j = 0; j < (BIG_SIZE >> PAGE_SHIFT); j++)
+ RTPrintf("%2d: vrt=%p phys=%08x\n", j, (char *)pvAligned + (j << PAGE_SHIFT), (uintptr_t)s_aPages[j].pvPhys);
+ #endif
+
+ /* unlock */
+ rc = supR3PageUnlock(pvAligned);
+ if (rc)
+ {
+ RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", pvAligned, rc);
+ rcRet++;
+ }
+ memset(pv, 0xcc, BIG_SIZEPP);
+ }
+ else
+ {
+ RTPrintf("SUPPageLock(%p) -> rc=%d\n", pvAligned, rc);
+ rcRet++;
+ }
+ SUPR3PageFree(pv, BIG_SIZEPP >> PAGE_SHIFT);
+ }
+
+ rc = SUPR3Term(false /*fForced*/);
+ RTPrintf("SUPR3Term -> rc=%d\n", rc);
+ rcRet += rc != 0;
+ }
+
+ return rcRet;
+}
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp
new file mode 100644
index 00000000..844cc552
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupLoadModule.cpp
@@ -0,0 +1,128 @@
+/* $Id: tstSupLoadModule.cpp $ */
+/** @file
+ * SUP Testcase - Test SUPR3LoadModule.
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Process arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--keep", 'k', RTGETOPT_REQ_NOTHING },
+ { "--no-keep", 'n', RTGETOPT_REQ_NOTHING },
+ };
+
+ bool fKeepLoaded = false;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ void *pvImageBase;
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ rc = SUPR3LoadModule(ValueUnion.psz, RTPathFilename(ValueUnion.psz), &pvImageBase, &ErrInfo.Core);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("%Rrc when attempting to load '%s': %s\n", rc, ValueUnion.psz, ErrInfo.Core.pszMsg);
+ return 1;
+ }
+ RTPrintf("Loaded '%s' at %p\n", ValueUnion.psz, pvImageBase);
+
+ if (!fKeepLoaded)
+ {
+ rc = SUPR3FreeModule(pvImageBase);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("%Rrc when attempting to load '%s'\n", rc, ValueUnion.psz);
+ return 1;
+ }
+ }
+ break;
+ }
+
+ case 'k':
+ fKeepLoaded = true;
+ break;
+
+ case 'n':
+ fKeepLoaded = false;
+ break;
+
+ case 'h':
+ RTPrintf("%s [mod1 [mod2...]]\n", argv[0]);
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp
new file mode 100644
index 00000000..fff1795c
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupSem-Zombie.cpp
@@ -0,0 +1,232 @@
+/* $Id: tstSupSem-Zombie.cpp $ */
+/** @file
+ * Support Library Testcase - Ring-3 Semaphore interface - Zombie bugs.
+ */
+
+/*
+ * 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 <VBox/sup.h>
+
+#include <VBox/param.h>
+#include <iprt/env.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+static PSUPDRVSESSION g_pSession;
+static RTTEST g_hTest;
+
+
+
+static DECLCALLBACK(int) tstSupSemSRETimed(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventWaitNoResume(g_pSession, hEvent, 120*1000);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemMRETimed(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, 120*1000);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemSREInf(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventWaitNoResume(g_pSession, hEvent, RT_INDEFINITE_WAIT);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+
+static DECLCALLBACK(int) tstSupSemMREInf(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ int rc = SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, RT_INDEFINITE_WAIT);
+ AssertReleaseMsg(rc == VERR_INTERRUPTED, ("%Rrc\n", rc));
+ return rc;
+}
+
+static int mainChild(void)
+{
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExeNoArguments(RTR3INIT_FLAGS_TRY_SUPLIB);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem-Zombie-Child: fatal error: RTR3InitExeNoArguments failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+
+ RTTEST hTest;
+ rc = RTTestCreate("tstSupSem-Zombie-Child", &hTest);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem-Zombie-Child: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+ g_hTest = hTest;
+
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(hTest, "SUPR3Init failed with rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ g_pSession = pSession;
+
+ /*
+ * A semaphore of each kind and throw a bunch of threads on them.
+ */
+ SUPSEMEVENT hEvent = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(rc = SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ SUPSEMEVENTMULTI hEventMulti = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t cThreads = 0; cThreads < 5; cThreads++)
+ {
+ RTTHREAD hThread;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemSRETimed, (void *)hEvent, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemMRETimed, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemSREInf, (void *)hEvent, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemMREInf, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, 0 /*fFlags*/, "IntMRE"), VINF_SUCCESS);
+ RTThreadSleep(2);
+ }
+ RTThreadSleep(50);
+
+ /*
+ * This is where the test really starts...
+ */
+ return 0;
+ }
+ }
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
+
+/**
+ * The parent main routine.
+ * @param argv0 The executable name (or whatever).
+ */
+static int mainParent(const char *argv0)
+{
+ /*
+ * Init.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstSupSem-Zombie", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ /*
+ * Spin of the child process which may or may not turn into a zombie
+ */
+ for (uint32_t iPass = 0; iPass < 32; iPass++)
+ {
+ RTTestSubF(hTest, "Pass %u", iPass);
+
+ RTPROCESS hProcess;
+ const char *apszArgs[3] = { argv0, "--child", NULL };
+ RTTESTI_CHECK_RC_OK(rc = RTProcCreate(argv0, apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProcess));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for 60 seconds then give up.
+ */
+ RTPROCSTATUS Status;
+ uint64_t StartTS = RTTimeMilliTS();
+ for (;;)
+ {
+ rc = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &Status);
+ if (RT_SUCCESS(rc))
+ break;
+ uint64_t cElapsed = RTTimeMilliTS() - StartTS;
+ if (cElapsed > 60*1000)
+ break;
+ RTThreadSleep(cElapsed < 60 ? 30 : cElapsed < 200 ? 10 : 100);
+ }
+ RTTESTI_CHECK_RC_OK(rc);
+ if ( RT_SUCCESS(rc)
+ && ( Status.enmReason != RTPROCEXITREASON_NORMAL
+ || Status.iStatus != 0))
+ {
+ RTTestIFailed("child %d (%#x) reason %d\n", Status.iStatus, Status.iStatus, Status.enmReason);
+ rc = VERR_PERMISSION_DENIED;
+ }
+ }
+ /* one zombie process is enough. */
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
+
+int main(int argc, char **argv)
+{
+ if ( argc == 2
+ && !strcmp(argv[1], "--child"))
+ return mainChild();
+ return mainParent(argv[0]);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp
new file mode 100644
index 00000000..3091d67a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupSem.cpp
@@ -0,0 +1,649 @@
+/* $Id: tstSupSem.cpp $ */
+/** @file
+ * Support Library Testcase - Ring-3 Semaphore interface.
+ */
+
+/*
+ * 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 <VBox/sup.h>
+
+#include <VBox/param.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+static PSUPDRVSESSION g_pSession;
+static RTTEST g_hTest;
+static uint32_t g_cMillies; /* Used by the interruptible tests. */
+
+
+
+static DECLCALLBACK(int) tstSupSemInterruptibleSRE(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENT hEvent = (SUPSEMEVENT)pvUser;
+ RTThreadUserSignal(hSelf);
+ return SUPSemEventWaitNoResume(g_pSession, hEvent, g_cMillies);
+}
+
+
+static DECLCALLBACK(int) tstSupSemInterruptibleMRE(RTTHREAD hSelf, void *pvUser)
+{
+ SUPSEMEVENTMULTI hEventMulti = (SUPSEMEVENTMULTI)pvUser;
+ RTThreadUserSignal(hSelf);
+ return SUPSemEventMultiWaitNoResume(g_pSession, hEventMulti, g_cMillies);
+}
+
+
+int main(int argc, char **argv)
+{
+ bool fSys = true;
+ bool fGip = false;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fGip = true;
+#endif
+
+ /*
+ * Init.
+ */
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ if (argc == 2 && !strcmp(argv[1], "child"))
+ {
+ RTThreadSleep(300);
+ return 0;
+ }
+
+ RTTEST hTest;
+ rc = RTTestCreate("tstSupSem", &hTest);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstSupSem: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
+ return 1;
+ }
+ g_hTest = hTest;
+
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(hTest, "SUPR3Init failed with rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ g_pSession = pSession;
+ RTTestBanner(hTest);
+
+ /*
+ * Basic API checks.
+ */
+ RTTestSub(hTest, "Single Release Event (SRE) API");
+ SUPSEMEVENT hEvent = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,1000),VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VERR_INVALID_HANDLE);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, NIL_SUPSEMEVENT), VINF_SUCCESS);
+
+ RTTestSub(hTest, "Multiple Release Event (MRE) API");
+ SUPSEMEVENTMULTI hEventMulti = NIL_SUPSEMEVENT;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiReset(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 20), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VERR_INVALID_HANDLE);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, NIL_SUPSEMEVENTMULTI), VINF_SUCCESS);
+
+#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
+ RTTestSub(hTest, "SRE Interruptibility");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ g_cMillies = RT_INDEFINITE_WAIT;
+ RTTHREAD hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ int rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ g_cMillies = 120*1000;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+
+ RTTestSub(hTest, "MRE Interruptibility");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ g_cMillies = RT_INDEFINITE_WAIT;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
+ g_cMillies = 120*1000;
+ hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
+ RTThreadSleep(120);
+ RTThreadPoke(hThread);
+ rcThread = VINF_SUCCESS;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);
+
+ /*
+ * Fork test.
+ * Spawn a thread waiting for an event, then spawn a new child process (of
+ * ourselves) and make sure that this does not alter the intended behaviour
+ * of our event semaphore implementation (see @bugref{5090}).
+ */
+ RTTestSub(hTest, "SRE Process Spawn");
+ hThread = NIL_RTTHREAD;
+ g_cMillies = 120*1000;
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ const char *apszArgs[3] = { argv[0], "child", NULL };
+ RTPROCESS Process = NIL_RTPROCESS;
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);
+
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);
+
+ rcThread = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+
+ RTTestSub(hTest, "MRE Process Spawn");
+ hThread = NIL_RTTHREAD;
+ g_cMillies = 120*1000;
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ RTTHREAD hThread2 = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(RTThreadCreate(&hThread2, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
+
+ Process = NIL_RTPROCESS;
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);
+
+ RTThreadSleep(250);
+ RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEvent), VINF_SUCCESS);
+
+ rcThread = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
+
+ int rcThread2 = VERR_GENERAL_FAILURE;
+ RTTESTI_CHECK_RC(RTThreadWait(hThread2, 120*1000, &rcThread2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(rcThread2, VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+
+#endif /* !OS2 && !WINDOWS */
+
+ {
+
+#define LOOP_COUNT 20
+ static unsigned const s_acMsIntervals[] = { 0, 1, 2, 3, 4, 8, 10, 16, 32 };
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SRE Timeout Accuracy (ms)");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
+ {
+ uint64_t cMs = s_acMsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventWaitNoResume(pSession, hEvent, cMs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%u ms min (clock=sys)", cMs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%u ms min (clock=gip)", cMs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=gip)", cMs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "MRE Timeout Accuracy (ms)");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
+ {
+ uint64_t cMs = s_acMsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventMultiWaitNoResume(pSession, hEvent, cMs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%u ms min (clock=sys)", cMs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%u ms min (clock=gip)", cMs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=gip)", cMs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+ }
+
+ {
+ static uint32_t const s_acNsIntervals[] =
+ {
+ 0, 1000, 5000, 15000, 30000, 50000, 100000, 250000, 500000, 750000, 900000, 1500000, 2200000
+ };
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventWaitNsRelIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "SRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventWaitNsRelIntr(pSession, hEvent, cNs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventMultiWaitNsRelIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ int rcX = SUPSemEventMultiWaitNsRelIntr(pSession, hEvent, cNs);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventWaitNsAbsIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ uint64_t uAbsDeadline = (fGip ? u64Start : u64StartSys) + cNs;
+ int rcX = SUPSemEventWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+
+ if (RTTestErrorCount(hTest) == 0)
+ {
+ RTTestSub(hTest, "SUPSemEventMultiWaitNsAbsIntr Accuracy");
+ RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
+ RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
+
+ uint32_t cInterrupted = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
+ {
+ uint64_t cNs = s_acNsIntervals[i];
+ uint64_t cNsMinSys = UINT64_MAX;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsTotalSys= 0;
+ uint64_t cNsTotal = 0;
+ unsigned cLoops = 0;
+ while (cLoops < LOOP_COUNT)
+ {
+ uint64_t u64StartSys = RTTimeSystemNanoTS();
+ uint64_t u64Start = RTTimeNanoTS();
+ uint64_t uAbsDeadline = (fGip ? u64Start : u64StartSys) + cNs;
+ int rcX = SUPSemEventMultiWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
+ uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
+ uint64_t cNsElapsed = RTTimeNanoTS() - u64Start;
+
+ if (rcX == VERR_INTERRUPTED)
+ {
+ cInterrupted++;
+ continue; /* retry */
+ }
+ if (rcX != VERR_TIMEOUT)
+ RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);
+
+ if (cNsElapsedSys < cNsMinSys)
+ cNsMinSys = cNsElapsedSys;
+ if (cNsElapsed < cNsMin)
+ cNsMin = cNsElapsed;
+ cNsTotalSys += cNsElapsedSys;
+ cNsTotal += cNsElapsed;
+ cLoops++;
+ }
+ if (fSys)
+ {
+ RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS, "%'u ns min (clock=sys)", cNs);
+ RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
+ }
+ if (fGip)
+ {
+ RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS, "%'u ns min (clock=gip)", cNs);
+ RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=gip)", cNs);
+ }
+ }
+
+ RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
+ RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
+ }
+
+ }
+
+
+ /*
+ * Done.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp
new file mode 100644
index 00000000..4fe0d8d4
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupTscDelta.cpp
@@ -0,0 +1,235 @@
+/* $Id: tstSupTscDelta.cpp $ */
+/** @file
+ * SUP Testcase - Global Info Page TSC Delta Measurement Utility.
+ */
+
+/*
+ * Copyright (C) 2015-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/getopt.h>
+#include <iprt/test.h>
+#include <iprt/thread.h>
+
+
+
+int main(int argc, char **argv)
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, 0 /*fRtInit*/, "tstSupTscDelta", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Parse args
+ */
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--iterations", 'i', RTGETOPT_REQ_INT32 },
+ { "--delay", 'd', RTGETOPT_REQ_INT32 },
+ };
+
+ uint32_t cIterations = 0; /* Currently 0 so that it doesn't upset testing. */
+ uint32_t cMsSleepBetweenIterations = 10;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'd':
+ cMsSleepBetweenIterations = ValueUnion.u32;
+ break;
+ case 'i':
+ cIterations = ValueUnion.u32;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!cIterations)
+ return RTTestSkipAndDestroy(hTest, "Nothing to do. The --iterations argument is 0 or not given.");
+
+ /*
+ * Init
+ */
+ PSUPDRVSESSION pSession = NIL_RTR0PTR;
+ int rc = SUPR3Init(&pSession);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if (pGip)
+ {
+ if (pGip->enmUseTscDelta < SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ return RTTestSkipAndDestroy(hTest, "No deltas to play with: enmUseTscDelta=%d\n", pGip->enmUseTscDelta);
+
+ /*
+ * Init stats.
+ */
+ struct
+ {
+ int64_t iLowest;
+ int64_t iHighest;
+ int64_t iTotal;
+ uint64_t uAbsMin;
+ uint64_t uAbsMax;
+ uint64_t uAbsTotal;
+ } aCpuStats[RTCPUSET_MAX_CPUS];
+ RT_ZERO(aCpuStats);
+ for (uint32_t i = 0; i < pGip->cCpus; i++)
+ {
+ aCpuStats[i].iLowest = INT64_MAX;
+ aCpuStats[i].iHighest = INT64_MIN;
+ aCpuStats[i].uAbsMin = UINT64_MAX;
+ }
+
+ /*
+ * Do the work.
+ */
+ for (uint32_t iIteration = 0; ; iIteration++)
+ {
+ /*
+ * Display the current deltas and gather statistics.
+ */
+ RTPrintf("tstSupTscDelta: Iteration #%u results:", iIteration);
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ int64_t iTscDelta = pGip->aCPUs[iCpu].i64TSCDelta;
+
+ /* print */
+ if ((iCpu % 4) == 0)
+ RTPrintf("\ntstSupTscDelta:");
+ if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
+ RTPrintf(" %02x: offline ", iCpu);
+ else if (iTscDelta != INT64_MAX)
+ RTPrintf(" %02x: %-12lld", iCpu, iTscDelta);
+ else
+ RTPrintf(" %02x: INT64_MAX ", iCpu);
+
+ /* stats */
+ if ( iTscDelta != INT64_MAX
+ && pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ if (aCpuStats[iCpu].iLowest > iTscDelta)
+ aCpuStats[iCpu].iLowest = iTscDelta;
+ if (aCpuStats[iCpu].iHighest < iTscDelta)
+ aCpuStats[iCpu].iHighest = iTscDelta;
+ aCpuStats[iCpu].iTotal += iTscDelta;
+
+ uint64_t uAbsTscDelta = iTscDelta >= 0 ? (uint64_t)iTscDelta : (uint64_t)-iTscDelta;
+ if (aCpuStats[iCpu].uAbsMin > uAbsTscDelta)
+ aCpuStats[iCpu].uAbsMin = uAbsTscDelta;
+ if (aCpuStats[iCpu].uAbsMax < uAbsTscDelta)
+ aCpuStats[iCpu].uAbsMax = uAbsTscDelta;
+ aCpuStats[iCpu].uAbsTotal += uAbsTscDelta;
+ }
+ }
+ if (((pGip->cCpus - 1) % 4) != 0)
+ RTPrintf("\n");
+
+ /*
+ * Done?
+ */
+ if (iIteration + 1 >= cIterations)
+ break;
+
+ /*
+ * Force a new measurement.
+ */
+ RTThreadSleep(cMsSleepBetweenIterations);
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ if (pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
+ {
+ rc = SUPR3TscDeltaMeasure(pGip->aCPUs[iCpu].idCpu, false /*fAsync*/, true /*fForce*/, 64, 16 /*ms*/);
+ if (RT_FAILURE(rc))
+ RTTestFailed(hTest, "SUPR3TscDeltaMeasure failed on %#x: %Rrc", pGip->aCPUs[iCpu].idCpu, rc);
+ }
+ }
+
+ /*
+ * Display statistics that we've gathered.
+ */
+ RTPrintf("tstSupTscDelta: Results:\n");
+ int64_t iLowest = INT64_MAX;
+ int64_t iHighest = INT64_MIN;
+ int64_t iTotal = 0;
+ uint32_t cTotal = 0;
+ for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
+ {
+ if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
+ RTPrintf("tstSupTscDelta: %02x: offline\n", iCpu);
+ else
+ {
+ RTPrintf("tstSupTscDelta: %02x: lowest=%-12lld highest=%-12lld average=%-12lld spread=%-12lld\n",
+ iCpu,
+ aCpuStats[iCpu].iLowest,
+ aCpuStats[iCpu].iHighest,
+ aCpuStats[iCpu].iTotal / cIterations,
+ aCpuStats[iCpu].iHighest - aCpuStats[iCpu].iLowest);
+ RTPrintf( "tstSupTscDelta: absmin=%-12llu absmax=%-12llu absavg=%-12llu idCpu=%#4x idApic=%#4x\n",
+ aCpuStats[iCpu].uAbsMin,
+ aCpuStats[iCpu].uAbsMax,
+ aCpuStats[iCpu].uAbsTotal / cIterations,
+ pGip->aCPUs[iCpu].idCpu,
+ pGip->aCPUs[iCpu].idApic);
+ if (iLowest > aCpuStats[iCpu].iLowest)
+ iLowest = aCpuStats[iCpu].iLowest;
+ if (iHighest < aCpuStats[iCpu].iHighest)
+ iHighest = aCpuStats[iCpu].iHighest;
+ iTotal += aCpuStats[iCpu].iHighest;
+ cTotal += cIterations;
+ }
+ }
+ RTPrintf("tstSupTscDelta: all: lowest=%-12lld highest=%-12lld average=%-12lld spread=%-12lld\n",
+ iLowest, iHighest, iTotal / cTotal, iHighest - iLowest);
+ }
+ else
+ RTTestFailed(hTest, "g_pSUPGlobalInfoPage is NULL");
+
+ SUPR3Term(false /*fForced*/);
+ }
+ else
+ RTTestFailed(hTest, "SUPR3Init failed: %Rrc", rc);
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp b/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp
new file mode 100644
index 00000000..b8f157d8
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/testcase/tstSupVerify.cpp
@@ -0,0 +1,162 @@
+/* $Id: tstSupVerify.cpp $ */
+/** @file
+ * SUP Testcase - Test SUPR3HardenedVerifyPlugIn.
+ */
+
+/*
+ * Copyright (C) 2006-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 <VBox/sup.h>
+#include <iprt/errcore.h>
+
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+
+
+//#define DYNAMIC
+#ifdef DYNAMIC
+# include <iprt/win/windows.h>
+
+# define DYNAMIC_IMPORTS() \
+ ONE_IMPORT(RTR3InitExe); \
+ ONE_IMPORT(RTMsgInitFailure); \
+ ONE_IMPORT(RTGetOpt); \
+ ONE_IMPORT(RTGetOptInit); \
+ ONE_IMPORT(RTGetOptPrintError); \
+ ONE_IMPORT(RTMsgError); \
+ ONE_IMPORT(RTMsgErrorExit); \
+ ONE_IMPORT(RTMsgInfo); \
+ ONE_IMPORT(RTPrintf); \
+ ONE_IMPORT(SUPR3HardenedVerifyInit); \
+ ONE_IMPORT(SUPR3HardenedVerifyPlugIn)
+
+# define ONE_IMPORT(a_fnName) static decltype(a_fnName) *g_pfn##a_fnName
+DYNAMIC_IMPORTS();
+# undef ONE_IMPORT
+
+static void resolve(void)
+{
+ HMODULE hmod = LoadLibrary("VBoxRT.dll");
+ DWORD cbWritten = 0;
+
+# define ONE_IMPORT(a_fnName) do { \
+ g_pfn##a_fnName = (decltype(a_fnName) *)GetProcAddress(hmod, #a_fnName); \
+ if (!g_pfn##a_fnName) \
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), RT_STR_TUPLE("Failed to resolve: " #a_fnName "\r\n"), &cbWritten, NULL); \
+ } while (0)
+ DYNAMIC_IMPORTS();
+# undef ONE_IMPORT
+}
+
+#define RTR3InitExe g_pfnRTR3InitExe
+#define RTMsgInitFailure g_pfnRTMsgInitFailure
+#define RTGetOpt g_pfnRTGetOpt
+#define RTGetOptInit g_pfnRTGetOptInit
+#define RTGetOptPrintError g_pfnRTGetOptPrintError
+#define RTMsgError g_pfnRTMsgError
+#define RTMsgErrorExit g_pfnRTMsgErrorExit
+#define RTMsgInfo g_pfnRTMsgInfo
+#define RTPrintf g_pfnRTPrintf
+#define SUPR3HardenedVerifyInit g_pfnSUPR3HardenedVerifyInit
+#define SUPR3HardenedVerifyPlugIn g_pfnSUPR3HardenedVerifyPlugIn
+
+#endif /* DYNAMIC */
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init.
+ */
+#ifdef DYNAMIC
+ resolve();
+#endif
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ rc = SUPR3HardenedVerifyInit();
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "SUPR3HardenedVerifyInit failed: %Rrc", rc);
+
+ /*
+ * Process arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--dummy", 'd', RTGETOPT_REQ_NOTHING },
+ };
+
+ //bool fKeepLoaded = false;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ rc = SUPR3HardenedVerifyPlugIn(ValueUnion.psz, &ErrInfo.Core);
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("SUPR3HardenedVerifyPlugIn: %Rrc for '%s'\n", rc, ValueUnion.psz);
+ else
+ RTMsgError("SUPR3HardenedVerifyPlugIn: %Rrc for '%s' ErrInfo: %s\n",
+ rc, ValueUnion.psz, ErrInfo.Core.pszMsg);
+ break;
+ }
+
+ case 'h':
+ RTPrintf("%s [dll1 [dll2...]]\n", argv[0]);
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ return 0;
+}
+