VirtualBox

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

Last change on this file since 103011 was 101672, checked in by vboxsync, 15 months ago

ValidationKit/TestBoxHelper: Some fixes for ARM, bugref:10542

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 KB
Line 
1/* $Id: TestBoxHelper.cpp 101672 2023-10-31 09:22:10Z 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#else
384#endif
385
386 /*
387 * DMI info.
388 */
389 RTPrintf("DMI Info\n"
390 "--------\n");
391 static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] =
392 {
393 { "Product Name", RTSYSDMISTR_PRODUCT_NAME },
394 { "Product version", RTSYSDMISTR_PRODUCT_VERSION },
395 { "Product UUID", RTSYSDMISTR_PRODUCT_UUID },
396 { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL },
397 { "System Manufacturer", RTSYSDMISTR_MANUFACTURER },
398 };
399 for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++)
400 {
401 char szTmp[4096];
402 RT_ZERO(szTmp);
403 int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1);
404 if (RT_SUCCESS(rc))
405 RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp));
406 else
407 RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc);
408 }
409 RTPrintf("\n");
410
411 /*
412 * Dump the environment.
413 */
414 RTPrintf("Environment\n"
415 "-----------\n");
416 RTENV hEnv;
417 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
418 if (RT_SUCCESS(rc))
419 {
420 uint32_t cVars = RTEnvCountEx(hEnv);
421 for (uint32_t iVar = 0; iVar < cVars; iVar++)
422 {
423 char szVar[1024];
424 char szValue[16384];
425 rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
426
427 /* zap the value of variables that are subject to change. */
428 if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
429 && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV")
430 || !strcmp(szVar, "TESTBOX_ID")
431 || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE")
432 || !strcmp(szVar, "TESTBOX_TIMEOUT")
433 || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS")
434 || !strcmp(szVar, "TESTBOX_TEST_SET_ID")
435 )
436 )
437 strcpy(szValue, "<volatile>");
438
439 if (RT_SUCCESS(rc))
440 RTPrintf("%25s=%s\n", szVar, szValue);
441 else if (rc == VERR_BUFFER_OVERFLOW)
442 RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
443 else
444 RTPrintf("rc=%Rrc\n", rc);
445 }
446 RTEnvDestroy(hEnv);
447 }
448
449 /** @todo enumerate volumes and whatnot. */
450
451 int cch = RTPrintf("\n");
452 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
453}
454
455
456/** Print the total memory size in bytes. */
457static RTEXITCODE handlerMemSize(int argc, char **argv)
458{
459 NOREF(argc); NOREF(argv);
460
461 uint64_t cb;
462 int rc = RTSystemQueryTotalRam(&cb);
463 if (RT_SUCCESS(rc))
464 {
465 int cch = RTPrintf("%llu\n", cb);
466 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
467 }
468 RTPrintf("%Rrc\n", rc);
469 return RTEXITCODE_FAILURE;
470}
471
472typedef enum { HWVIRTTYPE_NONE, HWVIRTTYPE_VTX, HWVIRTTYPE_AMDV, HVIRTTYPE_ARMV8 } HWVIRTTYPE;
473static HWVIRTTYPE isHwVirtSupported(void)
474{
475#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
476 uint32_t uEax, uEbx, uEcx, uEdx;
477
478 /* VT-x */
479 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
480 if (RTX86IsValidStdRange(uEax))
481 {
482 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
483 if (uEcx & X86_CPUID_FEATURE_ECX_VMX)
484 return HWVIRTTYPE_VTX;
485 }
486
487 /* AMD-V */
488 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
489 if (RTX86IsValidExtRange(uEax))
490 {
491 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
492 if (uEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
493 return HWVIRTTYPE_AMDV;
494 }
495#elif defined(RT_ARCH_ARM64)
496# if defined(RT_OS_DARWIN)
497 /*
498 * The kern.hv_support parameter indicates support for the hypervisor API in the
499 * kernel, which is the only way for virtualization on macOS on Apple Silicon.
500 */
501 int32_t fHvSupport = 0;
502 size_t cbOld = sizeof(fHvSupport);
503 if (sysctlbyname("kern.hv_support", &fHvSupport, &cbOld, NULL, 0) == 0)
504 {
505 if (fHvSupport != 0)
506 return HVIRTTYPE_ARMV8;
507 }
508# endif
509#endif
510
511 return HWVIRTTYPE_NONE;
512}
513
514/** Print the 'true' if VT-x or AMD-v is supported, 'false' it not. */
515static RTEXITCODE handlerCpuHwVirt(int argc, char **argv)
516{
517 NOREF(argc); NOREF(argv);
518 int cch = RTPrintf(isHwVirtSupported() != HWVIRTTYPE_NONE ? "true\n" : "false\n");
519 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
520}
521
522
523/** Print the 'true' if nested paging is supported, 'false' if not and
524 * 'dunno' if we cannot tell. */
525static RTEXITCODE handlerCpuNestedPaging(int argc, char **argv)
526{
527 NOREF(argc); NOREF(argv);
528 int fSupported = -1;
529
530 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
531#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
532 if (enmHwVirt == HWVIRTTYPE_AMDV)
533 {
534 uint32_t uEax, uEbx, uEcx, uEdx;
535 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
536 if (RTX86IsValidExtRange(uEax) && uEax >= 0x8000000a)
537 {
538 ASMCpuId(0x8000000a, &uEax, &uEbx, &uEcx, &uEdx);
539 if (uEdx & RT_BIT(0) /* AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING */)
540 fSupported = 1;
541 else
542 fSupported = 0;
543 }
544 }
545# if defined(RT_OS_LINUX)
546 else if (enmHwVirt == HWVIRTTYPE_VTX)
547 {
548 /*
549 * For Intel there is no generic way to query EPT support but on
550 * Linux we can resort to checking for the EPT flag in /proc/cpuinfo
551 */
552 RTFILE hFileCpu;
553 int rc = RTFileOpen(&hFileCpu, "/proc/cpuinfo", RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
554 if (RT_SUCCESS(rc))
555 {
556 /*
557 * Read enough to fit the first CPU entry in, we only check the first
558 * CPU as all the others should have the same features.
559 */
560 char szBuf[_4K];
561 size_t cbRead = 0;
562
563 RT_ZERO(szBuf); /* Ensure proper termination. */
564 rc = RTFileRead(hFileCpu, &szBuf[0], sizeof(szBuf) - 1, &cbRead);
565 if (RT_SUCCESS(rc))
566 {
567 /* Look for the start of the flags section. */
568 char *pszStrFlags = RTStrStr(&szBuf[0], "flags");
569 if (pszStrFlags)
570 {
571 /* Look for the end as indicated by new line. */
572 char *pszEnd = pszStrFlags;
573 while ( *pszEnd != '\0'
574 && *pszEnd != '\n')
575 pszEnd++;
576 *pszEnd = '\0'; /* Cut off everything after the flags section. */
577
578 /*
579 * Search for the ept flag indicating support and the absence meaning
580 * not supported.
581 */
582 if (RTStrStr(pszStrFlags, "ept"))
583 fSupported = 1;
584 else
585 fSupported = 0;
586 }
587 }
588 RTFileClose(hFileCpu);
589 }
590 }
591# elif defined(RT_OS_DARWIN)
592 else if (enmHwVirt == HWVIRTTYPE_VTX)
593 {
594 /*
595 * The kern.hv_support parameter indicates support for the hypervisor API in the
596 * kernel, which in turn is documented require nested paging and unrestricted
597 * guest mode. So, if it's there and set we've got nested paging. Howeber, if
598 * it's there and clear we have not definite answer as it might be due to lack
599 * of unrestricted guest mode support.
600 */
601 int32_t fHvSupport = 0;
602 size_t cbOld = sizeof(fHvSupport);
603 if (sysctlbyname("kern.hv_support", &fHvSupport, &cbOld, NULL, 0) == 0)
604 {
605 if (fHvSupport != 0)
606 fSupported = true;
607 }
608 }
609# endif
610#elif defined(RT_ARCH_ARM64)
611 /* On ARM nested paging is always supported if virtualization is there. */
612 if (enmHwVirt == HVIRTTYPE_ARMV8)
613 fSupported = 1;
614#endif
615
616 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
617 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
618}
619
620
621/** Print the 'true' if long mode guests are supported, 'false' if not and
622 * 'dunno' if we cannot tell. */
623static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
624{
625 NOREF(argc); NOREF(argv);
626 HWVIRTTYPE enmHwVirt = isHwVirtSupported();
627 int fSupported = 0;
628
629 if (enmHwVirt != HWVIRTTYPE_NONE)
630 {
631#if defined(RT_ARCH_AMD64)
632 fSupported = 1; /* We're running long mode, so it must be supported. */
633
634#elif defined(RT_ARCH_X86)
635# ifdef RT_OS_DARWIN
636 /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
637 int f64bitCapable = 0;
638 size_t cbParameter = sizeof(f64bitCapable);
639 int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, 0);
640 if (rc != -1)
641 fSupported = f64bitCapable != 0;
642 else
643# endif
644 {
645 /* PAE and HwVirt are required */
646 uint32_t uEax, uEbx, uEcx, uEdx;
647 ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
648 if (RTX86IsValidStdRange(uEax))
649 {
650 ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
651 if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
652 {
653 /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
654 won't necessarily do so. */
655 ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
656 if (RTX86IsValidExtRange(uEax))
657 {
658 ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
659 if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
660 fSupported = 1;
661 else if (enmHwVirt != HWVIRTTYPE_AMDV)
662 fSupported = -1;
663 }
664 }
665 }
666 }
667#elif defined(RT_ARCH_ARM64)
668 fSupported = 1; /** @todo LongMode is a misnomer here but in general means 64-bit guest capable. */
669#endif
670 }
671
672 int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
673 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
674}
675
676
677/** Print the CPU 'revision', if available. */
678static RTEXITCODE handlerCpuRevision(int argc, char **argv)
679{
680 NOREF(argc); NOREF(argv);
681
682#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
683 uint32_t uEax, uEbx, uEcx, uEdx;
684 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
685 if (RTX86IsValidStdRange(uEax) && uEax >= 1)
686 {
687 uint32_t uEax1 = ASMCpuId_EAX(1);
688 uint32_t uVersion = (RTX86GetCpuFamily(uEax1) << 24)
689 | (RTX86GetCpuModel(uEax1, RTX86IsIntelCpu(uEbx, uEcx, uEdx)) << 8)
690 | RTX86GetCpuStepping(uEax1);
691 int cch = RTPrintf("%#x\n", uVersion);
692 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
693 }
694#elif defined(RT_ARCH_ARM) || defined(RT_ARCH_ARM64)
695 /** @todo There is no way to access MIDR_EL1 from userspace except for parsing the various
696 * OS dependent ways (/proc/cpuinfo, sysctl, ...). Just fake it for now to get it running. */
697 int cch = RTPrintf("%#x\n", 1);
698 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
699#endif
700 return RTEXITCODE_FAILURE;
701}
702
703
704/** Print the CPU name, if available. */
705static RTEXITCODE handlerCpuName(int argc, char **argv)
706{
707 NOREF(argc); NOREF(argv);
708
709 char szTmp[1024];
710 int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
711 if (RT_SUCCESS(rc))
712 {
713 int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
714 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
715 }
716 return RTEXITCODE_FAILURE;
717}
718
719
720/** Print the CPU vendor name, 'GenuineIntel' and such. */
721static RTEXITCODE handlerCpuVendor(int argc, char **argv)
722{
723 NOREF(argc); NOREF(argv);
724
725#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
726 uint32_t uEax, uEbx, uEcx, uEdx;
727 ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
728 int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
729#elif defined(RT_ARCH_ARM64) && defined(RT_OS_DARWIN)
730 /*
731 * There is machdep.cpu.brand_string we could query but that identifies
732 * the whole CPU and not just the vendor.
733 *
734 * Running on macOS using the arm64 architecture is a pretty safe bet that
735 * we are also running on Apple Silicon (there is the possibility that
736 * this runs in a macOS VM on some other hardware but this is highly unlikely).
737 */
738 int cch = RTPrintf("Apple\n");
739#else
740 int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
741#endif
742 return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
743}
744
745
746
747int main(int argc, char **argv)
748{
749 int rc = RTR3InitExe(argc, &argv, 0);
750 if (RT_FAILURE(rc))
751 return RTMsgInitFailure(rc);
752
753 /*
754 * The first argument is a command. Figure out which and call its handler.
755 */
756 static const struct
757 {
758 const char *pszCommand;
759 RTEXITCODE (*pfnHandler)(int argc, char **argv);
760 bool fNoArgs;
761 } s_aHandlers[] =
762 {
763 { "cpuvendor", handlerCpuVendor, true },
764 { "cpuname", handlerCpuName, true },
765 { "cpurevision", handlerCpuRevision, true },
766 { "cpuhwvirt", handlerCpuHwVirt, true },
767 { "nestedpaging", handlerCpuNestedPaging, true },
768 { "longmode", handlerCpuLongMode, true },
769 { "memsize", handlerMemSize, true },
770 { "report", handlerReport, true },
771 { "wipefreespace", handlerWipeFreeSpace, false }
772 };
773
774 if (argc < 2)
775 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
776
777 for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
778 {
779 if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
780 {
781 if ( s_aHandlers[i].fNoArgs
782 && argc != 2)
783 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
784 return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
785 }
786 }
787
788 /*
789 * Help or version query?
790 */
791 for (int i = 1; i < argc; i++)
792 if ( !strcmp(argv[i], "--help")
793 || !strcmp(argv[i], "-h")
794 || !strcmp(argv[i], "-?")
795 || !strcmp(argv[i], "help") )
796 {
797 RTPrintf("usage: %s <cmd> [cmd specific args]\n"
798 "\n"
799 "commands:\n", argv[0]);
800 for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
801 RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
802 return RTEXITCODE_FAILURE;
803 }
804 else if ( !strcmp(argv[i], "--version")
805 || !strcmp(argv[i], "-V") )
806 {
807 RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
808 return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
809 }
810
811 /*
812 * Syntax error.
813 */
814 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
815}
816
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