VirtualBox

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

Last change on this file since 75495 was 69794, checked in by vboxsync, 7 years ago

testboxscript/TestBoxHelper.cpp: Use kern.hv_support to detect nested paging on darwin.

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