VirtualBox

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

Last change on this file since 98418 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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