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