VirtualBox

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

Last change on this file since 103159 was 103159, checked in by vboxsync, 12 months ago

ValidationKit/{common,testboxscript}: Add a has native API support flag when signing on indicating whether the testbox supports testing through NEM (Hyper-V on Windows, KVM on Linux and Hypervisor.framework on macOS). This is the first part not touching the actual testmanager, bugref:10592 [build and scm fix]

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