VirtualBox

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

Last change on this file since 95351 was 94490, checked in by vboxsync, 3 years ago

Validation Kit/Testboxscript: Fixed a warning for x86 Darwin builds.

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