VirtualBox

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

Last change on this file since 64608 was 64608, checked in by vboxsync, 9 years ago

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.1 KB
Line 
1/* $Id: TestBoxHelper.cpp 64608 2016-11-08 18:33:56Z 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.215389.xyz. 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: %'RTfoff bytes out of %'RTfoff are free\n", pszFilename, cbFree, cbTotal);
90
91 /*
92 * Start filling up the free space, down to the last 32MB.
93 */
94 RTFOFF const cbMinLeft = RT_MAX(cbMinLeftOpt, cbFiller * 2);
95 RTFOFF cbLeftToWrite = cbFree - cbMinLeft;
96 uint64_t cbWritten = 0;
97 uint32_t iLoop = 0;
98 while (cbLeftToWrite >= (RTFOFF)cbFiller)
99 {
100 rc = RTFileWrite(hFile, pvFiller, cbFiller, NULL);
101 if (RT_FAILURE(rc))
102 {
103 if (rc == VERR_DISK_FULL)
104 RTPrintf("%s: Disk full after writing %'RU64 bytes\n", pszFilename, cbWritten);
105 else
106 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Write error after %'RU64 bytes: %Rrc\n",
107 pszFilename, cbWritten, rc);
108 break;
109 }
110
111 /* Flush every now and then as we approach a completely full disk. */
112 if (cbLeftToWrite <= _1G && (iLoop & (cbLeftToWrite > _128M ? 15 : 3)) == 0)
113 RTFileFlush(hFile);
114
115 /*
116 * Advance and maybe recheck the amount of free space.
117 */
118 cbWritten += cbFiller;
119 cbLeftToWrite -= (ssize_t)cbFiller;
120 iLoop++;
121 if ((iLoop & (16 - 1)) == 0 || cbLeftToWrite < _256M)
122 {
123 RTFOFF cbFreeUpdated;
124 if (fFileHandleApiSupported)
125 rc = RTFsQuerySizes(pszFilename, NULL, &cbFreeUpdated, NULL, NULL);
126 else
127 rc = RTFileQueryFsSizes(hFile, NULL, &cbFreeUpdated, NULL, NULL);
128 if (RT_SUCCESS(rc))
129 {
130 cbFree = cbFreeUpdated;
131 cbLeftToWrite = cbFree - cbMinLeft;
132 }
133 else
134 {
135 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to query free space after %'RU64 bytes: %Rrc\n",
136 pszFilename, cbWritten, rc);
137 break;
138 }
139 if ((iLoop & (512 - 1)) == 0)
140 RTPrintf("%s: %'RTfoff bytes out of %'RTfoff are free after writing %'RU64 bytes\n",
141 pszFilename, cbFree, cbTotal, cbWritten);
142 }
143 }
144
145 /*
146 * Now flush the file and then reduce the size a little before closing
147 * it so the system won't entirely run out of space. The flush should
148 * ensure the data has actually hit the disk.
149 */
150 rc = RTFileFlush(hFile);
151 if (RT_FAILURE(rc))
152 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Flush failed at %'RU64 bytes: %Rrc\n", pszFilename, cbWritten, rc);
153
154 uint64_t cbReduced = cbWritten > _512M ? cbWritten - _512M : cbWritten / 2;
155 rc = RTFileSetSize(hFile, cbReduced);
156 if (RT_FAILURE(rc))
157 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to reduce file size from %'RU64 to %'RU64 bytes: %Rrc\n",
158 pszFilename, cbWritten, cbReduced, rc);
159 }
160 else
161 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Initial free space query failed: %Rrc \n", pszFilename, rc);
162
163 RTFileClose(hFile);
164
165 /*
166 * Delete the file.
167 */
168 rc = RTFileDelete(pszFilename);
169 if (RT_FAILURE(rc))
170 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Delete failed: %Rrc !!\n", pszFilename, rc);
171 }
172 else
173 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Open failed: %Rrc\n", pszFilename, rc);
174 return rcExit;
175}
176
177
178/**
179 * Wipes free space on one or more volumes by creating large files.
180 */
181static RTEXITCODE handlerWipeFreeSpace(int argc, char **argv)
182{
183 /*
184 * Parse arguments.
185 */
186 const char *apszDefFiles[2] = { "./wipefree.spc", NULL };
187 bool fAll = false;
188 uint32_t u32Filler = UINT32_C(0xf6f6f6f6);
189 uint64_t cbMinLeftOpt = _32M;
190
191 static RTGETOPTDEF const s_aOptions[] =
192 {
193 { "--all", 'a', RTGETOPT_REQ_NOTHING },
194 { "--filler", 'f', RTGETOPT_REQ_UINT32 },
195 { "--min-free", 'm', RTGETOPT_REQ_UINT64 },
196 };
197 RTGETOPTSTATE State;
198 RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
199 RTGETOPTUNION ValueUnion;
200 int chOpt;
201 while ( (chOpt = RTGetOpt(&State, &ValueUnion)) != 0
202 && chOpt != VINF_GETOPT_NOT_OPTION)
203 {
204 switch (chOpt)
205 {
206 case 'a':
207 fAll = true;
208 break;
209 case 'f':
210 u32Filler = ValueUnion.u32;
211 break;
212 case 'm':
213 cbMinLeftOpt = ValueUnion.u64;
214 break;
215 case 'h':
216 RTPrintf("usage: wipefrespace [options] [filename1 [..]]\n"
217 "\n"
218 "Options:\n"
219 " -a, --all\n"
220 " Try do the free space wiping on all seemingly relevant file systems.\n"
221 " Changes the meaning of the filenames "
222 " This is not yet implemented\n"
223 " -p, --filler <32-bit value>\n"
224 " What to fill the blocks we write with.\n"
225 " Default: 0xf6f6f6f6\n"
226 " -m, --min-free <64-bit byte count>\n"
227 " Specifies when to stop in terms of free disk space (in bytes).\n"
228 " Default: 32MB\n"
229 "\n"
230 "Zero or more names of files to do the free space wiping thru can be given.\n"
231 "When --all is NOT used, each of the files are used to do free space wiping on\n"
232 "the volume they will live on. However, when --all is in effect the files are\n"
233 "appended to the volume mountpoints and only the first that can be created will\n"
234 "be used. Files (used ones) will be removed when done.\n"
235 "\n"
236 "If no filename is given, the default is: %s\n"
237 , apszDefFiles[0]);
238 return RTEXITCODE_SUCCESS;
239
240 default:
241 return RTGetOptPrintError(chOpt, &ValueUnion);
242 }
243 }
244
245 char **papszFiles;
246 if (chOpt == 0)
247 papszFiles = (char **)apszDefFiles;
248 else
249 papszFiles = RTGetOptNonOptionArrayPtr(&State);
250
251 /*
252 * Allocate and prep a memory which we'll write over and over again.
253 */
254 uint32_t cbFiller = _2M;
255 uint32_t *pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
256 while (!pu32Filler)
257 {
258 cbFiller <<= 1;
259 if (cbFiller >= _4K)
260 pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
261 else
262 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTMemPageAlloc failed for sizes between 4KB and 2MB!\n");
263 }
264 for (uint32_t i = 0; i < cbFiller / sizeof(pu32Filler[0]); i++)
265 pu32Filler[i] = u32Filler;
266
267 /*
268 * Do the requested work.
269 */
270 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
271 if (!fAll)
272 {
273 for (uint32_t iFile = 0; papszFiles[iFile] != NULL; iFile++)
274 {
275 RTEXITCODE rcExit2 = doOneFreeSpaceWipe(papszFiles[iFile], pu32Filler, cbFiller, cbMinLeftOpt);
276 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
277 rcExit = rcExit2;
278 }
279 }
280 else
281 {
282 /*
283 * Reject --all for now.
284 */
285 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "The --all option is not yet implemented!\n");
286 }
287
288 RTMemPageFree(pu32Filler, cbFiller);
289 return rcExit;
290}
291
292
293/**
294 * Generates a kind of report of the hardware, software and whatever else we
295 * think might be useful to know about the testbox.
296 */
297static RTEXITCODE handlerReport(int argc, char **argv)
298{
299 NOREF(argc); NOREF(argv);
300
301#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
302 /*
303 * For now, a simple CPUID dump. Need to figure out how to share code
304 * like this with other bits, putting it in IPRT.
305 */
306 RTPrintf("CPUID Dump\n"
307 "Leaf eax ebx ecx edx\n"
308 "---------------------------------------------\n");
309 static uint32_t const s_auRanges[] =
310 {
311 UINT32_C(0x00000000),
312 UINT32_C(0x80000000),
313 UINT32_C(0x80860000),
314 UINT32_C(0xc0000000),
315 UINT32_C(0x40000000),
316 };
317 for (uint32_t iRange = 0; iRange < RT_ELEMENTS(s_auRanges); iRange++)
318 {
319 uint32_t const uFirst = s_auRanges[iRange];
320
321 uint32_t uEax, uEbx, uEcx, uEdx;
322 ASMCpuIdExSlow(uFirst, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
323 if (uEax >= uFirst && uEax < uFirst + 100)
324 {
325 uint32_t const cLeafs = RT_MIN(uEax - uFirst + 1, 32);
326 for (uint32_t iLeaf = 0; iLeaf < cLeafs; iLeaf++)
327 {
328 uint32_t uLeaf = uFirst + iLeaf;
329 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
330
331 /* Clear APIC IDs to avoid submitting new reports all the time. */
332 if (uLeaf == 1)
333 uEbx &= UINT32_C(0x00ffffff);
334 if (uLeaf == 0xb)
335 uEdx = 0;
336 if (uLeaf == 0x8000001e)
337 uEax = 0;
338
339 /* Clear some other node/cpu/core/thread ids. */
340 if (uLeaf == 0x8000001e)
341 {
342 uEbx &= UINT32_C(0xffffff00);
343 uEcx &= UINT32_C(0xffffff00);
344 }
345
346 RTPrintf("%08x: %08x %08x %08x %08x\n", uLeaf, uEax, uEbx, uEcx, uEdx);
347 }
348 }
349 }
350 RTPrintf("\n");
351
352 /*
353 * DMI info.
354 */
355 RTPrintf("DMI Info\n"
356 "--------\n");
357 static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] =
358 {
359 { "Product Name", RTSYSDMISTR_PRODUCT_NAME },
360 { "Product version", RTSYSDMISTR_PRODUCT_VERSION },
361 { "Product UUID", RTSYSDMISTR_PRODUCT_UUID },
362 { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL },
363 { "System Manufacturer", RTSYSDMISTR_MANUFACTURER },
364 };
365 for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++)
366 {
367 char szTmp[4096];
368 RT_ZERO(szTmp);
369 int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1);
370 if (RT_SUCCESS(rc))
371 RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp));
372 else
373 RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc);
374 }
375 RTPrintf("\n");
376
377#else
378#endif
379
380 /*
381 * Dump the environment.
382 */
383 RTPrintf("Environment\n"
384 "-----------\n");
385 RTENV hEnv;
386 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
387 if (RT_SUCCESS(rc))
388 {
389 uint32_t cVars = RTEnvCountEx(hEnv);
390 for (uint32_t iVar = 0; iVar < cVars; iVar++)
391 {
392 char szVar[1024];
393 char szValue[16384];
394 rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
395
396 /* zap the value of variables that are subject to change. */
397 if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
398 && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV")
399 || !strcmp(szVar, "TESTBOX_ID")
400 || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE")
401 || !strcmp(szVar, "TESTBOX_TIMEOUT")
402 || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS")
403 || !strcmp(szVar, "TESTBOX_TEST_SET_ID")
404 )
405 )
406 strcpy(szValue, "<volatile>");
407
408 if (RT_SUCCESS(rc))
409 RTPrintf("%25s=%s\n", szVar, szValue);
410 else if (rc == VERR_BUFFER_OVERFLOW)
411 RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
412 else
413 RTPrintf("rc=%Rrc\n", rc);
414 }
415 RTEnvDestroy(hEnv);
416 }
417
418 /** @todo enumerate volumes and whatnot. */
419
420 int cch = RTPrintf("\n");
421 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
422}
423
424
425/** Print the total memory size in bytes. */
426static RTEXITCODE handlerMemSize(int argc, char **argv)
427{
428 NOREF(argc); NOREF(argv);
429
430 uint64_t cb;
431 int rc = RTSystemQueryTotalRam(&cb);
432 if (RT_SUCCESS(rc))
433 {
434 int cch = RTPrintf("%llu\n", cb);
435 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
436 }
437 RTPrintf("%Rrc\n", rc);
438 return RTEXITCODE_FAILURE;
439}
440
441typedef enum { HWVIRTTYPE_NONE, HWVIRTTYPE_VTX, HWVIRTTYPE_AMDV } HWVIRTTYPE;
442static HWVIRTTYPE isHwVirtSupported(void)
443{
444#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
445 uint32_t uEax, uEbx, uEcx, uEdx;
446
447 /* VT-x */
448 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
449 if (ASMIsValidStdRange(uEax))
450 {
451 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
452 if (uEcx & X86_CPUID_FEATURE_ECX_VMX)
453 return HWVIRTTYPE_VTX;
454 }
455
456 /* AMD-V */
457 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
458 if (ASMIsValidExtRange(uEax))
459 {
460 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
461 if (uEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
462 return HWVIRTTYPE_AMDV;
463 }
464#endif
465
466 return HWVIRTTYPE_NONE;
467}
468
469/** Print the 'true' if VT-x or AMD-v is supported, 'false' it not. */
470static RTEXITCODE handlerCpuHwVirt(int argc, char **argv)
471{
472 NOREF(argc); NOREF(argv);
473 int cch = RTPrintf(isHwVirtSupported() != HWVIRTTYPE_NONE ? "true\n" : "false\n");
474 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
475}
476
477
478/** Print the 'true' if nested paging is supported, 'false' if not and
479 * 'dunno' if we cannot tell. */
480static RTEXITCODE handlerCpuNestedPaging(int argc, char **argv)
481{
482 NOREF(argc); NOREF(argv);
483 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
484 int fSupported = -1;
485
486#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
487 if (enmHwVirt == HWVIRTTYPE_AMDV)
488 {
489 uint32_t uEax, uEbx, uEcx, uEdx;
490 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
491 if (ASMIsValidExtRange(uEax) && uEax >= 0x8000000a)
492 {
493 ASMCpuId(0x8000000a, &uEax, &uEbx, &uEcx, &uEdx);
494 if (uEdx & RT_BIT(0) /* AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING */)
495 fSupported = 1;
496 else
497 fSupported = 0;
498 }
499 }
500#endif
501
502 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
503 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
504}
505
506
507/** Print the 'true' if long mode guests are supported, 'false' if not and
508 * 'dunno' if we cannot tell. */
509static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
510{
511 NOREF(argc); NOREF(argv);
512 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
513 int fSupported = 0;
514
515 if (enmHwVirt != HWVIRTTYPE_NONE)
516 {
517#if defined(RT_ARCH_AMD64)
518 fSupported = 1; /* We're running long mode, so it must be supported. */
519
520#elif defined(RT_ARCH_X86)
521# ifdef RT_OS_DARWIN
522 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
523 int f64bitCapable = 0;
524 size_t cbParameter = sizeof(f64bitCapable);
525 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL);
526 if (rc != -1)
527 fSupported = f64bitCapable != 0;
528 else
529# endif
530 {
531 /* PAE and HwVirt are required */
532 uint32_t uEax, uEbx, uEcx, uEdx;
533 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
534 if (ASMIsValidStdRange(uEax))
535 {
536 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
537 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
538 {
539 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
540 won't necessarily do so. */
541 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
542 if (ASMIsValidExtRange(uEax))
543 {
544 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
545 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
546 fSupported = 1;
547 else if (enmHwVirt != HWVIRTTYPE_AMDV)
548 fSupported = -1;
549 }
550 }
551 }
552 }
553#endif
554 }
555
556 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
557 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
558}
559
560
561/** Print the CPU 'revision', if available. */
562static RTEXITCODE handlerCpuRevision(int argc, char **argv)
563{
564 NOREF(argc); NOREF(argv);
565
566#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
567 uint32_t uEax, uEbx, uEcx, uEdx;
568 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
569 if (ASMIsValidStdRange(uEax) && uEax >= 1)
570 {
571 uint32_t uEax1 = ASMCpuId_EAX(1);
572 uint32_t uVersion = (ASMGetCpuFamily(uEax1) << 24)
573 | (ASMGetCpuModel(uEax1, ASMIsIntelCpuEx(uEbx, uEcx, uEdx)) << 8)
574 | ASMGetCpuStepping(uEax1);
575 int cch = RTPrintf("%#x\n", uVersion);
576 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
577 }
578#endif
579 return RTEXITCODE_FAILURE;
580}
581
582
583/** Print the CPU name, if available. */
584static RTEXITCODE handlerCpuName(int argc, char **argv)
585{
586 NOREF(argc); NOREF(argv);
587
588 char szTmp[1024];
589 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
590 if (RT_SUCCESS(rc))
591 {
592 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
593 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
594 }
595 return RTEXITCODE_FAILURE;
596}
597
598
599/** Print the CPU vendor name, 'GenuineIntel' and such. */
600static RTEXITCODE handlerCpuVendor(int argc, char **argv)
601{
602 NOREF(argc); NOREF(argv);
603
604#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
605 uint32_t uEax, uEbx, uEcx, uEdx;
606 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
607 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
608#else
609 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
610#endif
611 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
612}
613
614
615
616int main(int argc, char **argv)
617{
618 int rc = RTR3InitExe(argc, &argv, 0);
619 if (RT_FAILURE(rc))
620 return RTMsgInitFailure(rc);
621
622 /*
623 * The first argument is a command. Figure out which and call its handler.
624 */
625 static const struct
626 {
627 const char *pszCommand;
628 RTEXITCODE (*pfnHandler)(int argc, char **argv);
629 bool fNoArgs;
630 } s_aHandlers[] =
631 {
632 { "cpuvendor", handlerCpuVendor, true },
633 { "cpuname", handlerCpuName, true },
634 { "cpurevision", handlerCpuRevision, true },
635 { "cpuhwvirt", handlerCpuHwVirt, true },
636 { "nestedpaging", handlerCpuNestedPaging, true },
637 { "longmode", handlerCpuLongMode, true },
638 { "memsize", handlerMemSize, true },
639 { "report", handlerReport, true },
640 { "wipefreespace", handlerWipeFreeSpace, false }
641 };
642
643 if (argc < 2)
644 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
645
646 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
647 {
648 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
649 {
650 if ( s_aHandlers[i].fNoArgs
651 && argc != 2)
652 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
653 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
654 }
655 }
656
657 /*
658 * Help or version query?
659 */
660 for (int i = 1; i < argc; i++)
661 if ( !strcmp(argv[i], "--help")
662 || !strcmp(argv[i], "-h")
663 || !strcmp(argv[i], "-?")
664 || !strcmp(argv[i], "help") )
665 {
666 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
667 "\n"
668 "commands:\n", argv[0]);
669 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
670 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
671 return RTEXITCODE_FAILURE;
672 }
673 else if ( !strcmp(argv[i], "--version")
674 || !strcmp(argv[i], "-V") )
675 {
676 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
677 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
678 }
679
680 /*
681 * Syntax error.
682 */
683 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
684}
685
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette