/* $Id: VBoxCpuReport.cpp 112403 2026-01-11 19:29:08Z knut.osmundsen@oracle.com $ */ /** @file * VBoxCpuReport - Produces the basis for a CPU DB entry. */ /* * Copyright (C) 2013-2026 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 . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VBoxCpuReport.h" /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** The alternative report stream. */ PRTSTREAM g_pReportOut; /** The alternative debug stream. */ PRTSTREAM g_pDebugOut; /** The CPU vendor. Used by the MSR code. */ CPUMCPUVENDOR g_enmVendor = CPUMCPUVENDOR_INVALID; /** The CPU microarchitecture. Used by the MSR code. */ CPUMMICROARCH g_enmMicroarch = kCpumMicroarch_Invalid; /** Overrides the detected CPU name. * This is main for non-x86 hosts where the processor name string isn't * part of the silicone. */ const char *g_pszCpuNameOverride = NULL; void vbCpuRepDebug(const char *pszMsg, ...) { va_list va; /* Always print a copy of the report to standard error. */ va_start(va, pszMsg); RTStrmPrintfV(g_pStdErr, pszMsg, va); va_end(va); RTStrmFlush(g_pStdErr); /* Alternatively, also print to a log file. */ if (g_pDebugOut) { va_start(va, pszMsg); RTStrmPrintfV(g_pDebugOut, pszMsg, va); va_end(va); RTStrmFlush(g_pDebugOut); } /* Give the output device a chance to write / display it. */ RTThreadSleep(1); } void vbCpuRepPrintf(const char *pszMsg, ...) { va_list va; /* Output to report file, if requested. */ if (g_pReportOut) { va_start(va, pszMsg); RTStrmPrintfV(g_pReportOut, pszMsg, va); va_end(va); RTStrmFlush(g_pReportOut); } /* Always print a copy of the report to standard out. */ va_start(va, pszMsg); RTStrmPrintfV(g_pStdOut, pszMsg, va); va_end(va); RTStrmFlush(g_pStdOut); } /** Prints the file header. */ void vbCpuRepFileHdr(const char *pszName, const char *pszNameC) { RTTIMESPEC Now; char szNow[64]; RTTimeSpecToString(RTTimeNow(&Now), szNow, sizeof(szNow)); char *pchDot = strchr(szNow, '.'); if (pchDot) strcpy(pchDot, "Z"); vbCpuRepPrintf("/* $" "Id" "$ */\n" "/** @file\n" " * CPU database entry \"%s\".\n" " * Generated at %s by VBoxCpuReport v%sr%s on %s.%s.\n" " */\n" "\n" "/*\n" " * Copyright (C) 2013-" VBOX_C_YEAR " Oracle and/or its affiliates.\n" " *\n" " * This file is part of VirtualBox base platform packages, as\n" " * available from https://www.virtualbox.org.\n" " *\n" " * This program is free software; you can redistribute it and/or\n" " * modify it under the terms of the GNU General Public License\n" " * as published by the Free Software Foundation, in version 3 of the\n" " * License.\n" " *\n" " * This program is distributed in the hope that it will be useful, but\n" " * WITHOUT ANY WARRANTY; without even the implied warranty of\n" " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" " * General Public License for more details.\n" " *\n" " * You should have received a copy of the GNU General Public License\n" " * along with this program; if not, see .\n" " *\n" " * SPDX-License-Identifier: GPL-3.0-only\n" " */\n" "\n" "#ifndef VBOX_CPUDB_%s_h\n" "#define VBOX_CPUDB_%s_h\n" "#ifndef RT_WITHOUT_PRAGMA_ONCE\n" "# pragma once\n" "#endif\n" "\n", pszName, szNow, RTBldCfgVersion(), RTBldCfgRevisionStr(), RTBldCfgTarget(), RTBldCfgTargetArch(), pszNameC, pszNameC); } /** * Converts @a enmCpuVendor to the enum value name sans the typename prefix. */ const char *vbCpuVendorToString(CPUMCPUVENDOR enmCpuVendor) { switch (enmCpuVendor) { case CPUMCPUVENDOR_INTEL: return "INTEL"; case CPUMCPUVENDOR_AMD: return "AMD"; case CPUMCPUVENDOR_VIA: return "VIA"; case CPUMCPUVENDOR_CYRIX: return "CYRIX"; case CPUMCPUVENDOR_SHANGHAI: return "SHANGHAI"; case CPUMCPUVENDOR_HYGON: return "HYGON"; case CPUMCPUVENDOR_ARM: return "ARM"; case CPUMCPUVENDOR_BROADCOM: return "BROADCOM"; case CPUMCPUVENDOR_QUALCOMM: return "QUALCOMM"; case CPUMCPUVENDOR_APPLE: return "APPLE"; case CPUMCPUVENDOR_AMPERE: return "AMPERE"; case CPUMCPUVENDOR_INVALID: case CPUMCPUVENDOR_UNKNOWN: case CPUMCPUVENDOR_32BIT_HACK: break; } return "invalid-cpu-vendor"; } /** * Converts @a enmCoreType to the enum value name sans the typename prefix. */ const char *vbGetCoreTypeToString(CPUMCORETYPE enmCoreType) { switch (enmCoreType) { case kCpumCoreType_Efficiency: return "Efficiency"; case kCpumCoreType_Performance: return "Performance"; case kCpumCoreType_Unknown: return "Unknown"; case kCpumCoreType_Invalid: case kCpumCoreType_End: case kCpumCoreType_32BitHack: break; } return NULL; } int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, 0 /*fFlags*/); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); /* * Argument parsing? */ static const RTGETOPTDEF s_aOptions[] = { { "--cpu-name", 'c', RTGETOPT_REQ_STRING }, #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) { "--msrs-only", 'm', RTGETOPT_REQ_NOTHING }, { "--msrs-dev", 'd', RTGETOPT_REQ_NOTHING }, { "--no-msrs", 'n', RTGETOPT_REQ_NOTHING }, #endif { "--output", 'o', RTGETOPT_REQ_STRING }, { "--log", 'l', RTGETOPT_REQ_STRING }, }; RTGETOPTSTATE State; RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); enum { kCpuReportOp_Normal, #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) kCpuReportOp_MsrsOnly, kCpuReportOp_MsrsHacking #else kCpuReportOp_Dummy #endif } enmOp = kCpuReportOp_Normal; g_pReportOut = NULL; g_pDebugOut = NULL; const char *pszOutput = NULL; const char *pszDebugOut = NULL; int iOpt; RTGETOPTUNION ValueUnion; while ((iOpt = RTGetOpt(&State, &ValueUnion)) != 0) { switch (iOpt) { case 'c': g_pszCpuNameOverride = *ValueUnion.psz ? ValueUnion.psz : NULL; break; #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) case 'm': enmOp = kCpuReportOp_MsrsOnly; break; case 'd': enmOp = kCpuReportOp_MsrsHacking; break; case 'n': g_fNoMsrs = true; break; #endif case 'o': pszOutput = ValueUnion.psz; break; case 'l': pszDebugOut = ValueUnion.psz; break; case 'h': { #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) const char * const pszArchOps = "[-m|--msrs-only] [-d|--msrs-dev] [-n|--no-msrs] "; #else const char * const pszArchOps = ""; #endif RTPrintf("Usage: VBoxCpuReport %s[-c|--cpu-name ] [-h|--help] [-V|--version] [-o filename.h] [-l debug.log]\n", pszArchOps); RTPrintf("Internal tool for gathering information to the VMM CPU database.\n"); return RTEXITCODE_SUCCESS; } case 'V': RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); return RTEXITCODE_SUCCESS; default: return RTGetOptPrintError(iOpt, &ValueUnion); } } /* * Open the alternative debug log stream. */ if (pszDebugOut) { if (RTFileExists(pszDebugOut) && !RTSymlinkExists(pszDebugOut)) { char szOld[RTPATH_MAX]; rc = RTStrCopy(szOld, sizeof(szOld), pszDebugOut); if (RT_SUCCESS(rc)) rc = RTStrCat(szOld, sizeof(szOld), ".old"); if (RT_SUCCESS(rc)) RTFileRename(pszDebugOut, szOld, RTFILEMOVE_FLAGS_REPLACE); } rc = RTStrmOpen(pszDebugOut, "w", &g_pDebugOut); if (RT_FAILURE(rc)) { RTMsgError("Error opening '%s': %Rrc", pszDebugOut, rc); g_pDebugOut = NULL; } } /* * Do the requested job. */ rc = VERR_INTERNAL_ERROR; switch (enmOp) { case kCpuReportOp_Normal: /* switch output file. */ if (pszOutput) { if (RTFileExists(pszOutput) && !RTSymlinkExists(pszOutput)) { char szOld[RTPATH_MAX]; rc = RTStrCopy(szOld, sizeof(szOld), pszOutput); if (RT_SUCCESS(rc)) rc = RTStrCat(szOld, sizeof(szOld), ".old"); if (RT_SUCCESS(rc)) RTFileRename(pszOutput, szOld, RTFILEMOVE_FLAGS_REPLACE); } rc = RTStrmOpen(pszOutput, "w", &g_pReportOut); if (RT_FAILURE(rc)) { RTMsgError("Error opening '%s': %Rrc", pszOutput, rc); break; } } rc = produceCpuReport(); break; #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) case kCpuReportOp_MsrsOnly: case kCpuReportOp_MsrsHacking: rc = probeMsrs(enmOp == kCpuReportOp_MsrsHacking, NULL, NULL, NULL, 0); break; #else case kCpuReportOp_Dummy: break; #endif } /* * Close the output files. */ if (g_pReportOut) { RTStrmClose(g_pReportOut); g_pReportOut = NULL; } if (g_pDebugOut) { RTStrmClose(g_pDebugOut); g_pDebugOut = NULL; } return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }