VirtualBox

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

Last change on this file since 64668 was 64616, checked in by vboxsync, 8 years ago

wipefreespace: quick update of the status output.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.3 KB
Line 
1/* $Id: TestBoxHelper.cpp 64616 2016-11-09 16:28:31Z 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#endif
519
520 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
521 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
522}
523
524
525/** Print the 'true' if long mode guests are supported, 'false' if not and
526 * 'dunno' if we cannot tell. */
527static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
528{
529 NOREF(argc); NOREF(argv);
530 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
531 int fSupported = 0;
532
533 if (enmHwVirt != HWVIRTTYPE_NONE)
534 {
535#if defined(RT_ARCH_AMD64)
536 fSupported = 1; /* We're running long mode, so it must be supported. */
537
538#elif defined(RT_ARCH_X86)
539# ifdef RT_OS_DARWIN
540 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
541 int f64bitCapable = 0;
542 size_t cbParameter = sizeof(f64bitCapable);
543 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL);
544 if (rc != -1)
545 fSupported = f64bitCapable != 0;
546 else
547# endif
548 {
549 /* PAE and HwVirt are required */
550 uint32_t uEax, uEbx, uEcx, uEdx;
551 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
552 if (ASMIsValidStdRange(uEax))
553 {
554 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
555 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
556 {
557 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
558 won't necessarily do so. */
559 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
560 if (ASMIsValidExtRange(uEax))
561 {
562 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
563 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
564 fSupported = 1;
565 else if (enmHwVirt != HWVIRTTYPE_AMDV)
566 fSupported = -1;
567 }
568 }
569 }
570 }
571#endif
572 }
573
574 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
575 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
576}
577
578
579/** Print the CPU 'revision', if available. */
580static RTEXITCODE handlerCpuRevision(int argc, char **argv)
581{
582 NOREF(argc); NOREF(argv);
583
584#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
585 uint32_t uEax, uEbx, uEcx, uEdx;
586 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
587 if (ASMIsValidStdRange(uEax) && uEax >= 1)
588 {
589 uint32_t uEax1 = ASMCpuId_EAX(1);
590 uint32_t uVersion = (ASMGetCpuFamily(uEax1) << 24)
591 | (ASMGetCpuModel(uEax1, ASMIsIntelCpuEx(uEbx, uEcx, uEdx)) << 8)
592 | ASMGetCpuStepping(uEax1);
593 int cch = RTPrintf("%#x\n", uVersion);
594 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
595 }
596#endif
597 return RTEXITCODE_FAILURE;
598}
599
600
601/** Print the CPU name, if available. */
602static RTEXITCODE handlerCpuName(int argc, char **argv)
603{
604 NOREF(argc); NOREF(argv);
605
606 char szTmp[1024];
607 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
608 if (RT_SUCCESS(rc))
609 {
610 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
611 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
612 }
613 return RTEXITCODE_FAILURE;
614}
615
616
617/** Print the CPU vendor name, 'GenuineIntel' and such. */
618static RTEXITCODE handlerCpuVendor(int argc, char **argv)
619{
620 NOREF(argc); NOREF(argv);
621
622#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
623 uint32_t uEax, uEbx, uEcx, uEdx;
624 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
625 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
626#else
627 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
628#endif
629 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
630}
631
632
633
634int main(int argc, char **argv)
635{
636 int rc = RTR3InitExe(argc, &argv, 0);
637 if (RT_FAILURE(rc))
638 return RTMsgInitFailure(rc);
639
640 /*
641 * The first argument is a command. Figure out which and call its handler.
642 */
643 static const struct
644 {
645 const char *pszCommand;
646 RTEXITCODE (*pfnHandler)(int argc, char **argv);
647 bool fNoArgs;
648 } s_aHandlers[] =
649 {
650 { "cpuvendor", handlerCpuVendor, true },
651 { "cpuname", handlerCpuName, true },
652 { "cpurevision", handlerCpuRevision, true },
653 { "cpuhwvirt", handlerCpuHwVirt, true },
654 { "nestedpaging", handlerCpuNestedPaging, true },
655 { "longmode", handlerCpuLongMode, true },
656 { "memsize", handlerMemSize, true },
657 { "report", handlerReport, true },
658 { "wipefreespace", handlerWipeFreeSpace, false }
659 };
660
661 if (argc < 2)
662 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
663
664 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
665 {
666 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
667 {
668 if ( s_aHandlers[i].fNoArgs
669 && argc != 2)
670 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
671 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
672 }
673 }
674
675 /*
676 * Help or version query?
677 */
678 for (int i = 1; i < argc; i++)
679 if ( !strcmp(argv[i], "--help")
680 || !strcmp(argv[i], "-h")
681 || !strcmp(argv[i], "-?")
682 || !strcmp(argv[i], "help") )
683 {
684 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
685 "\n"
686 "commands:\n", argv[0]);
687 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
688 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
689 return RTEXITCODE_FAILURE;
690 }
691 else if ( !strcmp(argv[i], "--version")
692 || !strcmp(argv[i], "-V") )
693 {
694 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
695 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
696 }
697
698 /*
699 * Syntax error.
700 */
701 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
702}
703
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