VirtualBox

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

Last change on this file since 86715 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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