VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testboxscript/TestBoxHelper.cpp

Last change on this file was 106650, checked in by vboxsync, 6 weeks ago

ValKit: win.arm64 build fixes. jiraref:VBP-1253

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1/* $Id: TestBoxHelper.cpp 106650 2024-10-24 09:32:31Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Testbox C Helper Utility.
4 */
5
6/*
7 * Copyright (C) 2012-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/buildconfig.h>
42#include <iprt/env.h>
43#include <iprt/err.h>
44#include <iprt/file.h>
45#include <iprt/path.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/mem.h>
49#include <iprt/message.h>
50#include <iprt/mp.h>
51#include <iprt/string.h>
52#include <iprt/stream.h>
53#include <iprt/system.h>
54
55#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
56# include <iprt/x86.h>
57# include <iprt/asm-amd64-x86.h>
58#elif defined(RT_ARCH_ARM) || defined(RT_ARCH_ARM64)
59# include <iprt/asm-arm.h>
60#endif
61
62#ifdef RT_OS_DARWIN
63# include <sys/types.h>
64# include <sys/sysctl.h>
65#elif defined(RT_OS_WINDOWS)
66# include <iprt/nt/nt-and-windows.h>
67# include <iprt/ldr.h>
68
69extern "C" HRESULT WINAPI
70WHvGetCapability(UINT32 CapabilityCode, VOID *CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32 *WrittenSizeInBytes);
71#elif defined(RT_OS_LINUX)
72# include <sys/stat.h>
73# include <fcntl.h>
74# include <unistd.h>
75#endif
76
77
78
79/**
80 * Does one free space wipe, using the given filename.
81 *
82 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE on failure (fully
83 * bitched).
84 * @param pszFilename The filename to use for wiping free space. Will be
85 * replaced and afterwards deleted.
86 * @param pvFiller The filler block buffer.
87 * @param cbFiller The size of the filler block buffer.
88 * @param cbMinLeftOpt When to stop wiping.
89 */
90static RTEXITCODE doOneFreeSpaceWipe(const char *pszFilename, void const *pvFiller, size_t cbFiller, uint64_t cbMinLeftOpt)
91{
92 /*
93 * Open the file.
94 */
95 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
96 RTFILE hFile = NIL_RTFILE;
97 int rc = RTFileOpen(&hFile, pszFilename,
98 RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE_REPLACE | (0775 << RTFILE_O_CREATE_MODE_SHIFT));
99 if (RT_SUCCESS(rc))
100 {
101 /*
102 * Query the amount of available free space. Figure out which API we should use.
103 */
104 RTFOFF cbTotal = 0;
105 RTFOFF cbFree = 0;
106 rc = RTFileQueryFsSizes(hFile, &cbTotal, &cbFree, NULL, NULL);
107 bool const fFileHandleApiSupported = rc != VERR_NOT_SUPPORTED && rc != VERR_NOT_IMPLEMENTED;
108 if (!fFileHandleApiSupported)
109 rc = RTFsQuerySizes(pszFilename, &cbTotal, &cbFree, NULL, NULL);
110 if (RT_SUCCESS(rc))
111 {
112 RTPrintf("%s: %'9RTfoff MiB out of %'9RTfoff are free\n", pszFilename, cbFree / _1M, cbTotal / _1M);
113
114 /*
115 * Start filling up the free space, down to the last 32MB.
116 */
117 uint64_t const nsStart = RTTimeNanoTS(); /* for speed calcs */
118 uint64_t nsStat = nsStart; /* for speed calcs */
119 uint64_t cbStatWritten = 0; /* for speed calcs */
120 RTFOFF const cbMinLeft = RT_MAX(cbMinLeftOpt, cbFiller * 2);
121 RTFOFF cbLeftToWrite = cbFree - cbMinLeft;
122 uint64_t cbWritten = 0;
123 uint32_t iLoop = 0;
124 while (cbLeftToWrite >= (RTFOFF)cbFiller)
125 {
126 rc = RTFileWrite(hFile, pvFiller, cbFiller, NULL);
127 if (RT_FAILURE(rc))
128 {
129 if (rc == VERR_DISK_FULL)
130 RTPrintf("%s: Disk full after writing %'9RU64 MiB\n", pszFilename, cbWritten / _1M);
131 else
132 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Write error after %'RU64 bytes: %Rrc\n",
133 pszFilename, cbWritten, rc);
134 break;
135 }
136
137 /* Flush every now and then as we approach a completely full disk. */
138 if (cbLeftToWrite <= _1G && (iLoop & (cbLeftToWrite > _128M ? 15 : 3)) == 0)
139 RTFileFlush(hFile);
140
141 /*
142 * Advance and maybe recheck the amount of free space.
143 */
144 cbWritten += cbFiller;
145 cbLeftToWrite -= (ssize_t)cbFiller;
146 iLoop++;
147 if ((iLoop & (16 - 1)) == 0 || cbLeftToWrite < _256M)
148 {
149 RTFOFF cbFreeUpdated;
150 if (fFileHandleApiSupported)
151 rc = RTFileQueryFsSizes(hFile, NULL, &cbFreeUpdated, NULL, NULL);
152 else
153 rc = RTFsQuerySizes(pszFilename, NULL, &cbFreeUpdated, NULL, NULL);
154 if (RT_SUCCESS(rc))
155 {
156 cbFree = cbFreeUpdated;
157 cbLeftToWrite = cbFree - cbMinLeft;
158 }
159 else
160 {
161 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to query free space after %'RU64 bytes: %Rrc\n",
162 pszFilename, cbWritten, rc);
163 break;
164 }
165 if ((iLoop & (512 - 1)) == 0)
166 {
167 uint64_t const nsNow = RTTimeNanoTS();
168 uint64_t cNsInterval = nsNow - nsStat;
169 uint64_t cbInterval = cbWritten - cbStatWritten;
170 uint64_t cbIntervalPerSec = !cbInterval ? 0
171 : (uint64_t)((double)cbInterval / ((double)cNsInterval / (double)RT_NS_1SEC));
172
173 RTPrintf("%s: %'9RTfoff MiB out of %'9RTfoff are free after writing %'9RU64 MiB (%'5RU64 MiB/s)\n",
174 pszFilename, cbFree / _1M, cbTotal / _1M, cbWritten / _1M, cbIntervalPerSec / _1M);
175 nsStat = nsNow;
176 cbStatWritten = cbWritten;
177 }
178 }
179 }
180
181 /*
182 * Now flush the file and then reduce the size a little before closing
183 * it so the system won't entirely run out of space. The flush should
184 * ensure the data has actually hit the disk.
185 */
186 rc = RTFileFlush(hFile);
187 if (RT_FAILURE(rc))
188 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Flush failed at %'RU64 bytes: %Rrc\n", pszFilename, cbWritten, rc);
189
190 uint64_t cbReduced = cbWritten > _512M ? cbWritten - _512M : cbWritten / 2;
191 rc = RTFileSetSize(hFile, cbReduced);
192 if (RT_FAILURE(rc))
193 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to reduce file size from %'RU64 to %'RU64 bytes: %Rrc\n",
194 pszFilename, cbWritten, cbReduced, rc);
195
196 /* Issue a summary statements. */
197 uint64_t cNsElapsed = RTTimeNanoTS() - nsStart;
198 uint64_t cbPerSec = cbWritten ? (uint64_t)((double)cbWritten / ((double)cNsElapsed / (double)RT_NS_1SEC)) : 0;
199 RTPrintf("%s: Wrote %'RU64 MiB in %'RU64 s, avg %'RU64 MiB/s.\n",
200 pszFilename, cbWritten / _1M, cNsElapsed / RT_NS_1SEC, cbPerSec / _1M);
201 }
202 else
203 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Initial free space query failed: %Rrc \n", pszFilename, rc);
204
205 RTFileClose(hFile);
206
207 /*
208 * Delete the file.
209 */
210 rc = RTFileDelete(pszFilename);
211 if (RT_FAILURE(rc))
212 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Delete failed: %Rrc !!\n", pszFilename, rc);
213 }
214 else
215 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Open failed: %Rrc\n", pszFilename, rc);
216 return rcExit;
217}
218
219
220/**
221 * Wipes free space on one or more volumes by creating large files.
222 */
223static RTEXITCODE handlerWipeFreeSpace(int argc, char **argv)
224{
225 /*
226 * Parse arguments.
227 */
228 const char *apszDefFiles[2] = { "./wipefree.spc", NULL };
229 bool fAll = false;
230 uint32_t u32Filler = UINT32_C(0xf6f6f6f6);
231 uint64_t cbMinLeftOpt = _32M;
232
233 static RTGETOPTDEF const s_aOptions[] =
234 {
235 { "--all", 'a', RTGETOPT_REQ_NOTHING },
236 { "--filler", 'f', RTGETOPT_REQ_UINT32 },
237 { "--min-free", 'm', RTGETOPT_REQ_UINT64 },
238 };
239 RTGETOPTSTATE State;
240 RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
241 RTGETOPTUNION ValueUnion;
242 int chOpt;
243 while ( (chOpt = RTGetOpt(&State, &ValueUnion)) != 0
244 && chOpt != VINF_GETOPT_NOT_OPTION)
245 {
246 switch (chOpt)
247 {
248 case 'a':
249 fAll = true;
250 break;
251 case 'f':
252 u32Filler = ValueUnion.u32;
253 break;
254 case 'm':
255 cbMinLeftOpt = ValueUnion.u64;
256 break;
257 case 'h':
258 RTPrintf("usage: wipefrespace [options] [filename1 [..]]\n"
259 "\n"
260 "Options:\n"
261 " -a, --all\n"
262 " Try do the free space wiping on all seemingly relevant file systems.\n"
263 " Changes the meaning of the filenames "
264 " This is not yet implemented\n"
265 " -p, --filler <32-bit value>\n"
266 " What to fill the blocks we write with.\n"
267 " Default: 0xf6f6f6f6\n"
268 " -m, --min-free <64-bit byte count>\n"
269 " Specifies when to stop in terms of free disk space (in bytes).\n"
270 " Default: 32MB\n"
271 "\n"
272 "Zero or more names of files to do the free space wiping thru can be given.\n"
273 "When --all is NOT used, each of the files are used to do free space wiping on\n"
274 "the volume they will live on. However, when --all is in effect the files are\n"
275 "appended to the volume mountpoints and only the first that can be created will\n"
276 "be used. Files (used ones) will be removed when done.\n"
277 "\n"
278 "If no filename is given, the default is: %s\n"
279 , apszDefFiles[0]);
280 return RTEXITCODE_SUCCESS;
281
282 default:
283 return RTGetOptPrintError(chOpt, &ValueUnion);
284 }
285 }
286
287 char **papszFiles;
288 if (chOpt == 0)
289 papszFiles = (char **)apszDefFiles;
290 else
291 papszFiles = RTGetOptNonOptionArrayPtr(&State);
292
293 /*
294 * Allocate and prep a memory which we'll write over and over again.
295 */
296 uint32_t cbFiller = _2M;
297 uint32_t *pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
298 while (!pu32Filler)
299 {
300 cbFiller <<= 1;
301 if (cbFiller >= _4K)
302 pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
303 else
304 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTMemPageAlloc failed for sizes between 4KB and 2MB!\n");
305 }
306 for (uint32_t i = 0; i < cbFiller / sizeof(pu32Filler[0]); i++)
307 pu32Filler[i] = u32Filler;
308
309 /*
310 * Do the requested work.
311 */
312 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
313 if (!fAll)
314 {
315 for (uint32_t iFile = 0; papszFiles[iFile] != NULL; iFile++)
316 {
317 RTEXITCODE rcExit2 = doOneFreeSpaceWipe(papszFiles[iFile], pu32Filler, cbFiller, cbMinLeftOpt);
318 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
319 rcExit = rcExit2;
320 }
321 }
322 else
323 {
324 /*
325 * Reject --all for now.
326 */
327 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "The --all option is not yet implemented!\n");
328 }
329
330 RTMemPageFree(pu32Filler, cbFiller);
331 return rcExit;
332}
333
334
335/**
336 * Generates a kind of report of the hardware, software and whatever else we
337 * think might be useful to know about the testbox.
338 */
339static RTEXITCODE handlerReport(int argc, char **argv)
340{
341 NOREF(argc); NOREF(argv);
342
343#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
344 /*
345 * For now, a simple CPUID dump. Need to figure out how to share code
346 * like this with other bits, putting it in IPRT.
347 */
348 RTPrintf("CPUID Dump\n"
349 "Leaf eax ebx ecx edx\n"
350 "---------------------------------------------\n");
351 static uint32_t const s_auRanges[] =
352 {
353 UINT32_C(0x00000000),
354 UINT32_C(0x80000000),
355 UINT32_C(0x80860000),
356 UINT32_C(0xc0000000),
357 UINT32_C(0x40000000),
358 };
359 for (uint32_t iRange = 0; iRange < RT_ELEMENTS(s_auRanges); iRange++)
360 {
361 uint32_t const uFirst = s_auRanges[iRange];
362
363 uint32_t uEax, uEbx, uEcx, uEdx;
364 ASMCpuIdExSlow(uFirst, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
365 if (uEax >= uFirst && uEax < uFirst + 100)
366 {
367 uint32_t const cLeafs = RT_MIN(uEax - uFirst + 1, 32);
368 for (uint32_t iLeaf = 0; iLeaf < cLeafs; iLeaf++)
369 {
370 uint32_t uLeaf = uFirst + iLeaf;
371 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
372
373 /* Clear APIC IDs to avoid submitting new reports all the time. */
374 if (uLeaf == 1)
375 uEbx &= UINT32_C(0x00ffffff);
376 if (uLeaf == 0xb)
377 uEdx = 0;
378 if (uLeaf == 0x8000001e)
379 uEax = 0;
380
381 /* Clear some other node/cpu/core/thread ids. */
382 if (uLeaf == 0x8000001e)
383 {
384 uEbx &= UINT32_C(0xffffff00);
385 uEcx &= UINT32_C(0xffffff00);
386 }
387
388 RTPrintf("%08x: %08x %08x %08x %08x\n", uLeaf, uEax, uEbx, uEcx, uEdx);
389 }
390 }
391 }
392 RTPrintf("\n");
393#else
394#endif
395
396 /*
397 * DMI info.
398 */
399 RTPrintf("DMI Info\n"
400 "--------\n");
401 static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] =
402 {
403 { "Product Name", RTSYSDMISTR_PRODUCT_NAME },
404 { "Product version", RTSYSDMISTR_PRODUCT_VERSION },
405 { "Product UUID", RTSYSDMISTR_PRODUCT_UUID },
406 { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL },
407 { "System Manufacturer", RTSYSDMISTR_MANUFACTURER },
408 };
409 for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++)
410 {
411 char szTmp[4096];
412 RT_ZERO(szTmp);
413 int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1);
414 if (RT_SUCCESS(rc))
415 RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp));
416 else
417 RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc);
418 }
419 RTPrintf("\n");
420
421 /*
422 * Dump the environment.
423 */
424 RTPrintf("Environment\n"
425 "-----------\n");
426 RTENV hEnv;
427 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
428 if (RT_SUCCESS(rc))
429 {
430 uint32_t cVars = RTEnvCountEx(hEnv);
431 for (uint32_t iVar = 0; iVar < cVars; iVar++)
432 {
433 char szVar[1024];
434 char szValue[16384];
435 rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
436
437 /* zap the value of variables that are subject to change. */
438 if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
439 && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV")
440 || !strcmp(szVar, "TESTBOX_ID")
441 || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE")
442 || !strcmp(szVar, "TESTBOX_TIMEOUT")
443 || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS")
444 || !strcmp(szVar, "TESTBOX_TEST_SET_ID")
445 )
446 )
447 strcpy(szValue, "<volatile>");
448
449 if (RT_SUCCESS(rc))
450 RTPrintf("%25s=%s\n", szVar, szValue);
451 else if (rc == VERR_BUFFER_OVERFLOW)
452 RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
453 else
454 RTPrintf("rc=%Rrc\n", rc);
455 }
456 RTEnvDestroy(hEnv);
457 }
458
459 /** @todo enumerate volumes and whatnot. */
460
461 int cch = RTPrintf("\n");
462 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
463}
464
465
466/** Print the total memory size in bytes. */
467static RTEXITCODE handlerMemSize(int argc, char **argv)
468{
469 NOREF(argc); NOREF(argv);
470
471 uint64_t cb;
472 int rc = RTSystemQueryTotalRam(&cb);
473 if (RT_SUCCESS(rc))
474 {
475 int cch = RTPrintf("%llu\n", cb);
476 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
477 }
478 RTPrintf("%Rrc\n", rc);
479 return RTEXITCODE_FAILURE;
480}
481
482
483static bool isNativeApiSupported(void)
484{
485 bool fSupported = false;
486
487#if defined(RT_OS_DARWIN)
488 /*
489 * The kern.hv_support parameter indicates support for the hypervisor API
490 * in the kernel.
491 */
492 int32_t fHvSupport = 0;
493 size_t cbOld = sizeof(fHvSupport);
494 if (sysctlbyname("kern.hv_support", &fHvSupport, &cbOld, NULL, 0) == 0)
495 {
496 if (fHvSupport != 0)
497 fSupported = true;
498 }
499
500#elif defined(RT_OS_WINDOWS)
501 /*
502 * Check whether we can load WinHvPlatform.dll and whether the Hypervisor
503 * capability is present.
504 */
505 RTLDRMOD hLdrMod;
506 int rc = RTLdrLoadSystem("WinHvPlatform.dll", false /*fNoUnload*/, &hLdrMod);
507 if (RT_SUCCESS(rc))
508 {
509 decltype(WHvGetCapability) *pfnWHvGetCapability;
510
511 rc = RTLdrGetSymbol(hLdrMod, "WHvGetCapability", (void **)&pfnWHvGetCapability);
512 if (RT_SUCCESS(rc))
513 {
514 BOOL fHypervisorPresent = FALSE;
515 SetLastError(0);
516 HRESULT hrc = pfnWHvGetCapability(0 /*WHvCapabilityCodeHypervisorPresent*/,
517 &fHypervisorPresent,
518 sizeof(fHypervisorPresent),
519 NULL);
520 if ( SUCCEEDED(hrc)
521 && fHypervisorPresent)
522 fSupported = true;
523 }
524
525 RTLdrClose(hLdrMod);
526 }
527
528#elif defined(RT_OS_LINUX)
529 /* Check by opening /dev/kvm. */
530 int fdKvm = open("/dev/kvm", O_RDWR);
531 if (fdKvm >= 0)
532 {
533 close(fdKvm);
534 fSupported = true;
535 }
536#endif
537
538 return fSupported;
539}
540
541/** Print the 'true' if native API virtualization is supported, 'false' if not and
542 * 'dunno' if we cannot tell. */
543static RTEXITCODE handlerNativeApi(int argc, char **argv)
544{
545 NOREF(argc); NOREF(argv);
546
547 int cch = RTPrintf(isNativeApiSupported() ? "true\n" : "false\n");
548 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
549}
550
551
552typedef enum { HWVIRTTYPE_NONE, HWVIRTTYPE_VTX, HWVIRTTYPE_AMDV, HVIRTTYPE_ARMV8 } HWVIRTTYPE;
553static HWVIRTTYPE getHwVirtSupport(void)
554{
555 /* No native virtualization supported on macOS anymore (for the VBox versions we care about). */
556#if !defined RT_OS_DARWIN
557# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
558 uint32_t uEax, uEbx, uEcx, uEdx;
559
560 /* VT-x */
561 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
562 if (RTX86IsValidStdRange(uEax))
563 {
564 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
565 if (uEcx & X86_CPUID_FEATURE_ECX_VMX)
566 return HWVIRTTYPE_VTX;
567 }
568
569 /* AMD-V */
570 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
571 if (RTX86IsValidExtRange(uEax))
572 {
573 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
574 if (uEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
575 return HWVIRTTYPE_AMDV;
576 }
577# endif
578#endif
579
580 return HWVIRTTYPE_NONE;
581}
582
583/** Print the 'true' if VT-x or AMD-v is supported, 'false' it not. */
584static RTEXITCODE handlerCpuHwVirt(int argc, char **argv)
585{
586 NOREF(argc); NOREF(argv);
587 int cch = RTPrintf(getHwVirtSupport() != HWVIRTTYPE_NONE ? "true\n" : "false\n");
588 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
589}
590
591
592/** Print the 'true' if nested paging is supported, 'false' if not and
593 * 'dunno' if we cannot tell. */
594static RTEXITCODE handlerCpuNestedPaging(int argc, char **argv)
595{
596 NOREF(argc); NOREF(argv);
597 int fSupported = -1;
598
599 HWVIRTTYPE const enmHwVirt = getHwVirtSupport();
600 if (enmHwVirt == HWVIRTTYPE_NONE)
601 fSupported = 0;
602#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
603 else if (enmHwVirt == HWVIRTTYPE_AMDV)
604 {
605 uint32_t uEax, uEbx, uEcx, uEdx;
606 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
607 if (RTX86IsValidExtRange(uEax) && uEax >= 0x8000000a)
608 {
609 ASMCpuId(0x8000000a, &uEax, &uEbx, &uEcx, &uEdx);
610 if (uEdx & RT_BIT(0) /* AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING */)
611 fSupported = 1;
612 else
613 fSupported = 0;
614 }
615 }
616# if defined(RT_OS_LINUX)
617 else if (enmHwVirt == HWVIRTTYPE_VTX)
618 {
619 /*
620 * For Intel there is no generic way to query EPT support but on
621 * Linux we can resort to checking for the EPT flag in /proc/cpuinfo
622 */
623 RTFILE hFileCpu;
624 int rc = RTFileOpen(&hFileCpu, "/proc/cpuinfo", RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
625 if (RT_SUCCESS(rc))
626 {
627 /*
628 * Read enough to fit the first CPU entry in, we only check the first
629 * CPU as all the others should have the same features.
630 */
631 char szBuf[_4K];
632 size_t cbRead = 0;
633
634 RT_ZERO(szBuf); /* Ensure proper termination. */
635 rc = RTFileRead(hFileCpu, &szBuf[0], sizeof(szBuf) - 1, &cbRead);
636 if (RT_SUCCESS(rc))
637 {
638 /* Look for the start of the flags section. */
639 char *pszStrFlags = RTStrStr(&szBuf[0], "flags");
640 if (pszStrFlags)
641 {
642 /* Look for the end as indicated by new line. */
643 char *pszEnd = pszStrFlags;
644 while ( *pszEnd != '\0'
645 && *pszEnd != '\n')
646 pszEnd++;
647 *pszEnd = '\0'; /* Cut off everything after the flags section. */
648
649 /*
650 * Search for the ept flag indicating support and the absence meaning
651 * not supported.
652 */
653 if (RTStrStr(pszStrFlags, "ept"))
654 fSupported = 1;
655 else
656 fSupported = 0;
657 }
658 }
659 RTFileClose(hFileCpu);
660 }
661 }
662# endif
663#endif
664
665 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
666 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
667}
668
669
670/** Print the 'true' if 64-bits guests are supported, 'false' if not and
671 * 'dunno' if we cannot tell. */
672static RTEXITCODE handlerCpu64BitGuest(int argc, char **argv)
673{
674 NOREF(argc); NOREF(argv);
675 int fSupported = 0;
676
677 HWVIRTTYPE const enmHwVirt = getHwVirtSupport();
678 if ( enmHwVirt != HWVIRTTYPE_NONE
679 || isNativeApiSupported())
680 {
681#if ARCH_BITS == 64
682 fSupported = 1; /* We're running in 64-bit mode, so it must be supported. */
683
684#elif defined(RT_ARCH_X86)
685# ifdef RT_OS_DARWIN
686 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
687 int f64bitCapable = 0;
688 size_t cbParameter = sizeof(f64bitCapable);
689 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, 0);
690 if (rc != -1)
691 fSupported = f64bitCapable != 0;
692 else
693# endif
694 {
695 /* PAE and HwVirt are required */
696 uint32_t uEax, uEbx, uEcx, uEdx;
697 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
698 if (RTX86IsValidStdRange(uEax))
699 {
700 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
701 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
702 {
703 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
704 won't necessarily do so. */
705 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
706 if (RTX86IsValidExtRange(uEax))
707 {
708 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
709 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
710 fSupported = 1;
711 else if (enmHwVirt != HWVIRTTYPE_AMDV)
712 fSupported = -1;
713 }
714 }
715 }
716 }
717#endif
718 }
719
720 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
721 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
722}
723
724
725/** Print the CPU 'revision', if available. */
726static RTEXITCODE handlerCpuRevision(int argc, char **argv)
727{
728 NOREF(argc); NOREF(argv);
729
730#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
731 uint32_t uEax, uEbx, uEcx, uEdx;
732 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
733 if (RTX86IsValidStdRange(uEax) && uEax >= 1)
734 {
735 uint32_t uEax1 = ASMCpuId_EAX(1);
736 uint32_t uVersion = (RTX86GetCpuFamily(uEax1) << 24)
737 | (RTX86GetCpuModel(uEax1, RTX86IsIntelCpu(uEbx, uEcx, uEdx)) << 8)
738 | RTX86GetCpuStepping(uEax1);
739 int cch = RTPrintf("%#x\n", uVersion);
740 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
741 }
742#elif defined(RT_ARCH_ARM) || defined(RT_ARCH_ARM64)
743 /** @todo There is no way to access MIDR_EL1 from userspace except for parsing the various
744 * OS dependent ways (/proc/cpuinfo, sysctl, ...). Just fake it for now to get it running. */
745 int cch = RTPrintf("%#x\n", 1);
746 if (cch > 0)
747 return RTEXITCODE_SUCCESS;
748#endif
749 return RTEXITCODE_FAILURE;
750}
751
752
753/** Print the CPU name, if available. */
754static RTEXITCODE handlerCpuName(int argc, char **argv)
755{
756 NOREF(argc); NOREF(argv);
757
758 char szTmp[1024];
759 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
760 if (RT_SUCCESS(rc))
761 {
762 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
763 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
764 }
765 return RTEXITCODE_FAILURE;
766}
767
768
769/** Print the CPU vendor name, 'GenuineIntel' and such. */
770static RTEXITCODE handlerCpuVendor(int argc, char **argv)
771{
772 NOREF(argc); NOREF(argv);
773
774#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
775 uint32_t uEax, uEbx, uEcx, uEdx;
776 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
777 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
778#elif defined(RT_ARCH_ARM64) && defined(RT_OS_DARWIN)
779 /*
780 * There is machdep.cpu.brand_string we could query but that identifies
781 * the whole CPU and not just the vendor.
782 *
783 * Running on macOS using the arm64 architecture is a pretty safe bet that
784 * we are also running on Apple Silicon (there is the possibility that
785 * this runs in a macOS VM on some other hardware but this is highly unlikely).
786 */
787 int cch = RTPrintf("Apple\n");
788#else
789 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
790#endif
791 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
792}
793
794
795
796int main(int argc, char **argv)
797{
798 int rc = RTR3InitExe(argc, &argv, 0);
799 if (RT_FAILURE(rc))
800 return RTMsgInitFailure(rc);
801
802 /*
803 * The first argument is a command. Figure out which and call its handler.
804 */
805 static const struct
806 {
807 const char *pszCommand;
808 RTEXITCODE (*pfnHandler)(int argc, char **argv);
809 bool fNoArgs;
810 } s_aHandlers[] =
811 {
812 { "cpuvendor", handlerCpuVendor, true },
813 { "cpuname", handlerCpuName, true },
814 { "cpurevision", handlerCpuRevision, true },
815 { "cpuhwvirt", handlerCpuHwVirt, true },
816 { "nestedpaging", handlerCpuNestedPaging, true },
817 { "64bitguest", handlerCpu64BitGuest, true },
818 { "longmode", handlerCpu64BitGuest, true }, /* legacy name */
819 { "nativeapi", handlerNativeApi, true },
820 { "memsize", handlerMemSize, true },
821 { "report", handlerReport, true },
822 { "wipefreespace", handlerWipeFreeSpace, false }
823 };
824
825 if (argc < 2)
826 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
827
828 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
829 {
830 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
831 {
832 if ( s_aHandlers[i].fNoArgs
833 && argc != 2)
834 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
835 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
836 }
837 }
838
839 /*
840 * Help or version query?
841 */
842 for (int i = 1; i < argc; i++)
843 if ( !strcmp(argv[i], "--help")
844 || !strcmp(argv[i], "-h")
845 || !strcmp(argv[i], "-?")
846 || !strcmp(argv[i], "help") )
847 {
848 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
849 "\n"
850 "commands:\n", argv[0]);
851 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
852 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
853 return RTEXITCODE_FAILURE;
854 }
855 else if ( !strcmp(argv[i], "--version")
856 || !strcmp(argv[i], "-V") )
857 {
858 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
859 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
860 }
861
862 /*
863 * Syntax error.
864 */
865 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
866}
867
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette