VirtualBox

source: vbox/trunk/src/VBox/VMM/testcase/tstMicro.cpp@ 77994

Last change on this file since 77994 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.4 KB
Line 
1/* $Id: tstMicro.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * Micro Testcase, profiling special CPU operations.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#include <VBox/vmm/vm.h>
23#include <VBox/vmm/vmm.h>
24#include <VBox/vmm/mm.h>
25#include <VBox/vmm/cpum.h>
26#include <VBox/vmm/pdmapi.h>
27#include <VBox/vmm/dbgf.h>
28#include <VBox/vmm/pgm.h>
29#include <iprt/errcore.h>
30#include <VBox/param.h>
31
32#include <VBox/log.h>
33#include <iprt/assert.h>
34#include <iprt/initterm.h>
35#include <iprt/stream.h>
36#include <iprt/string.h>
37#include <iprt/semaphore.h>
38
39#include "tstMicro.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45#define TESTCASE "tstVMM"
46
47static const char *GetDescription(TSTMICROTEST enmTest)
48{
49 switch (enmTest)
50 {
51 case TSTMICROTEST_OVERHEAD: return "Overhead";
52 case TSTMICROTEST_INVLPG_0: return "invlpg [0]";
53 case TSTMICROTEST_INVLPG_EIP: return "invlpg [EIP]";
54 case TSTMICROTEST_INVLPG_ESP: return "invlpg [ESP]";
55 case TSTMICROTEST_CR3_RELOAD: return "cr3 reload";
56 case TSTMICROTEST_WP_DISABLE: return "CR0.WP <- 0";
57 case TSTMICROTEST_WP_ENABLE: return "CR0.WP <- 1";
58
59 case TSTMICROTEST_PF_R0: return "R0 #PG (NULL)";
60 case TSTMICROTEST_PF_R1: return "R1 #PG (NULL)";
61 case TSTMICROTEST_PF_R2: return "R2 #PG (NULL)";
62 case TSTMICROTEST_PF_R3: return "R3 #PG (NULL)";
63
64 default:
65 {
66 static char sz[64];
67 RTStrPrintf(sz, sizeof(sz), "%d?", enmTest);
68 return sz;
69 }
70 }
71}
72
73
74static void PrintHeaderInstr(void)
75{
76 RTPrintf(TESTCASE ": %-25s %10s %10s %10s\n",
77 "Test name",
78 "Min",
79 "Avg",
80 "Max");
81}
82
83static void PrintResultInstr(PTSTMICRO pTst, TSTMICROTEST enmTest, int rc, uint64_t cMinTicks, uint64_t cAvgTicks, uint64_t cMaxTicks)
84{
85 if (RT_FAILURE(rc))
86 RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu - %Rrc cr2=%x err=%x eip=%x!\n",
87 GetDescription(enmTest),
88 cMinTicks,
89 cAvgTicks,
90 cMaxTicks,
91 rc,
92 pTst->u32CR2,
93 pTst->u32ErrCd,
94 pTst->u32EIP);
95 else
96 RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu\n",
97 GetDescription(enmTest),
98 cMinTicks,
99 cAvgTicks,
100 cMaxTicks);
101}
102
103static void PrintHeaderTraps(void)
104{
105 RTPrintf(TESTCASE ": %-25s %10s %10s %10s %10s %10s\n",
106 "Test name",
107 "Total",
108 "ToRx",
109 "Trap",
110 "ToRxTrap",
111 "int42-done");
112}
113
114static void PrintResultTrap(PTSTMICRO pTst, TSTMICROTEST enmTest, int rc)
115{
116 if (RT_FAILURE(rc))
117 RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu %10llu %10llu - %Rrc cr2=%x err=%x eip=%x!\n",
118 GetDescription(enmTest),
119 pTst->aResults[enmTest].cTotalTicks,
120 pTst->aResults[enmTest].cToRxFirstTicks,
121 pTst->aResults[enmTest].cTrapTicks,
122 pTst->aResults[enmTest].cToRxTrapTicks,
123 pTst->aResults[enmTest].cToR0Ticks,
124 rc,
125 pTst->u32CR2,
126 pTst->u32ErrCd,
127 pTst->u32EIP);
128 else
129 RTPrintf(TESTCASE ": %-25s %10llu %10llu %10llu %10llu %10llu\n",
130 GetDescription(enmTest),
131 pTst->aResults[enmTest].cTotalTicks,
132 pTst->aResults[enmTest].cToRxFirstTicks,
133 pTst->aResults[enmTest].cTrapTicks,
134 pTst->aResults[enmTest].cToRxTrapTicks,
135 pTst->aResults[enmTest].cToR0Ticks);
136}
137
138
139/**
140 * 'Allocate' selectors for 32-bit code/data in rings 0-3.
141 *
142 * 0060 - r0 code
143 * 0068 - r0 data
144 *
145 * 1060 - r1 code
146 * 1068 - r1 data
147 *
148 * 2060 - r2 code
149 * 2068 - r2 data
150 *
151 * 3060 - r3 code
152 * 3068 - r3 data
153 *
154 */
155static void SetupSelectors(PVM pVM)
156{
157 /*
158 * Find the GDT - This is a HACK :-)
159 */
160 RTRCPTR RCPtr = CPUMGetHyperGDTR(VMMGetCpu0(pVM), NULL);
161 PX86DESC paGDTEs = (PX86DESC)MMHyperRCToR3(pVM, RCPtr);
162
163 for (unsigned i = 0; i <= 3; i++)
164 {
165 RTSEL Sel = (i << 12) + 0x60;
166
167 /* 32-bit code selector. */
168 PX86DESC pGDTE = &paGDTEs[Sel >> X86_SEL_SHIFT];
169 pGDTE->au32[0] = pGDTE->au32[1] = 0;
170 pGDTE->Gen.u16LimitLow = 0xffff;
171 pGDTE->Gen.u4LimitHigh = 0xf;
172 pGDTE->Gen.u1Granularity= 1;
173 pGDTE->Gen.u1Present = 1;
174 pGDTE->Gen.u2Dpl = i;
175 pGDTE->Gen.u1DefBig = 1;
176 pGDTE->Gen.u1DescType = 1; /* !system */
177 pGDTE->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
178
179 /* 32-bit data selector. */
180 pGDTE++;
181 pGDTE->au32[0] = pGDTE->au32[1] = 0;
182 pGDTE->Gen.u16LimitLow = 0xffff;
183 pGDTE->Gen.u4LimitHigh = 0xf;
184 pGDTE->Gen.u1Granularity= 1;
185 pGDTE->Gen.u1Present = 1;
186 pGDTE->Gen.u2Dpl = i;
187 pGDTE->Gen.u1DefBig = 1;
188 pGDTE->Gen.u1DescType = 1; /* !system */
189 pGDTE->Gen.u4Type = X86_SEL_TYPE_RW_ACC;
190 }
191}
192
193
194static DECLCALLBACK(int) doit(PVM pVM)
195{
196 RTPrintf(TESTCASE ": testing...\n");
197 SetupSelectors(pVM);
198
199 /*
200 * Loading the module and resolve the entry point.
201 */
202 int rc = PDMR3LdrLoadRC(pVM, NULL, "tstMicroRC.gc");
203 if (RT_FAILURE(rc))
204 {
205 RTPrintf(TESTCASE ": Failed to load tstMicroRC.gc, rc=%Rra\n", rc);
206 return rc;
207 }
208 RTRCPTR RCPtrEntry;
209 rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRC", &RCPtrEntry);
210 if (RT_FAILURE(rc))
211 {
212 RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRC' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
213 return rc;
214 }
215 RTRCPTR RCPtrStart;
216 rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRCAsmStart", &RCPtrStart);
217 if (RT_FAILURE(rc))
218 {
219 RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRCAsmStart' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
220 return rc;
221 }
222 RTRCPTR RCPtrEnd;
223 rc = PDMR3LdrGetSymbolRC(pVM, "tstMicroRC.gc", "tstMicroRCAsmEnd", &RCPtrEnd);
224 if (RT_FAILURE(rc))
225 {
226 RTPrintf(TESTCASE ": Failed to resolve the 'tstMicroRCAsmEnd' entry point in tstMicroRC.gc, rc=%Rra\n", rc);
227 return rc;
228 }
229
230 /*
231 * Allocate and initialize the instance data.
232 */
233 PTSTMICRO pTst;
234 rc = MMHyperAlloc(pVM, RT_ALIGN_Z(sizeof(*pTst), PAGE_SIZE), PAGE_SIZE, MM_TAG_VM, (void **)&pTst);
235 if (RT_FAILURE(rc))
236 {
237 RTPrintf(TESTCASE ": Failed to resolve allocate instance memory (%d bytes), rc=%Rra\n", sizeof(*pTst), rc);
238 return rc;
239 }
240 pTst->RCPtr = MMHyperR3ToRC(pVM, pTst);
241 pTst->RCPtrStack = MMHyperR3ToRC(pVM, &pTst->au8Stack[sizeof(pTst->au8Stack) - 32]);
242
243 /* the page must be writable from user mode */
244 rc = PGMMapModifyPage(pVM, pTst->RCPtr, sizeof(*pTst), X86_PTE_US | X86_PTE_RW, ~(uint64_t)(X86_PTE_US | X86_PTE_RW));
245 if (RT_FAILURE(rc))
246 {
247 RTPrintf(TESTCASE ": PGMMapModifyPage -> rc=%Rra\n", rc);
248 return rc;
249 }
250
251 /* all the code must be executable from R3. */
252 rc = PGMMapModifyPage(pVM, RCPtrStart, RCPtrEnd - RCPtrStart + PAGE_SIZE, X86_PTE_US, ~(uint64_t)X86_PTE_US);
253 if (RT_FAILURE(rc))
254 {
255 RTPrintf(TESTCASE ": PGMMapModifyPage -> rc=%Rra\n", rc);
256 return rc;
257 }
258 DBGFR3PagingDumpEx(pVM->pUVM, 0 /*idCpu*/, DBGFPGDMP_FLAGS_CURRENT_CR3 | DBGFPGDMP_FLAGS_CURRENT_MODE
259 | DBGFPGDMP_FLAGS_SHADOW | DBGFPGDMP_FLAGS_HEADER | DBGFPGDMP_FLAGS_PRINT_CR3,
260 0 /*cr3*/, 0 /*u64FirstAddr*/, UINT64_MAX /*u64LastAddr*/, 99 /*cMaxDepth*/, NULL);
261
262#if 0
263 /*
264 * Disassemble the assembly...
265 */
266 RTGCPTR GCPtr = RCPtrStart;
267 while (GCPtr < RCPtrEnd)
268 {
269 size_t cb = 0;
270 char sz[256];
271 int rc = DBGFR3DisasInstrEx(pVM, CPUMGetHyperCS(pVM), GCPtr, 0, sz, sizeof(sz), &cb);
272 if (RT_SUCCESS(rc))
273 RTLogPrintf("%s\n", sz);
274 else
275 {
276 RTLogPrintf("%RGv rc=%Rrc\n", GCPtr, rc);
277 cb = 1;
278 }
279 GCPtr += cb;
280 }
281#endif
282
283#ifdef VBOX_WITH_RAW_MODE
284 /*
285 * Do the profiling.
286 */
287 /* execute the instruction profiling tests */
288 PrintHeaderInstr();
289 int i;
290 for (i = TSTMICROTEST_OVERHEAD; i < TSTMICROTEST_TRAP_FIRST; i++)
291 {
292 TSTMICROTEST enmTest = (TSTMICROTEST)i;
293 uint64_t cMin = UINT64_MAX;
294 uint64_t cMax = 0;
295 uint64_t cTotal = 0;
296 unsigned cSamples = 0;
297 rc = VINF_SUCCESS;
298 for (int c = 0; c < 100; c++)
299 {
300 int rc2 = VMMR3CallRC(pVM, RCPtrEntry, 2, pTst->RCPtr, enmTest);
301 if (RT_SUCCESS(rc2))
302 {
303 uint64_t u64 = pTst->aResults[enmTest].cTotalTicks;
304 if (cMin > u64)
305 cMin = u64;
306 if (cMax < u64)
307 cMax = u64;
308 cTotal += u64;
309 cSamples++;
310 }
311 else if (RT_SUCCESS(rc))
312 rc = rc2;
313 }
314 uint64_t cAvg = cTotal / (cSamples ? cSamples : 1);
315 pTst->aResults[enmTest].cTotalTicks = cAvg;
316 PrintResultInstr(pTst, enmTest, rc, cMin, cAvg, cMax);
317 /* store the overhead */
318 if (enmTest == TSTMICROTEST_OVERHEAD)
319 pTst->u64Overhead = cMin;
320 }
321#endif
322
323
324#ifdef VBOX_WITH_RAW_MODE
325 /* execute the trap/cycle profiling tests. */
326 RTPrintf("\n");
327 PrintHeaderTraps();
328 /* don't disable rdtsc in R1/R2/R3! */
329 CPUMR3SetCR4Feature(pVM, 0, ~X86_CR4_TSD);
330 for (i = TSTMICROTEST_TRAP_FIRST; i < TSTMICROTEST_MAX; i++)
331 {
332 TSTMICROTEST enmTest = (TSTMICROTEST)i;
333 rc = VMMR3CallRC(pVM, RCPtrEntry, 2, pTst->RCPtr, enmTest);
334 PrintResultTrap(pTst, enmTest, rc);
335 }
336#endif
337
338 RTPrintf(TESTCASE ": done!\n");
339 return VINF_SUCCESS;
340}
341
342
343/**
344 * Entry point.
345 */
346extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
347{
348 RT_NOREF1(envp);
349 int rcRet = 0; /* error count. */
350
351 RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
352
353 /*
354 * Create empty VM.
355 */
356 PVM pVM;
357 PUVM pUVM;
358 int rc = VMR3Create(1, NULL, NULL, NULL, NULL, NULL, &pVM, &pUVM);
359 if (RT_SUCCESS(rc))
360 {
361 /*
362 * Do testing.
363 */
364 rc = VMR3ReqCallVoidWaitU(pUVM, VMCPUID_ANY, (PFNRT)doit, 1, pVM);
365 AssertRC(rc);
366 STAMR3Dump(pUVM, "*");
367
368 /*
369 * Cleanup.
370 */
371 rc = VMR3PowerOff(pUVM);
372 if (!RT_SUCCESS(rc))
373 {
374 RTPrintf(TESTCASE ": error: failed to power off vm! rc=%Rrc\n", rc);
375 rcRet++;
376 }
377 rc = VMR3Destroy(pUVM);
378 if (!RT_SUCCESS(rc))
379 {
380 RTPrintf(TESTCASE ": error: failed to destroy vm! rc=%Rrc\n", rc);
381 rcRet++;
382 }
383 VMR3ReleaseUVM(pUVM);
384 }
385 else
386 {
387 RTPrintf(TESTCASE ": fatal error: failed to create vm! rc=%Rrc\n", rc);
388 rcRet++;
389 }
390
391 return rcRet;
392}
393
394
395#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
396/**
397 * Main entry point.
398 */
399int main(int argc, char **argv, char **envp)
400{
401 return TrustedMain(argc, argv, envp);
402}
403#endif
404
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette