1 | /* $Id: NemRawBench-1.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * NEM Benchmark.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2018-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 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
26 | */
|
---|
27 |
|
---|
28 |
|
---|
29 | /*********************************************************************************************************************************
|
---|
30 | * Header Files *
|
---|
31 | *********************************************************************************************************************************/
|
---|
32 | #ifdef RT_OS_WINDOWS
|
---|
33 | # include <iprt/win/windows.h>
|
---|
34 | # include <WinHvPlatform.h>
|
---|
35 | # if !defined(_INTPTR) && defined(_M_AMD64) /* void pedantic stdint.h warnings */
|
---|
36 | # define _INTPTR 2
|
---|
37 | # endif
|
---|
38 |
|
---|
39 | #elif defined(RT_OS_LINUX)
|
---|
40 | # include <linux/kvm.h>
|
---|
41 | # include <errno.h>
|
---|
42 | # include <sys/fcntl.h>
|
---|
43 | # include <sys/ioctl.h>
|
---|
44 | # include <sys/mman.h>
|
---|
45 | # include <unistd.h>
|
---|
46 | # include <time.h>
|
---|
47 |
|
---|
48 | #elif defined(RT_OS_DARWIN)
|
---|
49 | # include <Availability.h>
|
---|
50 | # if 1 /* header mix hack */
|
---|
51 | # undef __OSX_AVAILABLE_STARTING
|
---|
52 | # define __OSX_AVAILABLE_STARTING(_osx, _ios)
|
---|
53 | # endif
|
---|
54 | # include <Hypervisor/hv.h>
|
---|
55 | # include <Hypervisor/hv_arch_x86.h>
|
---|
56 | # include <Hypervisor/hv_arch_vmx.h>
|
---|
57 | # include <Hypervisor/hv_vmx.h>
|
---|
58 | # include <mach/mach_time.h>
|
---|
59 | # include <mach/kern_return.h>
|
---|
60 | # include <sys/time.h>
|
---|
61 | # include <time.h>
|
---|
62 | # include <sys/mman.h>
|
---|
63 | # include <errno.h>
|
---|
64 |
|
---|
65 | #else
|
---|
66 | # error "port me"
|
---|
67 | #endif
|
---|
68 |
|
---|
69 | #include <iprt/stream.h>
|
---|
70 | #include <iprt/stdarg.h>
|
---|
71 | #include <iprt/types.h>
|
---|
72 | #include <iprt/string.h>
|
---|
73 |
|
---|
74 |
|
---|
75 | /*********************************************************************************************************************************
|
---|
76 | * Defined Constants And Macros *
|
---|
77 | *********************************************************************************************************************************/
|
---|
78 | /** The base mapping address of the g_pbMem. */
|
---|
79 | #define MY_MEM_BASE 0x1000
|
---|
80 | /** No-op MMIO access address. */
|
---|
81 | #define MY_NOP_MMIO 0x0808
|
---|
82 | /** The RIP which the testcode starts. */
|
---|
83 | #define MY_TEST_RIP 0x2000
|
---|
84 |
|
---|
85 | /** The test termination port number. */
|
---|
86 | #define MY_TERM_PORT 0x01
|
---|
87 | /** The no-op test port number. */
|
---|
88 | #define MY_NOP_PORT 0x7f
|
---|
89 |
|
---|
90 | #define MY_TEST_F_NOP_IO (1U<<0)
|
---|
91 | #define MY_TEST_F_CPUID (1U<<1)
|
---|
92 | #define MY_TEST_F_NOP_MMIO (1U<<2)
|
---|
93 |
|
---|
94 |
|
---|
95 |
|
---|
96 | /*********************************************************************************************************************************
|
---|
97 | * Global Variables *
|
---|
98 | *********************************************************************************************************************************/
|
---|
99 | /** Chunk of memory mapped at address 0x1000 (MY_MEM_BASE). */
|
---|
100 | static unsigned char *g_pbMem;
|
---|
101 | /** Amount of RAM at address 0x1000 (MY_MEM_BASE). */
|
---|
102 | static size_t g_cbMem;
|
---|
103 | #ifdef RT_OS_WINDOWS
|
---|
104 | static WHV_PARTITION_HANDLE g_hPartition = NULL;
|
---|
105 |
|
---|
106 | /** @name APIs imported from WinHvPlatform.dll
|
---|
107 | * @{ */
|
---|
108 | static decltype(WHvCreatePartition) *g_pfnWHvCreatePartition;
|
---|
109 | static decltype(WHvSetupPartition) *g_pfnWHvSetupPartition;
|
---|
110 | static decltype(WHvGetPartitionProperty) *g_pfnWHvGetPartitionProperty;
|
---|
111 | static decltype(WHvSetPartitionProperty) *g_pfnWHvSetPartitionProperty;
|
---|
112 | static decltype(WHvMapGpaRange) *g_pfnWHvMapGpaRange;
|
---|
113 | static decltype(WHvCreateVirtualProcessor) *g_pfnWHvCreateVirtualProcessor;
|
---|
114 | static decltype(WHvRunVirtualProcessor) *g_pfnWHvRunVirtualProcessor;
|
---|
115 | static decltype(WHvGetVirtualProcessorRegisters) *g_pfnWHvGetVirtualProcessorRegisters;
|
---|
116 | static decltype(WHvSetVirtualProcessorRegisters) *g_pfnWHvSetVirtualProcessorRegisters;
|
---|
117 | /** @} */
|
---|
118 | static uint64_t (WINAPI *g_pfnRtlGetSystemTimePrecise)(void);
|
---|
119 |
|
---|
120 | #elif defined(RT_OS_LINUX)
|
---|
121 | /** The VM handle. */
|
---|
122 | static int g_fdVm;
|
---|
123 | /** The VCPU handle. */
|
---|
124 | static int g_fdVCpu;
|
---|
125 | /** The kvm_run structure for the VCpu. */
|
---|
126 | static struct kvm_run *g_pVCpuRun;
|
---|
127 | /** The size of the g_pVCpuRun mapping. */
|
---|
128 | static ssize_t g_cbVCpuRun;
|
---|
129 |
|
---|
130 | #elif defined(RT_OS_DARWIN)
|
---|
131 | /** The VCpu ID. */
|
---|
132 | static hv_vcpuid_t g_idVCpu;
|
---|
133 | #endif
|
---|
134 |
|
---|
135 |
|
---|
136 | static int error(const char *pszFormat, ...)
|
---|
137 | {
|
---|
138 | RTStrmPrintf(g_pStdErr, "error: ");
|
---|
139 | va_list va;
|
---|
140 | va_start(va, pszFormat);
|
---|
141 | RTStrmPrintfV(g_pStdErr, pszFormat, va);
|
---|
142 | va_end(va);
|
---|
143 | return 1;
|
---|
144 | }
|
---|
145 |
|
---|
146 |
|
---|
147 | static uint64_t getNanoTS(void)
|
---|
148 | {
|
---|
149 | #ifdef RT_OS_WINDOWS
|
---|
150 | return g_pfnRtlGetSystemTimePrecise() * 100;
|
---|
151 |
|
---|
152 | #elif defined(RT_OS_LINUX)
|
---|
153 | struct timespec ts;
|
---|
154 | clock_gettime(CLOCK_MONOTONIC, &ts);
|
---|
155 | return (uint64_t)ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
|
---|
156 |
|
---|
157 | #elif defined(RT_OS_DARWIN)
|
---|
158 | static struct mach_timebase_info s_Info = { 0, 0 };
|
---|
159 | static double s_rdFactor = 0.0;
|
---|
160 | /* Lazy init. */
|
---|
161 | if (s_Info.denom != 0)
|
---|
162 | { /* likely */ }
|
---|
163 | else if (mach_timebase_info(&s_Info) == KERN_SUCCESS)
|
---|
164 | s_rdFactor = (double)s_Info.numer / (double)s_Info.denom;
|
---|
165 | else
|
---|
166 | {
|
---|
167 | error("mach_timebase_info(&Info) failed\n");
|
---|
168 | exit(1);
|
---|
169 | }
|
---|
170 | if (s_Info.denom == 1 && s_Info.numer == 1) /* special case: absolute time is in nanoseconds */
|
---|
171 | return mach_absolute_time();
|
---|
172 | return mach_absolute_time() * s_rdFactor;
|
---|
173 | #else
|
---|
174 | struct timeval tv;
|
---|
175 | gettimeofday(&tv, NULL);
|
---|
176 | return (uint64_t)tv.tv_sec * UINT64_C(1000000000)
|
---|
177 | + (tv.tv_usec * UINT32_C(1000));
|
---|
178 | #endif
|
---|
179 | }
|
---|
180 |
|
---|
181 |
|
---|
182 | char *formatNum(uint64_t uNum, unsigned cchWidth, char *pszDst, size_t cbDst)
|
---|
183 | {
|
---|
184 | char szTmp[64 + 22];
|
---|
185 | size_t cchTmp = RTStrPrintf(szTmp, sizeof(szTmp) - 22, "%llu", (unsigned long long)uNum);
|
---|
186 | size_t cSeps = (cchTmp - 1) / 3;
|
---|
187 | size_t const cchTotal = cchTmp + cSeps;
|
---|
188 | if (cSeps)
|
---|
189 | {
|
---|
190 | szTmp[cchTotal] = '\0';
|
---|
191 | for (size_t iSrc = cchTmp, iDst = cchTotal; cSeps > 0; cSeps--)
|
---|
192 | {
|
---|
193 | szTmp[--iDst] = szTmp[--iSrc];
|
---|
194 | szTmp[--iDst] = szTmp[--iSrc];
|
---|
195 | szTmp[--iDst] = szTmp[--iSrc];
|
---|
196 | szTmp[--iDst] = ' ';
|
---|
197 | }
|
---|
198 | }
|
---|
199 |
|
---|
200 | size_t offDst = 0;
|
---|
201 | while (cchWidth-- > cchTotal && offDst < cbDst)
|
---|
202 | pszDst[offDst++] = ' ';
|
---|
203 | size_t offSrc = 0;
|
---|
204 | while (offSrc < cchTotal && offDst < cbDst)
|
---|
205 | pszDst[offDst++] = szTmp[offSrc++];
|
---|
206 | pszDst[offDst] = '\0';
|
---|
207 | return pszDst;
|
---|
208 | }
|
---|
209 |
|
---|
210 |
|
---|
211 | int reportResult(const char *pszInstruction, uint32_t cInstructions, uint64_t nsElapsed, uint32_t cExits)
|
---|
212 | {
|
---|
213 | uint64_t const cInstrPerSec = nsElapsed ? (uint64_t)cInstructions * 1000000000 / nsElapsed : 0;
|
---|
214 | char szTmp1[64], szTmp2[64], szTmp3[64];
|
---|
215 | RTPrintf("%s %7s instructions per second (%s exits in %s ns)\n",
|
---|
216 | formatNum(cInstrPerSec, 10, szTmp1, sizeof(szTmp1)), pszInstruction,
|
---|
217 | formatNum(cExits, 0, szTmp2, sizeof(szTmp2)),
|
---|
218 | formatNum(nsElapsed, 0, szTmp3, sizeof(szTmp3)));
|
---|
219 | return 0;
|
---|
220 | }
|
---|
221 |
|
---|
222 |
|
---|
223 |
|
---|
224 | #ifdef RT_OS_WINDOWS
|
---|
225 |
|
---|
226 | /*
|
---|
227 | * Windows - Hyper-V Platform API.
|
---|
228 | */
|
---|
229 |
|
---|
230 | static int createVM(void)
|
---|
231 | {
|
---|
232 | /*
|
---|
233 | * Resolve APIs.
|
---|
234 | */
|
---|
235 | HMODULE hmod = LoadLibraryW(L"WinHvPlatform.dll");
|
---|
236 | if (hmod == NULL)
|
---|
237 | return error("Error loading WinHvPlatform.dll: %u\n", GetLastError());
|
---|
238 | static struct { const char *pszFunction; FARPROC *ppfn; } const s_aImports[] =
|
---|
239 | {
|
---|
240 | # define IMPORT_ENTRY(a_Name) { #a_Name, (FARPROC *)&g_pfn##a_Name }
|
---|
241 | IMPORT_ENTRY(WHvCreatePartition),
|
---|
242 | IMPORT_ENTRY(WHvSetupPartition),
|
---|
243 | IMPORT_ENTRY(WHvGetPartitionProperty),
|
---|
244 | IMPORT_ENTRY(WHvSetPartitionProperty),
|
---|
245 | IMPORT_ENTRY(WHvMapGpaRange),
|
---|
246 | IMPORT_ENTRY(WHvCreateVirtualProcessor),
|
---|
247 | IMPORT_ENTRY(WHvRunVirtualProcessor),
|
---|
248 | IMPORT_ENTRY(WHvGetVirtualProcessorRegisters),
|
---|
249 | IMPORT_ENTRY(WHvSetVirtualProcessorRegisters),
|
---|
250 | # undef IMPORT_ENTRY
|
---|
251 | };
|
---|
252 | FARPROC pfn;
|
---|
253 | for (size_t i = 0; i < sizeof(s_aImports) / sizeof(s_aImports[0]); i++)
|
---|
254 | {
|
---|
255 | *s_aImports[i].ppfn = pfn = GetProcAddress(hmod, s_aImports[i].pszFunction);
|
---|
256 | if (!pfn)
|
---|
257 | return error("Error resolving WinHvPlatform.dll!%s: %u\n", s_aImports[i].pszFunction, GetLastError());
|
---|
258 | }
|
---|
259 | # ifndef IN_SLICKEDIT
|
---|
260 | # define WHvCreatePartition g_pfnWHvCreatePartition
|
---|
261 | # define WHvSetupPartition g_pfnWHvSetupPartition
|
---|
262 | # define WHvGetPartitionProperty g_pfnWHvGetPartitionProperty
|
---|
263 | # define WHvSetPartitionProperty g_pfnWHvSetPartitionProperty
|
---|
264 | # define WHvMapGpaRange g_pfnWHvMapGpaRange
|
---|
265 | # define WHvCreateVirtualProcessor g_pfnWHvCreateVirtualProcessor
|
---|
266 | # define WHvRunVirtualProcessor g_pfnWHvRunVirtualProcessor
|
---|
267 | # define WHvGetVirtualProcessorRegisters g_pfnWHvGetVirtualProcessorRegisters
|
---|
268 | # define WHvSetVirtualProcessorRegisters g_pfnWHvSetVirtualProcessorRegisters
|
---|
269 | # endif
|
---|
270 | /* Need a precise time function. */
|
---|
271 | *(FARPROC *)&g_pfnRtlGetSystemTimePrecise = pfn = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetSystemTimePrecise");
|
---|
272 | if (pfn == NULL)
|
---|
273 | return error("Error resolving ntdll.dll!RtlGetSystemTimePrecise: %u\n", GetLastError());
|
---|
274 |
|
---|
275 | /*
|
---|
276 | * Create the partition with 1 CPU and the specfied amount of memory.
|
---|
277 | */
|
---|
278 | WHV_PARTITION_HANDLE hPartition;
|
---|
279 | HRESULT hrc = WHvCreatePartition(&hPartition);
|
---|
280 | if (!SUCCEEDED(hrc))
|
---|
281 | return error("WHvCreatePartition failed: %#x\n", hrc);
|
---|
282 | g_hPartition = hPartition;
|
---|
283 |
|
---|
284 | WHV_PARTITION_PROPERTY Property;
|
---|
285 | memset(&Property, 0, sizeof(Property));
|
---|
286 | Property.ProcessorCount = 1;
|
---|
287 | hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeProcessorCount, &Property, sizeof(Property));
|
---|
288 | if (!SUCCEEDED(hrc))
|
---|
289 | return error("WHvSetPartitionProperty/WHvPartitionPropertyCodeProcessorCount failed: %#x\n", hrc);
|
---|
290 |
|
---|
291 | memset(&Property, 0, sizeof(Property));
|
---|
292 | Property.ExtendedVmExits.X64CpuidExit = 1;
|
---|
293 | Property.ExtendedVmExits.X64MsrExit = 1;
|
---|
294 | hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeExtendedVmExits, &Property, sizeof(Property));
|
---|
295 | if (!SUCCEEDED(hrc))
|
---|
296 | return error("WHvSetPartitionProperty/WHvPartitionPropertyCodeExtendedVmExits failed: %#x\n", hrc);
|
---|
297 |
|
---|
298 | hrc = WHvSetupPartition(hPartition);
|
---|
299 | if (!SUCCEEDED(hrc))
|
---|
300 | return error("WHvSetupPartition failed: %#x\n", hrc);
|
---|
301 |
|
---|
302 | hrc = WHvCreateVirtualProcessor(hPartition, 0 /*idVCpu*/, 0 /*fFlags*/);
|
---|
303 | if (!SUCCEEDED(hrc))
|
---|
304 | return error("WHvCreateVirtualProcessor failed: %#x\n", hrc);
|
---|
305 |
|
---|
306 | g_pbMem = (unsigned char *)VirtualAlloc(NULL, g_cbMem, MEM_COMMIT, PAGE_READWRITE);
|
---|
307 | if (!g_pbMem)
|
---|
308 | return error("VirtualAlloc failed: %u\n", GetLastError());
|
---|
309 | memset(g_pbMem, 0xcc, g_cbMem);
|
---|
310 |
|
---|
311 | hrc = WHvMapGpaRange(hPartition, g_pbMem, MY_MEM_BASE /*GCPhys*/, g_cbMem,
|
---|
312 | WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite | WHvMapGpaRangeFlagExecute);
|
---|
313 | if (!SUCCEEDED(hrc))
|
---|
314 | return error("WHvMapGpaRange failed: %#x\n", hrc);
|
---|
315 |
|
---|
316 | WHV_RUN_VP_EXIT_CONTEXT ExitInfo;
|
---|
317 | memset(&ExitInfo, 0, sizeof(ExitInfo));
|
---|
318 | WHvRunVirtualProcessor(g_hPartition, 0 /*idCpu*/, &ExitInfo, sizeof(ExitInfo));
|
---|
319 |
|
---|
320 | return 0;
|
---|
321 | }
|
---|
322 |
|
---|
323 |
|
---|
324 | static int runtimeError(const char *pszFormat, ...)
|
---|
325 | {
|
---|
326 | RTStrmPrintf(g_pStdErr, "runtime error: ");
|
---|
327 | va_list va;
|
---|
328 | va_start(va, pszFormat);
|
---|
329 | RTStrmPrintfV(g_pStdErr, pszFormat, va);
|
---|
330 | va_end(va);
|
---|
331 |
|
---|
332 | static struct { const char *pszName; WHV_REGISTER_NAME enmName; unsigned uType; } const s_aRegs[] =
|
---|
333 | {
|
---|
334 | { "rip", WHvX64RegisterRip, 64 },
|
---|
335 | { "cs", WHvX64RegisterCs, 1 },
|
---|
336 | { "rflags", WHvX64RegisterRflags, 32 },
|
---|
337 | { "rax", WHvX64RegisterRax, 64 },
|
---|
338 | { "rcx", WHvX64RegisterRcx, 64 },
|
---|
339 | { "rdx", WHvX64RegisterRdx, 64 },
|
---|
340 | { "rbx", WHvX64RegisterRbx, 64 },
|
---|
341 | { "rsp", WHvX64RegisterRsp, 64 },
|
---|
342 | { "ss", WHvX64RegisterSs, 1 },
|
---|
343 | { "rbp", WHvX64RegisterRbp, 64 },
|
---|
344 | { "rsi", WHvX64RegisterRsi, 64 },
|
---|
345 | { "rdi", WHvX64RegisterRdi, 64 },
|
---|
346 | { "ds", WHvX64RegisterDs, 1 },
|
---|
347 | { "es", WHvX64RegisterEs, 1 },
|
---|
348 | { "fs", WHvX64RegisterFs, 1 },
|
---|
349 | { "gs", WHvX64RegisterGs, 1 },
|
---|
350 | { "cr0", WHvX64RegisterCr0, 64 },
|
---|
351 | { "cr2", WHvX64RegisterCr2, 64 },
|
---|
352 | { "cr3", WHvX64RegisterCr3, 64 },
|
---|
353 | { "cr4", WHvX64RegisterCr4, 64 },
|
---|
354 | };
|
---|
355 | for (unsigned i = 0; i < sizeof(s_aRegs) / sizeof(s_aRegs[0]); i++)
|
---|
356 | {
|
---|
357 | WHV_REGISTER_VALUE Value;
|
---|
358 | WHV_REGISTER_NAME enmName = s_aRegs[i].enmName;
|
---|
359 | HRESULT hrc = WHvGetVirtualProcessorRegisters(g_hPartition, 0 /*idCpu*/, &enmName, 1, &Value);
|
---|
360 | if (SUCCEEDED(hrc))
|
---|
361 | {
|
---|
362 | if (s_aRegs[i].uType == 32)
|
---|
363 | RTStrmPrintf(g_pStdErr, "%8s=%08x\n", s_aRegs[i].pszName, Value.Reg32);
|
---|
364 | else if (s_aRegs[i].uType == 64)
|
---|
365 | RTStrmPrintf(g_pStdErr, "%8s=%08x'%08x\n", s_aRegs[i].pszName, (unsigned)(Value.Reg64 >> 32), Value.Reg32);
|
---|
366 | else if (s_aRegs[i].uType == 1)
|
---|
367 | RTStrmPrintf(g_pStdErr, "%8s=%04x base=%08x'%08x limit=%08x attr=%04x\n", s_aRegs[i].pszName,
|
---|
368 | Value.Segment.Selector, (unsigned)(Value.Segment.Base >> 32), (unsigned)Value.Segment.Base,
|
---|
369 | Value.Segment.Limit, Value.Segment.Attributes);
|
---|
370 | }
|
---|
371 | else
|
---|
372 | RTStrmPrintf(g_pStdErr, "%8s=<WHvGetVirtualProcessorRegisters failed %#x>\n", s_aRegs[i].pszName, hrc);
|
---|
373 | }
|
---|
374 |
|
---|
375 | return 1;
|
---|
376 | }
|
---|
377 |
|
---|
378 |
|
---|
379 | static int runRealModeTest(unsigned cInstructions, const char *pszInstruction, unsigned fTest,
|
---|
380 | unsigned uEax, unsigned uEcx, unsigned uEdx, unsigned uEbx,
|
---|
381 | unsigned uEsp, unsigned uEbp, unsigned uEsi, unsigned uEdi)
|
---|
382 | {
|
---|
383 | (void)fTest;
|
---|
384 |
|
---|
385 | /*
|
---|
386 | * Initialize the real mode context.
|
---|
387 | */
|
---|
388 | # define ADD_REG64(a_enmName, a_uValue) do { \
|
---|
389 | aenmNames[iReg] = (a_enmName); \
|
---|
390 | aValues[iReg].Reg128.High64 = 0; \
|
---|
391 | aValues[iReg].Reg64 = (a_uValue); \
|
---|
392 | iReg++; \
|
---|
393 | } while (0)
|
---|
394 | # define ADD_SEG(a_enmName, a_Base, a_Limit, a_Sel, a_fCode) \
|
---|
395 | do { \
|
---|
396 | aenmNames[iReg] = a_enmName; \
|
---|
397 | aValues[iReg].Segment.Base = (a_Base); \
|
---|
398 | aValues[iReg].Segment.Limit = (a_Limit); \
|
---|
399 | aValues[iReg].Segment.Selector = (a_Sel); \
|
---|
400 | aValues[iReg].Segment.Attributes = a_fCode ? 0x9b : 0x93; \
|
---|
401 | iReg++; \
|
---|
402 | } while (0)
|
---|
403 | WHV_REGISTER_NAME aenmNames[80];
|
---|
404 | WHV_REGISTER_VALUE aValues[80];
|
---|
405 | unsigned iReg = 0;
|
---|
406 | ADD_REG64(WHvX64RegisterRax, uEax);
|
---|
407 | ADD_REG64(WHvX64RegisterRcx, uEcx);
|
---|
408 | ADD_REG64(WHvX64RegisterRdx, uEdx);
|
---|
409 | ADD_REG64(WHvX64RegisterRbx, uEbx);
|
---|
410 | ADD_REG64(WHvX64RegisterRsp, uEsp);
|
---|
411 | ADD_REG64(WHvX64RegisterRbp, uEbp);
|
---|
412 | ADD_REG64(WHvX64RegisterRsi, uEsi);
|
---|
413 | ADD_REG64(WHvX64RegisterRdi, uEdi);
|
---|
414 | ADD_REG64(WHvX64RegisterRip, MY_TEST_RIP);
|
---|
415 | ADD_REG64(WHvX64RegisterRflags, 2);
|
---|
416 | ADD_SEG(WHvX64RegisterEs, 0x00000, 0xffff, 0x0000, 0);
|
---|
417 | ADD_SEG(WHvX64RegisterCs, 0x00000, 0xffff, 0x0000, 1);
|
---|
418 | ADD_SEG(WHvX64RegisterSs, 0x00000, 0xffff, 0x0000, 0);
|
---|
419 | ADD_SEG(WHvX64RegisterDs, 0x00000, 0xffff, 0x0000, 0);
|
---|
420 | ADD_SEG(WHvX64RegisterFs, 0x00000, 0xffff, 0x0000, 0);
|
---|
421 | ADD_SEG(WHvX64RegisterGs, 0x00000, 0xffff, 0x0000, 0);
|
---|
422 | ADD_REG64(WHvX64RegisterCr0, 0x10010 /*WP+ET*/);
|
---|
423 | ADD_REG64(WHvX64RegisterCr2, 0);
|
---|
424 | ADD_REG64(WHvX64RegisterCr3, 0);
|
---|
425 | ADD_REG64(WHvX64RegisterCr4, 0);
|
---|
426 | HRESULT hrc = WHvSetVirtualProcessorRegisters(g_hPartition, 0 /*idCpu*/, aenmNames, iReg, aValues);
|
---|
427 | if (!SUCCEEDED(hrc))
|
---|
428 | return error("WHvSetVirtualProcessorRegisters failed (for %s): %#x\n", pszInstruction, hrc);
|
---|
429 | # undef ADD_REG64
|
---|
430 | # undef ADD_SEG
|
---|
431 |
|
---|
432 | /*
|
---|
433 | * Run the test.
|
---|
434 | */
|
---|
435 | uint32_t cExits = 0;
|
---|
436 | uint64_t const nsStart = getNanoTS();
|
---|
437 | for (;;)
|
---|
438 | {
|
---|
439 | WHV_RUN_VP_EXIT_CONTEXT ExitInfo;
|
---|
440 | memset(&ExitInfo, 0, sizeof(ExitInfo));
|
---|
441 | hrc = WHvRunVirtualProcessor(g_hPartition, 0 /*idCpu*/, &ExitInfo, sizeof(ExitInfo));
|
---|
442 | if (SUCCEEDED(hrc))
|
---|
443 | {
|
---|
444 | cExits++;
|
---|
445 | if (ExitInfo.ExitReason == WHvRunVpExitReasonX64IoPortAccess)
|
---|
446 | {
|
---|
447 | if (ExitInfo.IoPortAccess.PortNumber == MY_NOP_PORT)
|
---|
448 | { /* likely: nop instruction */ }
|
---|
449 | else if (ExitInfo.IoPortAccess.PortNumber == MY_TERM_PORT)
|
---|
450 | break;
|
---|
451 | else
|
---|
452 | return runtimeError("Unexpected I/O port access (for %s): %#x\n", pszInstruction, ExitInfo.IoPortAccess.PortNumber);
|
---|
453 |
|
---|
454 | /* Advance. */
|
---|
455 | if (ExitInfo.VpContext.InstructionLength)
|
---|
456 | {
|
---|
457 | aenmNames[0] = WHvX64RegisterRip;
|
---|
458 | aValues[0].Reg64 = ExitInfo.VpContext.Rip + ExitInfo.VpContext.InstructionLength;
|
---|
459 | hrc = WHvSetVirtualProcessorRegisters(g_hPartition, 0 /*idCpu*/, aenmNames, 1, aValues);
|
---|
460 | if (SUCCEEDED(hrc))
|
---|
461 | { /* likely */ }
|
---|
462 | else
|
---|
463 | return runtimeError("Error advancing RIP (for %s): %#x\n", pszInstruction, hrc);
|
---|
464 | }
|
---|
465 | else
|
---|
466 | return runtimeError("VpContext.InstructionLength is zero (for %s)\n", pszInstruction);
|
---|
467 | }
|
---|
468 | else if (ExitInfo.ExitReason == WHvRunVpExitReasonX64Cpuid)
|
---|
469 | {
|
---|
470 | /* Advance RIP and set default results. */
|
---|
471 | if (ExitInfo.VpContext.InstructionLength)
|
---|
472 | {
|
---|
473 | aenmNames[0] = WHvX64RegisterRip;
|
---|
474 | aValues[0].Reg64 = ExitInfo.VpContext.Rip + ExitInfo.VpContext.InstructionLength;
|
---|
475 | aenmNames[1] = WHvX64RegisterRax;
|
---|
476 | aValues[1].Reg64 = ExitInfo.CpuidAccess.DefaultResultRax;
|
---|
477 | aenmNames[2] = WHvX64RegisterRcx;
|
---|
478 | aValues[2].Reg64 = ExitInfo.CpuidAccess.DefaultResultRcx;
|
---|
479 | aenmNames[3] = WHvX64RegisterRdx;
|
---|
480 | aValues[3].Reg64 = ExitInfo.CpuidAccess.DefaultResultRdx;
|
---|
481 | aenmNames[4] = WHvX64RegisterRbx;
|
---|
482 | aValues[4].Reg64 = ExitInfo.CpuidAccess.DefaultResultRbx;
|
---|
483 | hrc = WHvSetVirtualProcessorRegisters(g_hPartition, 0 /*idCpu*/, aenmNames, 5, aValues);
|
---|
484 | if (SUCCEEDED(hrc))
|
---|
485 | { /* likely */ }
|
---|
486 | else
|
---|
487 | return runtimeError("Error advancing RIP (for %s): %#x\n", pszInstruction, hrc);
|
---|
488 | }
|
---|
489 | else
|
---|
490 | return runtimeError("VpContext.InstructionLength is zero (for %s)\n", pszInstruction);
|
---|
491 | }
|
---|
492 | else if (ExitInfo.ExitReason == WHvRunVpExitReasonMemoryAccess)
|
---|
493 | {
|
---|
494 | if (ExitInfo.MemoryAccess.Gpa == MY_NOP_MMIO)
|
---|
495 | { /* likely: nop address */ }
|
---|
496 | else
|
---|
497 | return runtimeError("Unexpected memory access (for %s): %#x\n", pszInstruction, ExitInfo.MemoryAccess.Gpa);
|
---|
498 |
|
---|
499 | /* Advance and set return register (assuming RAX and two byte instruction). */
|
---|
500 | aenmNames[0] = WHvX64RegisterRip;
|
---|
501 | if (ExitInfo.VpContext.InstructionLength)
|
---|
502 | aValues[0].Reg64 = ExitInfo.VpContext.Rip + ExitInfo.VpContext.InstructionLength;
|
---|
503 | else
|
---|
504 | aValues[0].Reg64 = ExitInfo.VpContext.Rip + 2;
|
---|
505 | aenmNames[1] = WHvX64RegisterRax;
|
---|
506 | aValues[1].Reg64 = 42;
|
---|
507 | hrc = WHvSetVirtualProcessorRegisters(g_hPartition, 0 /*idCpu*/, aenmNames, 2, aValues);
|
---|
508 | if (SUCCEEDED(hrc))
|
---|
509 | { /* likely */ }
|
---|
510 | else
|
---|
511 | return runtimeError("Error advancing RIP (for %s): %#x\n", pszInstruction, hrc);
|
---|
512 | }
|
---|
513 | else
|
---|
514 | return runtimeError("Unexpected exit (for %s): %#x\n", pszInstruction, ExitInfo.ExitReason);
|
---|
515 | }
|
---|
516 | else
|
---|
517 | return runtimeError("WHvRunVirtualProcessor failed (for %s): %#x\n", pszInstruction, hrc);
|
---|
518 | }
|
---|
519 | uint64_t const nsElapsed = getNanoTS() - nsStart;
|
---|
520 | return reportResult(pszInstruction, cInstructions, nsElapsed, cExits);
|
---|
521 | }
|
---|
522 |
|
---|
523 |
|
---|
524 |
|
---|
525 | #elif defined(RT_OS_LINUX)
|
---|
526 |
|
---|
527 | /*
|
---|
528 | * GNU/linux - KVM
|
---|
529 | */
|
---|
530 |
|
---|
531 | static int createVM(void)
|
---|
532 | {
|
---|
533 | int fd = open("/dev/kvm", O_RDWR);
|
---|
534 | if (fd < 0)
|
---|
535 | return error("Error opening /dev/kvm: %d\n", errno);
|
---|
536 |
|
---|
537 | g_fdVm = ioctl(fd, KVM_CREATE_VM, (uintptr_t)0);
|
---|
538 | if (g_fdVm < 0)
|
---|
539 | return error("KVM_CREATE_VM failed: %d\n", errno);
|
---|
540 |
|
---|
541 | /* Create the VCpu. */
|
---|
542 | g_cbVCpuRun = ioctl(fd, KVM_GET_VCPU_MMAP_SIZE, (uintptr_t)0);
|
---|
543 | if (g_cbVCpuRun <= 0x1000 || (g_cbVCpuRun & 0xfff))
|
---|
544 | return error("Failed to get KVM_GET_VCPU_MMAP_SIZE: %#xz errno=%d\n", g_cbVCpuRun, errno);
|
---|
545 |
|
---|
546 | g_fdVCpu = ioctl(g_fdVm, KVM_CREATE_VCPU, (uintptr_t)0);
|
---|
547 | if (g_fdVCpu < 0)
|
---|
548 | return error("KVM_CREATE_VCPU failed: %d\n", errno);
|
---|
549 |
|
---|
550 | g_pVCpuRun = (struct kvm_run *)mmap(NULL, g_cbVCpuRun, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fdVCpu, 0);
|
---|
551 | if ((void *)g_pVCpuRun == MAP_FAILED)
|
---|
552 | return error("mmap kvm_run failed: %d\n", errno);
|
---|
553 |
|
---|
554 | /* Memory. */
|
---|
555 | g_pbMem = (unsigned char *)mmap(NULL, g_cbMem, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
---|
556 | if ((void *)g_pbMem == MAP_FAILED)
|
---|
557 | return error("mmap RAM failed: %d\n", errno);
|
---|
558 |
|
---|
559 | struct kvm_userspace_memory_region MemReg;
|
---|
560 | MemReg.slot = 0;
|
---|
561 | MemReg.flags = 0;
|
---|
562 | MemReg.guest_phys_addr = MY_MEM_BASE;
|
---|
563 | MemReg.memory_size = g_cbMem;
|
---|
564 | MemReg.userspace_addr = (uintptr_t)g_pbMem;
|
---|
565 | int rc = ioctl(g_fdVm, KVM_SET_USER_MEMORY_REGION, &MemReg);
|
---|
566 | if (rc != 0)
|
---|
567 | return error("KVM_SET_USER_MEMORY_REGION failed: %d (%d)\n", errno, rc);
|
---|
568 |
|
---|
569 | close(fd);
|
---|
570 | return 0;
|
---|
571 | }
|
---|
572 |
|
---|
573 |
|
---|
574 | static void printSReg(const char *pszName, struct kvm_segment const *pSReg)
|
---|
575 | {
|
---|
576 | RTStrmPrintf(g_pStdErr, " %5s=%04x base=%016llx limit=%08x type=%#x p=%d dpl=%d db=%d s=%d l=%d g=%d avl=%d un=%d\n",
|
---|
577 | pszName, pSReg->selector, pSReg->base, pSReg->limit, pSReg->type, pSReg->present, pSReg->dpl,
|
---|
578 | pSReg->db, pSReg->s, pSReg->l, pSReg->g, pSReg->avl, pSReg->unusable);
|
---|
579 | }
|
---|
580 |
|
---|
581 |
|
---|
582 | static int runtimeError(const char *pszFormat, ...)
|
---|
583 | {
|
---|
584 | RTStrmPrintf(g_pStdErr, "runtime error: ");
|
---|
585 | va_list va;
|
---|
586 | va_start(va, pszFormat);
|
---|
587 | RTStrmPrintfV(g_pStdErr, pszFormat, va);
|
---|
588 | va_end(va);
|
---|
589 |
|
---|
590 | RTStrmPrintf(g_pStdErr, " exit_reason=%#010x\n", g_pVCpuRun->exit_reason);
|
---|
591 | RTStrmPrintf(g_pStdErr, "ready_for_interrupt_injection=%#x\n", g_pVCpuRun->ready_for_interrupt_injection);
|
---|
592 | RTStrmPrintf(g_pStdErr, " if_flag=%#x\n", g_pVCpuRun->if_flag);
|
---|
593 | RTStrmPrintf(g_pStdErr, " flags=%#x\n", g_pVCpuRun->flags);
|
---|
594 | RTStrmPrintf(g_pStdErr, " kvm_valid_regs=%#018llx\n", g_pVCpuRun->kvm_valid_regs);
|
---|
595 | RTStrmPrintf(g_pStdErr, " kvm_dirty_regs=%#018llx\n", g_pVCpuRun->kvm_dirty_regs);
|
---|
596 |
|
---|
597 | struct kvm_regs Regs;
|
---|
598 | memset(&Regs, 0, sizeof(Regs));
|
---|
599 | struct kvm_sregs SRegs;
|
---|
600 | memset(&SRegs, 0, sizeof(SRegs));
|
---|
601 | if ( ioctl(g_fdVCpu, KVM_GET_REGS, &Regs) != -1
|
---|
602 | && ioctl(g_fdVCpu, KVM_GET_SREGS, &SRegs) != -1)
|
---|
603 | {
|
---|
604 | RTStrmPrintf(g_pStdErr, " rip=%016llx\n", Regs.rip);
|
---|
605 | printSReg("cs", &SRegs.cs);
|
---|
606 | RTStrmPrintf(g_pStdErr, " rflags=%08llx\n", Regs.rflags);
|
---|
607 | RTStrmPrintf(g_pStdErr, " rax=%016llx\n", Regs.rax);
|
---|
608 | RTStrmPrintf(g_pStdErr, " rbx=%016llx\n", Regs.rcx);
|
---|
609 | RTStrmPrintf(g_pStdErr, " rdx=%016llx\n", Regs.rdx);
|
---|
610 | RTStrmPrintf(g_pStdErr, " rcx=%016llx\n", Regs.rbx);
|
---|
611 | RTStrmPrintf(g_pStdErr, " rsp=%016llx\n", Regs.rsp);
|
---|
612 | RTStrmPrintf(g_pStdErr, " rbp=%016llx\n", Regs.rbp);
|
---|
613 | RTStrmPrintf(g_pStdErr, " rsi=%016llx\n", Regs.rsi);
|
---|
614 | RTStrmPrintf(g_pStdErr, " rdi=%016llx\n", Regs.rdi);
|
---|
615 | printSReg("ss", &SRegs.ss);
|
---|
616 | printSReg("ds", &SRegs.ds);
|
---|
617 | printSReg("es", &SRegs.es);
|
---|
618 | printSReg("fs", &SRegs.fs);
|
---|
619 | printSReg("gs", &SRegs.gs);
|
---|
620 | printSReg("tr", &SRegs.tr);
|
---|
621 | printSReg("ldtr", &SRegs.ldt);
|
---|
622 |
|
---|
623 | uint64_t const offMem = Regs.rip + SRegs.cs.base - MY_MEM_BASE;
|
---|
624 | if (offMem < g_cbMem - 10)
|
---|
625 | RTStrmPrintf(g_pStdErr, " bytes at PC (%#zx): %02x %02x %02x %02x %02x %02x %02x %02x\n", (size_t)(offMem + MY_MEM_BASE),
|
---|
626 | g_pbMem[offMem ], g_pbMem[offMem + 1], g_pbMem[offMem + 2], g_pbMem[offMem + 3],
|
---|
627 | g_pbMem[offMem + 4], g_pbMem[offMem + 5], g_pbMem[offMem + 6], g_pbMem[offMem + 7]);
|
---|
628 | }
|
---|
629 |
|
---|
630 | return 1;
|
---|
631 | }
|
---|
632 |
|
---|
633 | static int runRealModeTest(unsigned cInstructions, const char *pszInstruction, unsigned fTest,
|
---|
634 | unsigned uEax, unsigned uEcx, unsigned uEdx, unsigned uEbx,
|
---|
635 | unsigned uEsp, unsigned uEbp, unsigned uEsi, unsigned uEdi)
|
---|
636 | {
|
---|
637 | (void)fTest;
|
---|
638 |
|
---|
639 | /*
|
---|
640 | * Setup real mode context.
|
---|
641 | */
|
---|
642 | #define SET_SEG(a_SReg, a_Base, a_Limit, a_Sel, a_fCode) \
|
---|
643 | do { \
|
---|
644 | a_SReg.base = (a_Base); \
|
---|
645 | a_SReg.limit = (a_Limit); \
|
---|
646 | a_SReg.selector = (a_Sel); \
|
---|
647 | a_SReg.type = (a_fCode) ? 10 : 3; \
|
---|
648 | a_SReg.present = 1; \
|
---|
649 | a_SReg.dpl = 0; \
|
---|
650 | a_SReg.db = 0; \
|
---|
651 | a_SReg.s = 1; \
|
---|
652 | a_SReg.l = 0; \
|
---|
653 | a_SReg.g = 0; \
|
---|
654 | a_SReg.avl = 0; \
|
---|
655 | a_SReg.unusable = 0; \
|
---|
656 | a_SReg.padding = 0; \
|
---|
657 | } while (0)
|
---|
658 | struct kvm_regs Regs;
|
---|
659 | memset(&Regs, 0, sizeof(Regs));
|
---|
660 | Regs.rax = uEax;
|
---|
661 | Regs.rcx = uEcx;
|
---|
662 | Regs.rdx = uEdx;
|
---|
663 | Regs.rbx = uEbx;
|
---|
664 | Regs.rsp = uEsp;
|
---|
665 | Regs.rbp = uEbp;
|
---|
666 | Regs.rsi = uEsi;
|
---|
667 | Regs.rdi = uEdi;
|
---|
668 | Regs.rip = MY_TEST_RIP;
|
---|
669 | Regs.rflags = 2;
|
---|
670 | int rc = ioctl(g_fdVCpu, KVM_SET_REGS, &Regs);
|
---|
671 | if (rc != 0)
|
---|
672 | return error("KVM_SET_REGS failed: %d (rc=%d)\n", errno, rc);
|
---|
673 |
|
---|
674 | struct kvm_sregs SRegs;
|
---|
675 | memset(&SRegs, 0, sizeof(SRegs));
|
---|
676 | rc = ioctl(g_fdVCpu, KVM_GET_SREGS, &SRegs);
|
---|
677 | if (rc != 0)
|
---|
678 | return error("KVM_GET_SREGS failed: %d (rc=%d)\n", errno, rc);
|
---|
679 | SET_SEG(SRegs.es, 0x00000, 0xffff, 0x0000, 0);
|
---|
680 | SET_SEG(SRegs.cs, 0x00000, 0xffff, 0x0000, 1);
|
---|
681 | SET_SEG(SRegs.ss, 0x00000, 0xffff, 0x0000, 0);
|
---|
682 | SET_SEG(SRegs.ds, 0x00000, 0xffff, 0x0000, 0);
|
---|
683 | SET_SEG(SRegs.fs, 0x00000, 0xffff, 0x0000, 0);
|
---|
684 | SET_SEG(SRegs.gs, 0x00000, 0xffff, 0x0000, 0);
|
---|
685 | //SRegs.cr0 = 0x10010 /*WP+ET*/;
|
---|
686 | SRegs.cr2 = 0;
|
---|
687 | //SRegs.cr3 = 0;
|
---|
688 | //SRegs.cr4 = 0;
|
---|
689 | rc = ioctl(g_fdVCpu, KVM_SET_SREGS, &SRegs);
|
---|
690 | if (rc != 0)
|
---|
691 | return error("KVM_SET_SREGS failed: %d (rc=%d)\n", errno, rc);
|
---|
692 |
|
---|
693 | /*
|
---|
694 | * Run the test.
|
---|
695 | */
|
---|
696 | uint32_t cExits = 0;
|
---|
697 | uint64_t const nsStart = getNanoTS();
|
---|
698 | for (;;)
|
---|
699 | {
|
---|
700 | rc = ioctl(g_fdVCpu, KVM_RUN, (uintptr_t)0);
|
---|
701 | if (rc == 0)
|
---|
702 | {
|
---|
703 | cExits++;
|
---|
704 | if (g_pVCpuRun->exit_reason == KVM_EXIT_IO)
|
---|
705 | {
|
---|
706 | if (g_pVCpuRun->io.port == MY_NOP_PORT)
|
---|
707 | { /* likely: nop instruction */ }
|
---|
708 | else if (g_pVCpuRun->io.port == MY_TERM_PORT)
|
---|
709 | break;
|
---|
710 | else
|
---|
711 | return runtimeError("Unexpected I/O port access (for %s): %#x\n", pszInstruction, g_pVCpuRun->io.port);
|
---|
712 | }
|
---|
713 | else if (g_pVCpuRun->exit_reason == KVM_EXIT_MMIO)
|
---|
714 | {
|
---|
715 | if (g_pVCpuRun->mmio.phys_addr == MY_NOP_MMIO)
|
---|
716 | { /* likely: nop address */ }
|
---|
717 | else
|
---|
718 | return runtimeError("Unexpected memory access (for %s): %#llx\n", pszInstruction, g_pVCpuRun->mmio.phys_addr);
|
---|
719 | }
|
---|
720 | else
|
---|
721 | return runtimeError("Unexpected exit (for %s): %d\n", pszInstruction, g_pVCpuRun->exit_reason);
|
---|
722 | }
|
---|
723 | else
|
---|
724 | return runtimeError("KVM_RUN failed (for %s): %#x (ret %d)\n", pszInstruction, errno, rc);
|
---|
725 | }
|
---|
726 | uint64_t const nsElapsed = getNanoTS() - nsStart;
|
---|
727 | return reportResult(pszInstruction, cInstructions, nsElapsed, cExits);
|
---|
728 | }
|
---|
729 |
|
---|
730 |
|
---|
731 | #elif defined(RT_OS_DARWIN)
|
---|
732 |
|
---|
733 | /*
|
---|
734 | * Mac OS X - Hypervisor API.
|
---|
735 | */
|
---|
736 |
|
---|
737 | static int createVM(void)
|
---|
738 | {
|
---|
739 | /* VM and VCpu */
|
---|
740 | hv_return_t rcHv = hv_vm_create(HV_VM_DEFAULT);
|
---|
741 | if (rcHv != HV_SUCCESS)
|
---|
742 | return error("hv_vm_create failed: %#x\n", rcHv);
|
---|
743 |
|
---|
744 | g_idVCpu = -1;
|
---|
745 | rcHv = hv_vcpu_create(&g_idVCpu, HV_VCPU_DEFAULT);
|
---|
746 | if (rcHv != HV_SUCCESS)
|
---|
747 | return error("hv_vcpu_create failed: %#x\n", rcHv);
|
---|
748 |
|
---|
749 | /* Memory. */
|
---|
750 | g_pbMem = (unsigned char *)mmap(NULL, g_cbMem, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
|
---|
751 | if ((void *)g_pbMem == MAP_FAILED)
|
---|
752 | return error("mmap RAM failed: %d\n", errno);
|
---|
753 | memset(g_pbMem, 0xf4, g_cbMem);
|
---|
754 |
|
---|
755 | rcHv = hv_vm_map(g_pbMem, MY_MEM_BASE, g_cbMem, HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
|
---|
756 | if (rcHv != HV_SUCCESS)
|
---|
757 | return error("hv_vm_map failed: %#x\n", rcHv);
|
---|
758 |
|
---|
759 | rcHv = hv_vm_protect(0x2000, 0x1000, HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
|
---|
760 | if (rcHv != HV_SUCCESS)
|
---|
761 | return error("hv_vm_protect failed: %#x\n", rcHv);
|
---|
762 | return 0;
|
---|
763 | }
|
---|
764 |
|
---|
765 |
|
---|
766 | static int runtimeError(const char *pszFormat, ...)
|
---|
767 | {
|
---|
768 | RTStrmPrintf(g_pStdErr, "runtime error: ");
|
---|
769 | va_list va;
|
---|
770 | va_start(va, pszFormat);
|
---|
771 | RTStrmPrintfV(g_pStdErr, pszFormat, va);
|
---|
772 | va_end(va);
|
---|
773 |
|
---|
774 | static struct { const char *pszName; uint32_t uField; uint32_t uFmt : 31; uint32_t fIsReg : 1; } const s_aFields[] =
|
---|
775 | {
|
---|
776 | { "VMCS_RO_EXIT_REASON", VMCS_RO_EXIT_REASON, 64, 0 },
|
---|
777 | { "VMCS_RO_EXIT_QUALIFIC", VMCS_RO_EXIT_QUALIFIC, 64, 0 },
|
---|
778 | { "VMCS_RO_INSTR_ERROR", VMCS_RO_INSTR_ERROR, 64, 0 },
|
---|
779 | { "VMCS_RO_VMEXIT_IRQ_INFO", VMCS_RO_VMEXIT_IRQ_INFO, 64, 0 },
|
---|
780 | { "VMCS_RO_VMEXIT_IRQ_ERROR", VMCS_RO_VMEXIT_IRQ_ERROR, 64, 0 },
|
---|
781 | { "VMCS_RO_VMEXIT_INSTR_LEN", VMCS_RO_VMEXIT_INSTR_LEN, 64, 0 },
|
---|
782 | { "VMCS_RO_VMX_INSTR_INFO", VMCS_RO_VMX_INSTR_INFO, 64, 0 },
|
---|
783 | { "VMCS_RO_GUEST_LIN_ADDR", VMCS_RO_GUEST_LIN_ADDR, 64, 0 },
|
---|
784 | { "VMCS_GUEST_PHYSICAL_ADDRESS",VMCS_GUEST_PHYSICAL_ADDRESS,64, 0 },
|
---|
785 | { "VMCS_RO_IO_RCX", VMCS_RO_IO_RCX, 64, 0 },
|
---|
786 | { "VMCS_RO_IO_RSI", VMCS_RO_IO_RSI, 64, 0 },
|
---|
787 | { "VMCS_RO_IO_RDI", VMCS_RO_IO_RDI, 64, 0 },
|
---|
788 | { "VMCS_RO_IO_RIP", VMCS_RO_IO_RIP, 64, 0 },
|
---|
789 | { "rip", HV_X86_RIP, 64, 1 },
|
---|
790 | { "rip (vmcs)", VMCS_GUEST_RIP, 64, 0 },
|
---|
791 | { "cs", HV_X86_CS, 16, 1 },
|
---|
792 | { "cs (vmcs)", VMCS_GUEST_CS, 16, 0 },
|
---|
793 | { "cs.base", VMCS_GUEST_CS_BASE, 64, 0 },
|
---|
794 | { "cs.limit", VMCS_GUEST_CS_LIMIT, 32, 0 },
|
---|
795 | { "cs.attr", VMCS_GUEST_CS_AR, 32, 0 },
|
---|
796 | { "rflags", HV_X86_RFLAGS, 32, 1 },
|
---|
797 | { "rax", HV_X86_RAX, 64, 1 },
|
---|
798 | { "rcx", HV_X86_RCX, 64, 1 },
|
---|
799 | { "rdx", HV_X86_RDX, 64, 1 },
|
---|
800 | { "rbx", HV_X86_RBX, 64, 1 },
|
---|
801 | { "rsp", HV_X86_RSP, 64, 1 },
|
---|
802 | { "rsp (vmcs)", VMCS_GUEST_RSP, 64, 0 },
|
---|
803 | { "ss", HV_X86_SS, 16, 1 },
|
---|
804 | { "ss (vmcs)", VMCS_GUEST_SS, 16, 0 },
|
---|
805 | { "ss.base", VMCS_GUEST_SS_BASE, 64, 0 },
|
---|
806 | { "ss.limit", VMCS_GUEST_SS_LIMIT, 32, 0 },
|
---|
807 | { "ss.attr", VMCS_GUEST_SS_AR, 32, 0 },
|
---|
808 | { "rbp", HV_X86_RBP, 64, 1 },
|
---|
809 | { "rsi", HV_X86_RSI, 64, 1 },
|
---|
810 | { "rdi", HV_X86_RDI, 64, 1 },
|
---|
811 | { "ds", HV_X86_DS, 16, 1 },
|
---|
812 | { "ds (vmcs)", VMCS_GUEST_DS, 16, 0 },
|
---|
813 | { "ds.base", VMCS_GUEST_DS_BASE, 64, 0 },
|
---|
814 | { "ds.limit", VMCS_GUEST_DS_LIMIT, 32, 0 },
|
---|
815 | { "ds.attr", VMCS_GUEST_DS_AR, 32, 0 },
|
---|
816 | { "es", HV_X86_ES, 16, 1 },
|
---|
817 | { "es (vmcs)", VMCS_GUEST_ES, 16, 0 },
|
---|
818 | { "es.base", VMCS_GUEST_ES_BASE, 64, 0 },
|
---|
819 | { "es.limit", VMCS_GUEST_ES_LIMIT, 32, 0 },
|
---|
820 | { "es.attr", VMCS_GUEST_ES_AR, 32, 0 },
|
---|
821 | { "fs", HV_X86_FS, 16, 1 },
|
---|
822 | { "fs (vmcs)", VMCS_GUEST_FS, 16, 0 },
|
---|
823 | { "fs.base", VMCS_GUEST_FS_BASE, 64, 0 },
|
---|
824 | { "fs.limit", VMCS_GUEST_FS_LIMIT, 32, 0 },
|
---|
825 | { "fs.attr", VMCS_GUEST_FS_AR, 32, 0 },
|
---|
826 | { "gs", HV_X86_GS, 16, 1 },
|
---|
827 | { "gs (vmcs)", VMCS_GUEST_GS, 16, 0 },
|
---|
828 | { "gs.base", VMCS_GUEST_GS_BASE, 64, 0 },
|
---|
829 | { "gs.limit", VMCS_GUEST_GS_LIMIT, 32, 0 },
|
---|
830 | { "gs.attr", VMCS_GUEST_GS_AR, 32, 0 },
|
---|
831 | { "cr0", HV_X86_CR0, 64, 1 },
|
---|
832 | { "cr0 (vmcs)", VMCS_GUEST_CR0, 64, 0 },
|
---|
833 | { "cr2", HV_X86_CR2, 64, 1 },
|
---|
834 | { "cr3", HV_X86_CR3, 64, 1 },
|
---|
835 | { "cr3 (vmcs)", VMCS_GUEST_CR3, 64, 0 },
|
---|
836 | { "cr4", HV_X86_CR4, 64, 1 },
|
---|
837 | { "cr4 (vmcs)", VMCS_GUEST_CR4, 64, 0 },
|
---|
838 | { "idtr.base", VMCS_GUEST_IDTR_BASE, 64, 0 },
|
---|
839 | { "idtr.limit", VMCS_GUEST_IDTR_LIMIT, 32, 0 },
|
---|
840 | { "gdtr.base", VMCS_GUEST_GDTR_BASE, 64, 0 },
|
---|
841 | { "gdtr.limit", VMCS_GUEST_GDTR_LIMIT, 32, 0 },
|
---|
842 |
|
---|
843 | { "VMCS_CTRL_PIN_BASED", VMCS_CTRL_PIN_BASED, 64, 0 },
|
---|
844 | { "VMCS_CTRL_CPU_BASED", VMCS_CTRL_CPU_BASED, 64, 0 },
|
---|
845 | { "VMCS_CTRL_CPU_BASED2", VMCS_CTRL_CPU_BASED2, 64, 0 },
|
---|
846 | { "VMCS_CTRL_VMENTRY_CONTROLS", VMCS_CTRL_VMENTRY_CONTROLS, 64, 0 },
|
---|
847 | { "VMCS_CTRL_VMEXIT_CONTROLS", VMCS_CTRL_VMEXIT_CONTROLS, 64, 0 },
|
---|
848 | { "VMCS_CTRL_EXC_BITMAP", VMCS_CTRL_EXC_BITMAP, 64, 0 },
|
---|
849 | { "VMCS_CTRL_CR0_MASK", VMCS_CTRL_CR0_MASK, 64, 0 },
|
---|
850 | { "VMCS_CTRL_CR0_SHADOW", VMCS_CTRL_CR0_SHADOW, 64, 0 },
|
---|
851 | { "VMCS_CTRL_CR4_MASK", VMCS_CTRL_CR4_MASK, 64, 0 },
|
---|
852 | { "VMCS_CTRL_CR4_SHADOW", VMCS_CTRL_CR4_SHADOW, 64, 0 },
|
---|
853 | };
|
---|
854 | for (unsigned i = 0; i < sizeof(s_aFields) / sizeof(s_aFields[0]); i++)
|
---|
855 | {
|
---|
856 | uint64_t uValue = UINT64_MAX;
|
---|
857 | hv_return_t rcHv;
|
---|
858 | if (s_aFields[i].fIsReg)
|
---|
859 | rcHv = hv_vcpu_read_register(g_idVCpu, (hv_x86_reg_t)s_aFields[i].uField, &uValue);
|
---|
860 | else
|
---|
861 | rcHv = hv_vmx_vcpu_read_vmcs(g_idVCpu, s_aFields[i].uField, &uValue);
|
---|
862 | if (rcHv == HV_SUCCESS)
|
---|
863 | {
|
---|
864 | if (s_aFields[i].uFmt == 16)
|
---|
865 | RTStrmPrintf(g_pStdErr, "%28s=%04llx\n", s_aFields[i].pszName, uValue);
|
---|
866 | else if (s_aFields[i].uFmt == 32)
|
---|
867 | RTStrmPrintf(g_pStdErr, "%28s=%08llx\n", s_aFields[i].pszName, uValue);
|
---|
868 | else
|
---|
869 | RTStrmPrintf(g_pStdErr, "%28s=%08x'%08x\n", s_aFields[i].pszName, (uint32_t)(uValue >> 32), (uint32_t)uValue);
|
---|
870 | }
|
---|
871 | else
|
---|
872 | RTStrmPrintf(g_pStdErr, "%28s=<%s failed %#x>\n", s_aFields[i].pszName,
|
---|
873 | s_aFields[i].fIsReg ? "hv_vcpu_read_register" : "hv_vmx_vcpu_read_vmcs", rcHv);
|
---|
874 | }
|
---|
875 | return 1;
|
---|
876 | }
|
---|
877 |
|
---|
878 |
|
---|
879 | static int runRealModeTest(unsigned cInstructions, const char *pszInstruction, unsigned fTest,
|
---|
880 | unsigned uEax, unsigned uEcx, unsigned uEdx, unsigned uEbx,
|
---|
881 | unsigned uEsp, unsigned uEbp, unsigned uEsi, unsigned uEdi)
|
---|
882 | {
|
---|
883 | /*
|
---|
884 | * Setup real mode context.
|
---|
885 | */
|
---|
886 | #define WRITE_REG_RET(a_enmReg, a_uValue) \
|
---|
887 | do { \
|
---|
888 | hv_return_t rcHvX = hv_vcpu_write_register(g_idVCpu, a_enmReg, a_uValue); \
|
---|
889 | if (rcHvX == HV_SUCCESS) { /* likely */ } \
|
---|
890 | else return error("hv_vcpu_write_register(%#x, %s, %#llx) -> %#x\n", g_idVCpu, #a_enmReg, (uint64_t)(a_uValue), rcHvX); \
|
---|
891 | } while (0)
|
---|
892 | #define READ_REG_RET(a_enmReg, a_puValue) \
|
---|
893 | do { \
|
---|
894 | hv_return_t rcHvX = hv_vcpu_read_register(g_idVCpu, a_enmReg, a_puValue); \
|
---|
895 | if (rcHvX == HV_SUCCESS) { /* likely */ } \
|
---|
896 | else return error("hv_vcpu_read_register(%#x, %s,) -> %#x\n", g_idVCpu, #a_enmReg, rcHvX); \
|
---|
897 | } while (0)
|
---|
898 | #define WRITE_VMCS_RET(a_enmField, a_uValue) \
|
---|
899 | do { \
|
---|
900 | hv_return_t rcHvX = hv_vmx_vcpu_write_vmcs(g_idVCpu, a_enmField, a_uValue); \
|
---|
901 | if (rcHvX == HV_SUCCESS) { /* likely */ } \
|
---|
902 | else return error("hv_vmx_vcpu_write_vmcs(%#x, %s, %#llx) -> %#x\n", g_idVCpu, #a_enmField, (uint64_t)(a_uValue), rcHvX); \
|
---|
903 | } while (0)
|
---|
904 | #define READ_VMCS_RET(a_enmField, a_puValue) \
|
---|
905 | do { \
|
---|
906 | hv_return_t rcHvX = hv_vmx_vcpu_read_vmcs(g_idVCpu, a_enmField, a_puValue); \
|
---|
907 | if (rcHvX == HV_SUCCESS) { /* likely */ } \
|
---|
908 | else return error("hv_vmx_vcpu_read_vmcs(%#x, %s,) -> %#x\n", g_idVCpu, #a_enmField, rcHvX); \
|
---|
909 | } while (0)
|
---|
910 | #define READ_CAP_RET(a_enmCap, a_puValue) \
|
---|
911 | do { \
|
---|
912 | hv_return_t rcHvX = hv_vmx_read_capability(a_enmCap, a_puValue); \
|
---|
913 | if (rcHvX == HV_SUCCESS) { /* likely */ } \
|
---|
914 | else return error("hv_vmx_read_capability(%s) -> %#x\n", #a_enmCap); \
|
---|
915 | } while (0)
|
---|
916 | #define CAP_2_CTRL(a_uCap, a_fWanted) ( ((a_fWanted) | (uint32_t)(a_uCap)) & (uint32_t)((a_uCap) >> 32) )
|
---|
917 | #if 1
|
---|
918 | uint64_t uCap;
|
---|
919 | READ_CAP_RET(HV_VMX_CAP_PINBASED, &uCap);
|
---|
920 | WRITE_VMCS_RET(VMCS_CTRL_PIN_BASED, CAP_2_CTRL(uCap, PIN_BASED_INTR | PIN_BASED_NMI | PIN_BASED_VIRTUAL_NMI));
|
---|
921 | READ_CAP_RET(HV_VMX_CAP_PROCBASED, &uCap);
|
---|
922 | WRITE_VMCS_RET(VMCS_CTRL_CPU_BASED, CAP_2_CTRL(uCap, CPU_BASED_HLT
|
---|
923 | | CPU_BASED_INVLPG
|
---|
924 | | CPU_BASED_MWAIT
|
---|
925 | | CPU_BASED_RDPMC
|
---|
926 | | CPU_BASED_RDTSC
|
---|
927 | | CPU_BASED_CR3_LOAD
|
---|
928 | | CPU_BASED_CR3_STORE
|
---|
929 | | CPU_BASED_CR8_LOAD
|
---|
930 | | CPU_BASED_CR8_STORE
|
---|
931 | | CPU_BASED_MOV_DR
|
---|
932 | | CPU_BASED_UNCOND_IO
|
---|
933 | | CPU_BASED_MONITOR
|
---|
934 | | CPU_BASED_PAUSE
|
---|
935 | ));
|
---|
936 | READ_CAP_RET(HV_VMX_CAP_PROCBASED2, &uCap);
|
---|
937 | WRITE_VMCS_RET(VMCS_CTRL_CPU_BASED2, CAP_2_CTRL(uCap, 0));
|
---|
938 | READ_CAP_RET(HV_VMX_CAP_ENTRY, &uCap);
|
---|
939 | WRITE_VMCS_RET(VMCS_CTRL_VMENTRY_CONTROLS, CAP_2_CTRL(uCap, 0));
|
---|
940 | #endif
|
---|
941 | WRITE_VMCS_RET(VMCS_CTRL_EXC_BITMAP, UINT32_MAX);
|
---|
942 | WRITE_VMCS_RET(VMCS_CTRL_CR0_MASK, 0x60000000);
|
---|
943 | WRITE_VMCS_RET(VMCS_CTRL_CR0_SHADOW, 0x00000000);
|
---|
944 | WRITE_VMCS_RET(VMCS_CTRL_CR4_MASK, 0x00000000);
|
---|
945 | WRITE_VMCS_RET(VMCS_CTRL_CR4_SHADOW, 0x00000000);
|
---|
946 |
|
---|
947 | WRITE_REG_RET(HV_X86_RAX, uEax);
|
---|
948 | WRITE_REG_RET(HV_X86_RCX, uEcx);
|
---|
949 | WRITE_REG_RET(HV_X86_RDX, uEdx);
|
---|
950 | WRITE_REG_RET(HV_X86_RBX, uEbx);
|
---|
951 | WRITE_REG_RET(HV_X86_RSP, uEsp);
|
---|
952 | WRITE_REG_RET(HV_X86_RBP, uEbp);
|
---|
953 | WRITE_REG_RET(HV_X86_RSI, uEsi);
|
---|
954 | WRITE_REG_RET(HV_X86_RDI, uEdi);
|
---|
955 | WRITE_REG_RET(HV_X86_RIP, MY_TEST_RIP);
|
---|
956 | WRITE_REG_RET(HV_X86_RFLAGS, 2);
|
---|
957 | WRITE_REG_RET(HV_X86_ES, 0x0000);
|
---|
958 | WRITE_VMCS_RET(VMCS_GUEST_ES_BASE, 0x0000000);
|
---|
959 | WRITE_VMCS_RET(VMCS_GUEST_ES_LIMIT, 0xffff);
|
---|
960 | WRITE_VMCS_RET(VMCS_GUEST_ES_AR, 0x93);
|
---|
961 | WRITE_REG_RET(HV_X86_CS, 0x0000);
|
---|
962 | WRITE_VMCS_RET(VMCS_GUEST_CS_BASE, 0x0000000);
|
---|
963 | WRITE_VMCS_RET(VMCS_GUEST_CS_LIMIT, 0xffff);
|
---|
964 | WRITE_VMCS_RET(VMCS_GUEST_CS_AR, 0x9b);
|
---|
965 | WRITE_REG_RET(HV_X86_SS, 0x0000);
|
---|
966 | WRITE_VMCS_RET(VMCS_GUEST_SS_BASE, 0x0000000);
|
---|
967 | WRITE_VMCS_RET(VMCS_GUEST_SS_LIMIT, 0xffff);
|
---|
968 | WRITE_VMCS_RET(VMCS_GUEST_SS_AR, 0x93);
|
---|
969 | WRITE_REG_RET(HV_X86_DS, 0x0000);
|
---|
970 | WRITE_VMCS_RET(VMCS_GUEST_DS_BASE, 0x0000000);
|
---|
971 | WRITE_VMCS_RET(VMCS_GUEST_DS_LIMIT, 0xffff);
|
---|
972 | WRITE_VMCS_RET(VMCS_GUEST_DS_AR, 0x93);
|
---|
973 | WRITE_REG_RET(HV_X86_FS, 0x0000);
|
---|
974 | WRITE_VMCS_RET(VMCS_GUEST_FS_BASE, 0x0000000);
|
---|
975 | WRITE_VMCS_RET(VMCS_GUEST_FS_LIMIT, 0xffff);
|
---|
976 | WRITE_VMCS_RET(VMCS_GUEST_FS_AR, 0x93);
|
---|
977 | WRITE_REG_RET(HV_X86_GS, 0x0000);
|
---|
978 | WRITE_VMCS_RET(VMCS_GUEST_GS_BASE, 0x0000000);
|
---|
979 | WRITE_VMCS_RET(VMCS_GUEST_GS_LIMIT, 0xffff);
|
---|
980 | WRITE_VMCS_RET(VMCS_GUEST_GS_AR, 0x93);
|
---|
981 | //WRITE_REG_RET(HV_X86_CR0, 0x10030 /*WP+NE+ET*/);
|
---|
982 | WRITE_VMCS_RET(VMCS_GUEST_CR0, 0x10030 /*WP+NE+ET*/);
|
---|
983 | //WRITE_REG_RET(HV_X86_CR2, 0);
|
---|
984 | //WRITE_REG_RET(HV_X86_CR3, 0);
|
---|
985 | WRITE_VMCS_RET(VMCS_GUEST_CR3, 0);
|
---|
986 | //WRITE_REG_RET(HV_X86_CR4, 0x2000);
|
---|
987 | WRITE_VMCS_RET(VMCS_GUEST_CR4, 0x2000);
|
---|
988 | WRITE_VMCS_RET(VMCS_GUEST_LDTR, 0x0000);
|
---|
989 | WRITE_VMCS_RET(VMCS_GUEST_LDTR_BASE, 0x00000000);
|
---|
990 | WRITE_VMCS_RET(VMCS_GUEST_LDTR_LIMIT, 0x0000);
|
---|
991 | WRITE_VMCS_RET(VMCS_GUEST_LDTR_AR, 0x10000);
|
---|
992 | WRITE_VMCS_RET(VMCS_GUEST_TR, 0x0000);
|
---|
993 | WRITE_VMCS_RET(VMCS_GUEST_TR_BASE, 0x00000000);
|
---|
994 | WRITE_VMCS_RET(VMCS_GUEST_TR_LIMIT, 0x0000);
|
---|
995 | WRITE_VMCS_RET(VMCS_GUEST_TR_AR, 0x00083);
|
---|
996 | hv_vcpu_flush(g_idVCpu);
|
---|
997 | hv_vcpu_invalidate_tlb(g_idVCpu);
|
---|
998 |
|
---|
999 | /*
|
---|
1000 | * Run the test.
|
---|
1001 | */
|
---|
1002 | uint32_t cExits = 0;
|
---|
1003 | uint64_t const nsStart = getNanoTS();
|
---|
1004 | for (;;)
|
---|
1005 | {
|
---|
1006 | hv_return_t rcHv = hv_vcpu_run(g_idVCpu);
|
---|
1007 | if (rcHv == HV_SUCCESS)
|
---|
1008 | {
|
---|
1009 | cExits++;
|
---|
1010 | uint64_t uExitReason = UINT64_MAX;
|
---|
1011 | READ_VMCS_RET(VMCS_RO_EXIT_REASON, &uExitReason);
|
---|
1012 | if (!(uExitReason & UINT64_C(0x80000000)))
|
---|
1013 | {
|
---|
1014 | if (uExitReason == VMX_REASON_IO)
|
---|
1015 | {
|
---|
1016 | uint64_t uIoQual = UINT64_MAX;
|
---|
1017 | READ_VMCS_RET(VMCS_RO_EXIT_QUALIFIC, &uIoQual);
|
---|
1018 | if ((uint16_t)(uIoQual >> 16) == MY_NOP_PORT && (fTest & MY_TEST_F_NOP_IO))
|
---|
1019 | { /* likely: nop instruction */ }
|
---|
1020 | else if ((uint16_t)(uIoQual >> 16) == MY_TERM_PORT)
|
---|
1021 | break;
|
---|
1022 | else
|
---|
1023 | return runtimeError("Unexpected I/O port access (for %s): %#x\n", pszInstruction, (uint16_t)(uIoQual >> 16));
|
---|
1024 |
|
---|
1025 | /* Advance RIP. */
|
---|
1026 | uint64_t cbInstr = UINT64_MAX;
|
---|
1027 | READ_VMCS_RET(VMCS_RO_VMEXIT_INSTR_LEN, &cbInstr);
|
---|
1028 | if (cbInstr < 1 || cbInstr > 15)
|
---|
1029 | return runtimeError("Bad instr len: %#llx\n", cbInstr);
|
---|
1030 | uint64_t uRip = UINT64_MAX;
|
---|
1031 | READ_REG_RET(HV_X86_RIP, &uRip);
|
---|
1032 | WRITE_REG_RET(HV_X86_RIP, uRip + cbInstr);
|
---|
1033 | }
|
---|
1034 | else if (uExitReason == VMX_REASON_CPUID && (fTest & MY_TEST_F_CPUID))
|
---|
1035 | {
|
---|
1036 | /* Set registers and advance RIP. */
|
---|
1037 | WRITE_REG_RET(HV_X86_RAX, 0x42424242);
|
---|
1038 | WRITE_REG_RET(HV_X86_RCX, 0x04242424);
|
---|
1039 | WRITE_REG_RET(HV_X86_RDX, 0x00424242);
|
---|
1040 | WRITE_REG_RET(HV_X86_RBX, 0x00024242);
|
---|
1041 |
|
---|
1042 | uint64_t cbInstr = UINT64_MAX;
|
---|
1043 | READ_VMCS_RET(VMCS_RO_VMEXIT_INSTR_LEN, &cbInstr);
|
---|
1044 | if (cbInstr < 1 || cbInstr > 15)
|
---|
1045 | return runtimeError("Bad instr len: %#llx\n", cbInstr);
|
---|
1046 | uint64_t uRip = UINT64_MAX;
|
---|
1047 | READ_REG_RET(HV_X86_RIP, &uRip);
|
---|
1048 | WRITE_REG_RET(HV_X86_RIP, uRip + cbInstr);
|
---|
1049 | }
|
---|
1050 | else if (uExitReason == VMX_REASON_EPT_VIOLATION)
|
---|
1051 | {
|
---|
1052 | uint64_t uEptQual = UINT64_MAX;
|
---|
1053 | READ_VMCS_RET(VMCS_RO_EXIT_QUALIFIC, &uEptQual);
|
---|
1054 | uint64_t GCPhys = UINT64_MAX;
|
---|
1055 | READ_VMCS_RET(VMCS_GUEST_PHYSICAL_ADDRESS, &GCPhys);
|
---|
1056 | if (GCPhys == MY_NOP_MMIO && (fTest & MY_TEST_F_NOP_MMIO))
|
---|
1057 | { /* likely */ }
|
---|
1058 | else if (GCPhys == MY_TEST_RIP)
|
---|
1059 | continue; /* dunno why we get this, but restarting it works */
|
---|
1060 | else
|
---|
1061 | return runtimeError("Unexpected EPT viotaion at %#llx\n", GCPhys);
|
---|
1062 |
|
---|
1063 | /* Set RAX and advance RIP. */
|
---|
1064 | WRITE_REG_RET(HV_X86_RAX, 42);
|
---|
1065 |
|
---|
1066 | uint64_t cbInstr = UINT64_MAX;
|
---|
1067 | READ_VMCS_RET(VMCS_RO_VMEXIT_INSTR_LEN, &cbInstr);
|
---|
1068 | if (cbInstr < 1 || cbInstr > 15)
|
---|
1069 | return runtimeError("Bad instr len: %#llx\n", cbInstr);
|
---|
1070 | uint64_t uRip = UINT64_MAX;
|
---|
1071 | READ_REG_RET(HV_X86_RIP, &uRip);
|
---|
1072 | WRITE_REG_RET(HV_X86_RIP, uRip + cbInstr);
|
---|
1073 | }
|
---|
1074 | else if (uExitReason == VMX_REASON_IRQ)
|
---|
1075 | { /* ignore */ }
|
---|
1076 | else
|
---|
1077 | return runtimeError("Unexpected exit reason: %#x\n", uExitReason);
|
---|
1078 | }
|
---|
1079 | else
|
---|
1080 | return runtimeError("VM entry failure: %#x\n", uExitReason);
|
---|
1081 | }
|
---|
1082 | else
|
---|
1083 | return runtimeError("hv_vcpu_run failed (for %s): %#x\n", pszInstruction, rcHv);
|
---|
1084 | }
|
---|
1085 | uint64_t const nsElapsed = getNanoTS() - nsStart;
|
---|
1086 | return reportResult(pszInstruction, cInstructions, nsElapsed, cExits);
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | #else
|
---|
1090 | # error "port me"
|
---|
1091 | #endif
|
---|
1092 |
|
---|
1093 | void dumpCode(uint8_t const *pb, uint8_t *pbEnd)
|
---|
1094 | {
|
---|
1095 | RTPrintf("testing:");
|
---|
1096 | for (; pb != pbEnd; pb++)
|
---|
1097 | RTPrintf(" %02x", *pb);
|
---|
1098 | RTPrintf("\n");
|
---|
1099 | }
|
---|
1100 |
|
---|
1101 |
|
---|
1102 | int ioportTest(unsigned cFactor)
|
---|
1103 | {
|
---|
1104 | /*
|
---|
1105 | * Produce realmode code
|
---|
1106 | */
|
---|
1107 | unsigned char *pb = &g_pbMem[MY_TEST_RIP - MY_MEM_BASE];
|
---|
1108 | unsigned char * const pbStart = pb;
|
---|
1109 | /* OUT DX, AL - 10 times */
|
---|
1110 | for (unsigned i = 0; i < 10; i++)
|
---|
1111 | *pb++ = 0xee;
|
---|
1112 | /* DEC ECX */
|
---|
1113 | *pb++ = 0x66;
|
---|
1114 | *pb++ = 0x48 + 1;
|
---|
1115 | /* JNZ MY_TEST_RIP */
|
---|
1116 | *pb++ = 0x75;
|
---|
1117 | *pb = (signed char)(pbStart - pb - 1);
|
---|
1118 | pb++;
|
---|
1119 | /* OUT 1, AL - Temination port call. */
|
---|
1120 | *pb++ = 0xe6;
|
---|
1121 | *pb++ = MY_TERM_PORT;
|
---|
1122 | /* JMP to previous instruction */
|
---|
1123 | *pb++ = 0xeb;
|
---|
1124 | *pb++ = 0xfc;
|
---|
1125 | dumpCode(pbStart, pb);
|
---|
1126 |
|
---|
1127 | return runRealModeTest(100000 * cFactor, "OUT", MY_TEST_F_NOP_IO,
|
---|
1128 | 42 /*eax*/, 10000 * cFactor /*ecx*/, MY_NOP_PORT /*edx*/, 0 /*ebx*/,
|
---|
1129 | 0 /*esp*/, 0 /*ebp*/, 0 /*esi*/, 0 /*uEdi*/);
|
---|
1130 | }
|
---|
1131 |
|
---|
1132 |
|
---|
1133 | int cpuidTest(unsigned cFactor)
|
---|
1134 | {
|
---|
1135 | /*
|
---|
1136 | * Produce realmode code
|
---|
1137 | */
|
---|
1138 | unsigned char *pb = &g_pbMem[MY_TEST_RIP - MY_MEM_BASE];
|
---|
1139 | unsigned char * const pbStart = pb;
|
---|
1140 | for (unsigned i = 0; i < 10; i++)
|
---|
1141 | {
|
---|
1142 | /* XOR EAX,EAX */
|
---|
1143 | *pb++ = 0x66;
|
---|
1144 | *pb++ = 0x33;
|
---|
1145 | *pb++ = 0xc0;
|
---|
1146 |
|
---|
1147 | /* CPUID */
|
---|
1148 | *pb++ = 0x0f;
|
---|
1149 | *pb++ = 0xa2;
|
---|
1150 | }
|
---|
1151 | /* DEC ESI */
|
---|
1152 | *pb++ = 0x66;
|
---|
1153 | *pb++ = 0x48 + 6;
|
---|
1154 | /* JNZ MY_TEST_RIP */
|
---|
1155 | *pb++ = 0x75;
|
---|
1156 | *pb = (signed char)(pbStart - pb - 1);
|
---|
1157 | pb++;
|
---|
1158 | /* OUT 1, AL - Temination port call. */
|
---|
1159 | *pb++ = 0xe6;
|
---|
1160 | *pb++ = MY_TERM_PORT;
|
---|
1161 | /* JMP to previous instruction */
|
---|
1162 | *pb++ = 0xeb;
|
---|
1163 | *pb++ = 0xfc;
|
---|
1164 | dumpCode(pbStart, pb);
|
---|
1165 |
|
---|
1166 | return runRealModeTest(100000 * cFactor, "CPUID", MY_TEST_F_CPUID,
|
---|
1167 | 0 /*eax*/, 0 /*ecx*/, 0 /*edx*/, 0 /*ebx*/,
|
---|
1168 | 0 /*esp*/, 0 /*ebp*/, 10000 * cFactor /*esi*/, 0 /*uEdi*/);
|
---|
1169 | }
|
---|
1170 |
|
---|
1171 |
|
---|
1172 | int mmioTest(unsigned cFactor)
|
---|
1173 | {
|
---|
1174 | /*
|
---|
1175 | * Produce realmode code accessing MY_MMIO_NOP address assuming it's low.
|
---|
1176 | */
|
---|
1177 | unsigned char *pb = &g_pbMem[MY_TEST_RIP - MY_MEM_BASE];
|
---|
1178 | unsigned char * const pbStart = pb;
|
---|
1179 | for (unsigned i = 0; i < 10; i++)
|
---|
1180 | {
|
---|
1181 | /* MOV AL,DS:[BX] */
|
---|
1182 | *pb++ = 0x8a;
|
---|
1183 | *pb++ = 0x07;
|
---|
1184 | }
|
---|
1185 | /* DEC ESI */
|
---|
1186 | *pb++ = 0x66;
|
---|
1187 | *pb++ = 0x48 + 6;
|
---|
1188 | /* JNZ MY_TEST_RIP */
|
---|
1189 | *pb++ = 0x75;
|
---|
1190 | *pb = (signed char)(pbStart - pb - 1);
|
---|
1191 | pb++;
|
---|
1192 | /* OUT 1, AL - Temination port call. */
|
---|
1193 | *pb++ = 0xe6;
|
---|
1194 | *pb++ = MY_TERM_PORT;
|
---|
1195 | /* JMP to previous instruction */
|
---|
1196 | *pb++ = 0xeb;
|
---|
1197 | *pb++ = 0xfc;
|
---|
1198 | dumpCode(pbStart, pb);
|
---|
1199 |
|
---|
1200 | return runRealModeTest(100000 * cFactor, "MMIO/r1", MY_TEST_F_NOP_MMIO,
|
---|
1201 | 0 /*eax*/, 0 /*ecx*/, 0 /*edx*/, MY_NOP_MMIO /*ebx*/,
|
---|
1202 | 0 /*esp*/, 0 /*ebp*/, 10000 * cFactor /*esi*/, 0 /*uEdi*/);
|
---|
1203 | }
|
---|
1204 |
|
---|
1205 |
|
---|
1206 |
|
---|
1207 | int main(int argc, char **argv)
|
---|
1208 | {
|
---|
1209 | /*
|
---|
1210 | * Do some parameter parsing.
|
---|
1211 | */
|
---|
1212 | #ifdef RT_OS_WINDOWS
|
---|
1213 | unsigned const cFactorDefault = 4;
|
---|
1214 | #elif RT_OS_DARWIN
|
---|
1215 | unsigned const cFactorDefault = 32;
|
---|
1216 | #else
|
---|
1217 | unsigned const cFactorDefault = 24;
|
---|
1218 | #endif
|
---|
1219 | unsigned cFactor = cFactorDefault;
|
---|
1220 | for (int i = 1; i < argc; i++)
|
---|
1221 | {
|
---|
1222 | const char *pszArg = argv[i];
|
---|
1223 | if ( strcmp(pszArg, "--help") == 0
|
---|
1224 | || strcmp(pszArg, "/help") == 0
|
---|
1225 | || strcmp(pszArg, "-h") == 0
|
---|
1226 | || strcmp(pszArg, "-?") == 0
|
---|
1227 | || strcmp(pszArg, "/?") == 0)
|
---|
1228 | {
|
---|
1229 | RTPrintf("Does some benchmarking of the native NEM engine.\n"
|
---|
1230 | "\n"
|
---|
1231 | "Usage: NemRawBench-1 --factor <factor>\n"
|
---|
1232 | "\n"
|
---|
1233 | "Options\n"
|
---|
1234 | " --factor <factor>\n"
|
---|
1235 | " Iteration count factor. Default is %u.\n"
|
---|
1236 | " Lower it if execution is slow, increase if quick.\n",
|
---|
1237 | cFactorDefault);
|
---|
1238 | return 0;
|
---|
1239 | }
|
---|
1240 | if (strcmp(pszArg, "--factor") == 0)
|
---|
1241 | {
|
---|
1242 | i++;
|
---|
1243 | if (i < argc)
|
---|
1244 | cFactor = RTStrToUInt32(argv[i]);
|
---|
1245 | else
|
---|
1246 | {
|
---|
1247 | RTStrmPrintf(g_pStdErr, "syntax error: Option %s is takes a value!\n", pszArg);
|
---|
1248 | return 2;
|
---|
1249 | }
|
---|
1250 | }
|
---|
1251 | else
|
---|
1252 | {
|
---|
1253 | RTStrmPrintf(g_pStdErr, "syntax error: Unknown option: %s\n", pszArg);
|
---|
1254 | return 2;
|
---|
1255 | }
|
---|
1256 | }
|
---|
1257 |
|
---|
1258 | /*
|
---|
1259 | * Create the VM
|
---|
1260 | */
|
---|
1261 | g_cbMem = 128*1024 - MY_MEM_BASE;
|
---|
1262 | int rcExit = createVM();
|
---|
1263 | if (rcExit == 0)
|
---|
1264 | {
|
---|
1265 | RTPrintf("tstNemBench-1: Successfully created test VM...\n");
|
---|
1266 |
|
---|
1267 | /*
|
---|
1268 | * Do the benchmarking.
|
---|
1269 | */
|
---|
1270 | ioportTest(cFactor);
|
---|
1271 | cpuidTest(cFactor);
|
---|
1272 | mmioTest(cFactor);
|
---|
1273 |
|
---|
1274 | RTPrintf("tstNemBench-1: done\n");
|
---|
1275 | }
|
---|
1276 | return rcExit;
|
---|
1277 | }
|
---|
1278 |
|
---|
1279 | /*
|
---|
1280 | * Results:
|
---|
1281 | *
|
---|
1282 | * - Darwin/xnu 10.12.6/16.7.0; 3.1GHz Intel Core i7-7920HQ (Kaby Lake):
|
---|
1283 | * 925 845 OUT instructions per second (3 200 307 exits in 3 456 301 621 ns)
|
---|
1284 | * 949 278 CPUID instructions per second (3 200 222 exits in 3 370 980 173 ns)
|
---|
1285 | * 871 499 MMIO/r1 instructions per second (3 200 223 exits in 3 671 834 221 ns)
|
---|
1286 | *
|
---|
1287 | * - Linux 4.15.0 / ubuntu 18.04.1 Desktop LiveCD; 3.1GHz Intel Core i7-7920HQ (Kaby Lake):
|
---|
1288 | * 829 775 OUT instructions per second (3 200 001 exits in 3 856 466 567 ns)
|
---|
1289 | * 2 212 038 CPUID instructions per second (1 exits in 1 446 629 591 ns) [1]
|
---|
1290 | * 477 962 MMIO/r1 instructions per second (3 200 001 exits in 6 695 090 600 ns)
|
---|
1291 | *
|
---|
1292 | * - Linux 4.15.0 / ubuntu 18.04.1 Desktop LiveCD; 3.4GHz Core i5-3570 (Ivy Bridge):
|
---|
1293 | * 717 216 OUT instructions per second (2 400 001 exits in 3 346 271 640 ns)
|
---|
1294 | * 1 675 983 CPUID instructions per second (1 exits in 1 431 995 135 ns) [1]
|
---|
1295 | * 402 621 MMIO/r1 instructions per second (2 400 001 exits in 5 960 930 854 ns)
|
---|
1296 | *
|
---|
1297 | * - Linux 4.18.0-1-amd64 (debian); 3.4GHz AMD Threadripper 1950X:
|
---|
1298 | * 455 727 OUT instructions per second (2 400 001 exits in 5 266 300 471 ns)
|
---|
1299 | * 1 745 014 CPUID instructions per second (1 exits in 1 375 346 658 ns) [1]
|
---|
1300 | * 351 767 MMIO/r1 instructions per second (2 400 001 exits in 6 822 684 544 ns)
|
---|
1301 | *
|
---|
1302 | * - Windows 1803 updated as per 2018-10-01; 3.4GHz Core i5-3570 (Ivy Bridge):
|
---|
1303 | * 67 778 OUT instructions per second (400 001 exits in 5 901 560 700 ns)
|
---|
1304 | * 66 113 CPUID instructions per second (400 001 exits in 6 050 208 000 ns)
|
---|
1305 | * 62 939 MMIO/r1 instructions per second (400 001 exits in 6 355 302 900 ns)
|
---|
1306 | *
|
---|
1307 | * - Windows 1803 updated as per 2018-09-28; 3.4GHz AMD Threadripper 1950X:
|
---|
1308 | * 34 485 OUT instructions per second (400 001 exits in 11 598 918 200 ns)
|
---|
1309 | * 34 043 CPUID instructions per second (400 001 exits in 11 749 753 200 ns)
|
---|
1310 | * 33 124 MMIO/r1 instructions per second (400 001 exits in 12 075 617 000 ns)
|
---|
1311 | *
|
---|
1312 | * - Windows build 17763; 3.4GHz AMD Threadripper 1950X:
|
---|
1313 | * 65 633 OUT instructions per second (400 001 exits in 6 094 409 100 ns)
|
---|
1314 | * 65 245 CPUID instructions per second (400 001 exits in 6 130 720 600 ns)
|
---|
1315 | * 61 642 MMIO/r1 instructions per second (400 001 exits in 6 489 013 700 ns)
|
---|
1316 | *
|
---|
1317 | *
|
---|
1318 | * [1] CPUID causes no return to ring-3 with KVM.
|
---|
1319 | *
|
---|
1320 | *
|
---|
1321 | * For reference we can compare with similar tests in bs2-test1 running VirtualBox:
|
---|
1322 | *
|
---|
1323 | * - Linux 4.18.0-1-amd64 (debian); 3.4GHz AMD Threadripper 1950X; trunk/r125404:
|
---|
1324 | * real mode, 32-bit OUT : 1 338 471 ins/sec
|
---|
1325 | * real mode, 32-bit OUT-to-ring-3 : 500 337 ins/sec
|
---|
1326 | * real mode, CPUID : 1 566 343 ins/sec
|
---|
1327 | * real mode, 32-bit write : 870 671 ins/sec
|
---|
1328 | * real mode, 32-bit write-to-ring-3: 391 014 ins/sec
|
---|
1329 | *
|
---|
1330 | * - Darwin/xnu 10.12.6/16.7.0; 3.1GHz Intel Core i7-7920HQ (Kaby Lake); trunk/r125404:
|
---|
1331 | * real mode, 32-bit OUT : 790 117 ins/sec
|
---|
1332 | * real mode, 32-bit OUT-to-ring-3 : 157 205 ins/sec
|
---|
1333 | * real mode, CPUID : 1 001 087 ins/sec
|
---|
1334 | * real mode, 32-bit write : 651 257 ins/sec
|
---|
1335 | * real mode, 32-bit write-to-ring-3: 157 773 ins/sec
|
---|
1336 | *
|
---|
1337 | * - Linux 4.15.0 / ubuntu 18.04.1 Desktop LiveCD; 3.1GHz Intel Core i7-7920HQ (Kaby Lake); trunk/r125450:
|
---|
1338 | * real mode, 32-bit OUT : 1 229 245 ins/sec
|
---|
1339 | * real mode, 32-bit OUT-to-ring-3 : 284 848 ins/sec
|
---|
1340 | * real mode, CPUID : 1 429 760 ins/sec
|
---|
1341 | * real mode, 32-bit write : 820 679 ins/sec
|
---|
1342 | * real mode, 32-bit write-to-ring-3: 245 159 ins/sec
|
---|
1343 | *
|
---|
1344 | * - Windows 1803 updated as per 2018-10-01; 3.4GHz Core i5-3570 (Ivy Bridge); trunk/r15442:
|
---|
1345 | * real mode, 32-bit OUT : 961 939 ins/sec
|
---|
1346 | * real mode, 32-bit OUT-to-ring-3 : 189 458 ins/sec
|
---|
1347 | * real mode, CPUID : 1 060 582 ins/sec
|
---|
1348 | * real mode, 32-bit write : 637 967 ins/sec
|
---|
1349 | * real mode, 32-bit write-to-ring-3: 148 573 ins/sec
|
---|
1350 | *
|
---|
1351 | */
|
---|