VirtualBox

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

Last change on this file since 99196 was 98929, checked in by vboxsync, 21 months ago

ValidationKit: More work to get it running on arm64, bugref:9898

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