VirtualBox

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

Last change on this file since 66784 was 64781, checked in by vboxsync, 8 years ago

ValidationKit/TestBoxHelper: On Linux we can check whether EPT is supported by looking for ept in /proc/cpuinfo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: TestBoxHelper.cpp 64781 2016-12-04 13:00:49Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Testbox C Helper Utility.
4 */
5
6/*
7 * Copyright (C) 2012-2016 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# endif
565#endif
566
567 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
568 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
569}
570
571
572/** Print the 'true' if long mode guests are supported, 'false' if not and
573 * 'dunno' if we cannot tell. */
574static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
575{
576 NOREF(argc); NOREF(argv);
577 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
578 int fSupported = 0;
579
580 if (enmHwVirt != HWVIRTTYPE_NONE)
581 {
582#if defined(RT_ARCH_AMD64)
583 fSupported = 1; /* We're running long mode, so it must be supported. */
584
585#elif defined(RT_ARCH_X86)
586# ifdef RT_OS_DARWIN
587 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
588 int f64bitCapable = 0;
589 size_t cbParameter = sizeof(f64bitCapable);
590 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL);
591 if (rc != -1)
592 fSupported = f64bitCapable != 0;
593 else
594# endif
595 {
596 /* PAE and HwVirt are required */
597 uint32_t uEax, uEbx, uEcx, uEdx;
598 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
599 if (ASMIsValidStdRange(uEax))
600 {
601 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
602 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
603 {
604 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
605 won't necessarily do so. */
606 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
607 if (ASMIsValidExtRange(uEax))
608 {
609 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
610 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
611 fSupported = 1;
612 else if (enmHwVirt != HWVIRTTYPE_AMDV)
613 fSupported = -1;
614 }
615 }
616 }
617 }
618#endif
619 }
620
621 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
622 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
623}
624
625
626/** Print the CPU 'revision', if available. */
627static RTEXITCODE handlerCpuRevision(int argc, char **argv)
628{
629 NOREF(argc); NOREF(argv);
630
631#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
632 uint32_t uEax, uEbx, uEcx, uEdx;
633 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
634 if (ASMIsValidStdRange(uEax) && uEax >= 1)
635 {
636 uint32_t uEax1 = ASMCpuId_EAX(1);
637 uint32_t uVersion = (ASMGetCpuFamily(uEax1) << 24)
638 | (ASMGetCpuModel(uEax1, ASMIsIntelCpuEx(uEbx, uEcx, uEdx)) << 8)
639 | ASMGetCpuStepping(uEax1);
640 int cch = RTPrintf("%#x\n", uVersion);
641 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
642 }
643#endif
644 return RTEXITCODE_FAILURE;
645}
646
647
648/** Print the CPU name, if available. */
649static RTEXITCODE handlerCpuName(int argc, char **argv)
650{
651 NOREF(argc); NOREF(argv);
652
653 char szTmp[1024];
654 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
655 if (RT_SUCCESS(rc))
656 {
657 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
658 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
659 }
660 return RTEXITCODE_FAILURE;
661}
662
663
664/** Print the CPU vendor name, 'GenuineIntel' and such. */
665static RTEXITCODE handlerCpuVendor(int argc, char **argv)
666{
667 NOREF(argc); NOREF(argv);
668
669#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
670 uint32_t uEax, uEbx, uEcx, uEdx;
671 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
672 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
673#else
674 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
675#endif
676 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
677}
678
679
680
681int main(int argc, char **argv)
682{
683 int rc = RTR3InitExe(argc, &argv, 0);
684 if (RT_FAILURE(rc))
685 return RTMsgInitFailure(rc);
686
687 /*
688 * The first argument is a command. Figure out which and call its handler.
689 */
690 static const struct
691 {
692 const char *pszCommand;
693 RTEXITCODE (*pfnHandler)(int argc, char **argv);
694 bool fNoArgs;
695 } s_aHandlers[] =
696 {
697 { "cpuvendor", handlerCpuVendor, true },
698 { "cpuname", handlerCpuName, true },
699 { "cpurevision", handlerCpuRevision, true },
700 { "cpuhwvirt", handlerCpuHwVirt, true },
701 { "nestedpaging", handlerCpuNestedPaging, true },
702 { "longmode", handlerCpuLongMode, true },
703 { "memsize", handlerMemSize, true },
704 { "report", handlerReport, true },
705 { "wipefreespace", handlerWipeFreeSpace, false }
706 };
707
708 if (argc < 2)
709 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
710
711 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
712 {
713 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
714 {
715 if ( s_aHandlers[i].fNoArgs
716 && argc != 2)
717 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
718 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
719 }
720 }
721
722 /*
723 * Help or version query?
724 */
725 for (int i = 1; i < argc; i++)
726 if ( !strcmp(argv[i], "--help")
727 || !strcmp(argv[i], "-h")
728 || !strcmp(argv[i], "-?")
729 || !strcmp(argv[i], "help") )
730 {
731 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
732 "\n"
733 "commands:\n", argv[0]);
734 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
735 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
736 return RTEXITCODE_FAILURE;
737 }
738 else if ( !strcmp(argv[i], "--version")
739 || !strcmp(argv[i], "-V") )
740 {
741 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
742 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
743 }
744
745 /*
746 * Syntax error.
747 */
748 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
749}
750
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