VirtualBox

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

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

oops

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.0 KB
Line 
1/* $Id: TestBoxHelper.cpp 64607 2016-11-08 18:32:00Z 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. Default: 0xf6f6f6f6\n"
225 " -m, --min-free <64-bit byte count>\n"
226 " Specifies when to stop in terms of free disk space (in bytes).\n"
227 " Default: 32MB\n"
228 "\n"
229 "Zero or more names of files to do the free space wiping thru can be given.\n"
230 "When --all is NOT used, each of the files are used to do free space wiping on\n"
231 "the volume they will live on. However, when --all is in effect the files are\n"
232 "appended to the volume mountpoints and only the first that can be created will\n"
233 "be used. Files (used ones) will be removed when done.\n"
234 );
235 return RTEXITCODE_SUCCESS;
236
237 default:
238 return RTGetOptPrintError(chOpt, &ValueUnion);
239 }
240 }
241
242 char **papszFiles;
243 if (chOpt == 0)
244 papszFiles = (char **)apszDefFiles;
245 else
246 papszFiles = RTGetOptNonOptionArrayPtr(&State);
247
248 /*
249 * Allocate and prep a memory which we'll write over and over again.
250 */
251 uint32_t cbFiller = _2M;
252 uint32_t *pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
253 while (!pu32Filler)
254 {
255 cbFiller <<= 1;
256 if (cbFiller >= _4K)
257 pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
258 else
259 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTMemPageAlloc failed for sizes between 4KB and 2MB!\n");
260 }
261 for (uint32_t i = 0; i < cbFiller / sizeof(pu32Filler[0]); i++)
262 pu32Filler[i] = u32Filler;
263
264 /*
265 * Do the requested work.
266 */
267 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
268 if (!fAll)
269 {
270 for (uint32_t iFile = 0; papszFiles[iFile] != NULL; iFile++)
271 {
272 RTEXITCODE rcExit2 = doOneFreeSpaceWipe(papszFiles[iFile], pu32Filler, cbFiller, cbMinLeftOpt);
273 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
274 rcExit = rcExit2;
275 }
276 }
277 else
278 {
279 /*
280 * Reject --all for now.
281 */
282 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "The --all option is not yet implemented!\n");
283 }
284
285 RTMemPageFree(pu32Filler, cbFiller);
286 return rcExit;
287}
288
289
290/**
291 * Generates a kind of report of the hardware, software and whatever else we
292 * think might be useful to know about the testbox.
293 */
294static RTEXITCODE handlerReport(int argc, char **argv)
295{
296 NOREF(argc); NOREF(argv);
297
298#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
299 /*
300 * For now, a simple CPUID dump. Need to figure out how to share code
301 * like this with other bits, putting it in IPRT.
302 */
303 RTPrintf("CPUID Dump\n"
304 "Leaf eax ebx ecx edx\n"
305 "---------------------------------------------\n");
306 static uint32_t const s_auRanges[] =
307 {
308 UINT32_C(0x00000000),
309 UINT32_C(0x80000000),
310 UINT32_C(0x80860000),
311 UINT32_C(0xc0000000),
312 UINT32_C(0x40000000),
313 };
314 for (uint32_t iRange = 0; iRange < RT_ELEMENTS(s_auRanges); iRange++)
315 {
316 uint32_t const uFirst = s_auRanges[iRange];
317
318 uint32_t uEax, uEbx, uEcx, uEdx;
319 ASMCpuIdExSlow(uFirst, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
320 if (uEax >= uFirst && uEax < uFirst + 100)
321 {
322 uint32_t const cLeafs = RT_MIN(uEax - uFirst + 1, 32);
323 for (uint32_t iLeaf = 0; iLeaf < cLeafs; iLeaf++)
324 {
325 uint32_t uLeaf = uFirst + iLeaf;
326 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
327
328 /* Clear APIC IDs to avoid submitting new reports all the time. */
329 if (uLeaf == 1)
330 uEbx &= UINT32_C(0x00ffffff);
331 if (uLeaf == 0xb)
332 uEdx = 0;
333 if (uLeaf == 0x8000001e)
334 uEax = 0;
335
336 /* Clear some other node/cpu/core/thread ids. */
337 if (uLeaf == 0x8000001e)
338 {
339 uEbx &= UINT32_C(0xffffff00);
340 uEcx &= UINT32_C(0xffffff00);
341 }
342
343 RTPrintf("%08x: %08x %08x %08x %08x\n", uLeaf, uEax, uEbx, uEcx, uEdx);
344 }
345 }
346 }
347 RTPrintf("\n");
348
349 /*
350 * DMI info.
351 */
352 RTPrintf("DMI Info\n"
353 "--------\n");
354 static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] =
355 {
356 { "Product Name", RTSYSDMISTR_PRODUCT_NAME },
357 { "Product version", RTSYSDMISTR_PRODUCT_VERSION },
358 { "Product UUID", RTSYSDMISTR_PRODUCT_UUID },
359 { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL },
360 { "System Manufacturer", RTSYSDMISTR_MANUFACTURER },
361 };
362 for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++)
363 {
364 char szTmp[4096];
365 RT_ZERO(szTmp);
366 int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1);
367 if (RT_SUCCESS(rc))
368 RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp));
369 else
370 RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc);
371 }
372 RTPrintf("\n");
373
374#else
375#endif
376
377 /*
378 * Dump the environment.
379 */
380 RTPrintf("Environment\n"
381 "-----------\n");
382 RTENV hEnv;
383 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
384 if (RT_SUCCESS(rc))
385 {
386 uint32_t cVars = RTEnvCountEx(hEnv);
387 for (uint32_t iVar = 0; iVar < cVars; iVar++)
388 {
389 char szVar[1024];
390 char szValue[16384];
391 rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
392
393 /* zap the value of variables that are subject to change. */
394 if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
395 && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV")
396 || !strcmp(szVar, "TESTBOX_ID")
397 || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE")
398 || !strcmp(szVar, "TESTBOX_TIMEOUT")
399 || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS")
400 || !strcmp(szVar, "TESTBOX_TEST_SET_ID")
401 )
402 )
403 strcpy(szValue, "<volatile>");
404
405 if (RT_SUCCESS(rc))
406 RTPrintf("%25s=%s\n", szVar, szValue);
407 else if (rc == VERR_BUFFER_OVERFLOW)
408 RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
409 else
410 RTPrintf("rc=%Rrc\n", rc);
411 }
412 RTEnvDestroy(hEnv);
413 }
414
415 /** @todo enumerate volumes and whatnot. */
416
417 int cch = RTPrintf("\n");
418 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
419}
420
421
422/** Print the total memory size in bytes. */
423static RTEXITCODE handlerMemSize(int argc, char **argv)
424{
425 NOREF(argc); NOREF(argv);
426
427 uint64_t cb;
428 int rc = RTSystemQueryTotalRam(&cb);
429 if (RT_SUCCESS(rc))
430 {
431 int cch = RTPrintf("%llu\n", cb);
432 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
433 }
434 RTPrintf("%Rrc\n", rc);
435 return RTEXITCODE_FAILURE;
436}
437
438typedef enum { HWVIRTTYPE_NONE, HWVIRTTYPE_VTX, HWVIRTTYPE_AMDV } HWVIRTTYPE;
439static HWVIRTTYPE isHwVirtSupported(void)
440{
441#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
442 uint32_t uEax, uEbx, uEcx, uEdx;
443
444 /* VT-x */
445 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
446 if (ASMIsValidStdRange(uEax))
447 {
448 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
449 if (uEcx & X86_CPUID_FEATURE_ECX_VMX)
450 return HWVIRTTYPE_VTX;
451 }
452
453 /* AMD-V */
454 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
455 if (ASMIsValidExtRange(uEax))
456 {
457 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
458 if (uEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
459 return HWVIRTTYPE_AMDV;
460 }
461#endif
462
463 return HWVIRTTYPE_NONE;
464}
465
466/** Print the 'true' if VT-x or AMD-v is supported, 'false' it not. */
467static RTEXITCODE handlerCpuHwVirt(int argc, char **argv)
468{
469 NOREF(argc); NOREF(argv);
470 int cch = RTPrintf(isHwVirtSupported() != HWVIRTTYPE_NONE ? "true\n" : "false\n");
471 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
472}
473
474
475/** Print the 'true' if nested paging is supported, 'false' if not and
476 * 'dunno' if we cannot tell. */
477static RTEXITCODE handlerCpuNestedPaging(int argc, char **argv)
478{
479 NOREF(argc); NOREF(argv);
480 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
481 int fSupported = -1;
482
483#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
484 if (enmHwVirt == HWVIRTTYPE_AMDV)
485 {
486 uint32_t uEax, uEbx, uEcx, uEdx;
487 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
488 if (ASMIsValidExtRange(uEax) && uEax >= 0x8000000a)
489 {
490 ASMCpuId(0x8000000a, &uEax, &uEbx, &uEcx, &uEdx);
491 if (uEdx & RT_BIT(0) /* AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING */)
492 fSupported = 1;
493 else
494 fSupported = 0;
495 }
496 }
497#endif
498
499 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
500 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
501}
502
503
504/** Print the 'true' if long mode guests are supported, 'false' if not and
505 * 'dunno' if we cannot tell. */
506static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
507{
508 NOREF(argc); NOREF(argv);
509 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
510 int fSupported = 0;
511
512 if (enmHwVirt != HWVIRTTYPE_NONE)
513 {
514#if defined(RT_ARCH_AMD64)
515 fSupported = 1; /* We're running long mode, so it must be supported. */
516
517#elif defined(RT_ARCH_X86)
518# ifdef RT_OS_DARWIN
519 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
520 int f64bitCapable = 0;
521 size_t cbParameter = sizeof(f64bitCapable);
522 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL);
523 if (rc != -1)
524 fSupported = f64bitCapable != 0;
525 else
526# endif
527 {
528 /* PAE and HwVirt are required */
529 uint32_t uEax, uEbx, uEcx, uEdx;
530 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
531 if (ASMIsValidStdRange(uEax))
532 {
533 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
534 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
535 {
536 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
537 won't necessarily do so. */
538 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
539 if (ASMIsValidExtRange(uEax))
540 {
541 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
542 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
543 fSupported = 1;
544 else if (enmHwVirt != HWVIRTTYPE_AMDV)
545 fSupported = -1;
546 }
547 }
548 }
549 }
550#endif
551 }
552
553 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
554 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
555}
556
557
558/** Print the CPU 'revision', if available. */
559static RTEXITCODE handlerCpuRevision(int argc, char **argv)
560{
561 NOREF(argc); NOREF(argv);
562
563#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
564 uint32_t uEax, uEbx, uEcx, uEdx;
565 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
566 if (ASMIsValidStdRange(uEax) && uEax >= 1)
567 {
568 uint32_t uEax1 = ASMCpuId_EAX(1);
569 uint32_t uVersion = (ASMGetCpuFamily(uEax1) << 24)
570 | (ASMGetCpuModel(uEax1, ASMIsIntelCpuEx(uEbx, uEcx, uEdx)) << 8)
571 | ASMGetCpuStepping(uEax1);
572 int cch = RTPrintf("%#x\n", uVersion);
573 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
574 }
575#endif
576 return RTEXITCODE_FAILURE;
577}
578
579
580/** Print the CPU name, if available. */
581static RTEXITCODE handlerCpuName(int argc, char **argv)
582{
583 NOREF(argc); NOREF(argv);
584
585 char szTmp[1024];
586 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
587 if (RT_SUCCESS(rc))
588 {
589 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
590 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
591 }
592 return RTEXITCODE_FAILURE;
593}
594
595
596/** Print the CPU vendor name, 'GenuineIntel' and such. */
597static RTEXITCODE handlerCpuVendor(int argc, char **argv)
598{
599 NOREF(argc); NOREF(argv);
600
601#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
602 uint32_t uEax, uEbx, uEcx, uEdx;
603 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
604 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
605#else
606 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
607#endif
608 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
609}
610
611
612
613int main(int argc, char **argv)
614{
615 int rc = RTR3InitExe(argc, &argv, 0);
616 if (RT_FAILURE(rc))
617 return RTMsgInitFailure(rc);
618
619 /*
620 * The first argument is a command. Figure out which and call its handler.
621 */
622 static const struct
623 {
624 const char *pszCommand;
625 RTEXITCODE (*pfnHandler)(int argc, char **argv);
626 bool fNoArgs;
627 } s_aHandlers[] =
628 {
629 { "cpuvendor", handlerCpuVendor, true },
630 { "cpuname", handlerCpuName, true },
631 { "cpurevision", handlerCpuRevision, true },
632 { "cpuhwvirt", handlerCpuHwVirt, true },
633 { "nestedpaging", handlerCpuNestedPaging, true },
634 { "longmode", handlerCpuLongMode, true },
635 { "memsize", handlerMemSize, true },
636 { "report", handlerReport, true },
637 { "wipefreespace", handlerWipeFreeSpace, false }
638 };
639
640 if (argc < 2)
641 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
642
643 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
644 {
645 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
646 {
647 if ( s_aHandlers[i].fNoArgs
648 && argc != 2)
649 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
650 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
651 }
652 }
653
654 /*
655 * Help or version query?
656 */
657 for (int i = 1; i < argc; i++)
658 if ( !strcmp(argv[i], "--help")
659 || !strcmp(argv[i], "-h")
660 || !strcmp(argv[i], "-?")
661 || !strcmp(argv[i], "help") )
662 {
663 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
664 "\n"
665 "commands:\n", argv[0]);
666 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
667 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
668 return RTEXITCODE_FAILURE;
669 }
670 else if ( !strcmp(argv[i], "--version")
671 || !strcmp(argv[i], "-V") )
672 {
673 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
674 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
675 }
676
677 /*
678 * Syntax error.
679 */
680 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
681}
682
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