VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMTests.cpp@ 18499

Last change on this file since 18499 was 16861, checked in by vboxsync, 15 years ago

More assertions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.8 KB
Line 
1/* $Id: VMMTests.cpp 16861 2009-02-17 16:37:45Z vboxsync $ */
2/** @file
3 * VMM - The Virtual Machine Monitor Core, Tests.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22//#define NO_SUPCALLR0VMM
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_VMM
28#include <VBox/vmm.h>
29#include <VBox/pdm.h>
30#include <VBox/cpum.h>
31#include <VBox/mm.h>
32#include <VBox/trpm.h>
33#include <VBox/selm.h>
34#include "VMMInternal.h"
35#include <VBox/vm.h>
36#include <VBox/err.h>
37#include <VBox/param.h>
38#include <VBox/x86.h>
39#include <VBox/hwaccm.h>
40
41#include <iprt/assert.h>
42#include <iprt/asm.h>
43#include <iprt/time.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46
47
48/**
49 * Performs a testcase.
50 *
51 * @returns return value from the test.
52 * @param pVM The VM handle.
53 * @param enmTestcase The testcase operation to perform.
54 * @param uVariation The testcase variation id.
55 */
56static int vmmR3DoGCTest(PVM pVM, VMMGCOPERATION enmTestcase, unsigned uVariation)
57{
58 RTRCPTR RCPtrEP;
59 int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
60 if (RT_FAILURE(rc))
61 return rc;
62
63 CPUMHyperSetCtxCore(pVM, NULL);
64 memset(pVM->vmm.s.pbEMTStackR3, 0xaa, VMM_STACK_SIZE);
65 CPUMSetHyperESP(pVM, pVM->vmm.s.pbEMTStackBottomRC); /* Clear the stack. */
66 CPUMPushHyper(pVM, uVariation);
67 CPUMPushHyper(pVM, enmTestcase);
68 CPUMPushHyper(pVM, pVM->pVMRC);
69 CPUMPushHyper(pVM, 3 * sizeof(RTRCPTR)); /* stack frame size */
70 CPUMPushHyper(pVM, RCPtrEP); /* what to call */
71 CPUMSetHyperEIP(pVM, pVM->vmm.s.pfnCallTrampolineRC);
72 Assert(CPUMGetHyperCR3(pVM) && CPUMGetHyperCR3(pVM) == PGMGetHyperCR3(pVM));
73 rc = SUPCallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
74 if (RT_LIKELY(rc == VINF_SUCCESS))
75 rc = pVM->vmm.s.iLastGZRc;
76 return rc;
77}
78
79
80/**
81 * Performs a trap test.
82 *
83 * @returns Return value from the trap test.
84 * @param pVM The VM handle.
85 * @param u8Trap The trap number to test.
86 * @param uVariation The testcase variation.
87 * @param rcExpect The expected result.
88 * @param u32Eax The expected eax value.
89 * @param pszFaultEIP The fault address. Pass NULL if this isn't available or doesn't apply.
90 * @param pszDesc The test description.
91 */
92static int vmmR3DoTrapTest(PVM pVM, uint8_t u8Trap, unsigned uVariation, int rcExpect, uint32_t u32Eax, const char *pszFaultEIP, const char *pszDesc)
93{
94 RTPrintf("VMM: testing 0%x / %d - %s\n", u8Trap, uVariation, pszDesc);
95
96 RTRCPTR RCPtrEP;
97 int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
98 if (RT_FAILURE(rc))
99 return rc;
100
101 CPUMHyperSetCtxCore(pVM, NULL);
102 memset(pVM->vmm.s.pbEMTStackR3, 0xaa, VMM_STACK_SIZE);
103 CPUMSetHyperESP(pVM, pVM->vmm.s.pbEMTStackBottomRC); /* Clear the stack. */
104 CPUMPushHyper(pVM, uVariation);
105 CPUMPushHyper(pVM, u8Trap + VMMGC_DO_TESTCASE_TRAP_FIRST);
106 CPUMPushHyper(pVM, pVM->pVMRC);
107 CPUMPushHyper(pVM, 3 * sizeof(RTRCPTR)); /* stack frame size */
108 CPUMPushHyper(pVM, RCPtrEP); /* what to call */
109 CPUMSetHyperEIP(pVM, pVM->vmm.s.pfnCallTrampolineRC);
110 Assert(CPUMGetHyperCR3(pVM) && CPUMGetHyperCR3(pVM) == PGMGetHyperCR3(pVM));
111 rc = SUPCallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
112 if (RT_LIKELY(rc == VINF_SUCCESS))
113 rc = pVM->vmm.s.iLastGZRc;
114 bool fDump = false;
115 if (rc != rcExpect)
116 {
117 RTPrintf("VMM: FAILURE - rc=%Rrc expected %Rrc\n", rc, rcExpect);
118 if (rc != VERR_NOT_IMPLEMENTED)
119 fDump = true;
120 }
121 else if ( rcExpect != VINF_SUCCESS
122 && u8Trap != 8 /* double fault doesn't dare set TrapNo. */
123 && u8Trap != 3 /* guest only, we're not in guest. */
124 && u8Trap != 1 /* guest only, we're not in guest. */
125 && u8Trap != TRPMGetTrapNo(pVM))
126 {
127 RTPrintf("VMM: FAILURE - Trap %#x expected %#x\n", TRPMGetTrapNo(pVM), u8Trap);
128 fDump = true;
129 }
130 else if (pszFaultEIP)
131 {
132 RTRCPTR RCPtrFault;
133 int rc2 = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, pszFaultEIP, &RCPtrFault);
134 if (RT_FAILURE(rc2))
135 RTPrintf("VMM: FAILURE - Failed to resolve symbol '%s', %Rrc!\n", pszFaultEIP, rc);
136 else if (RCPtrFault != CPUMGetHyperEIP(pVM))
137 {
138 RTPrintf("VMM: FAILURE - EIP=%08RX32 expected %RRv (%s)\n", CPUMGetHyperEIP(pVM), RCPtrFault, pszFaultEIP);
139 fDump = true;
140 }
141 }
142 else if (rcExpect != VINF_SUCCESS)
143 {
144 if (CPUMGetHyperSS(pVM) == SELMGetHyperDS(pVM))
145 RTPrintf("VMM: FAILURE - ss=%x expected %x\n", CPUMGetHyperSS(pVM), SELMGetHyperDS(pVM));
146 if (CPUMGetHyperES(pVM) == SELMGetHyperDS(pVM))
147 RTPrintf("VMM: FAILURE - es=%x expected %x\n", CPUMGetHyperES(pVM), SELMGetHyperDS(pVM));
148 if (CPUMGetHyperDS(pVM) == SELMGetHyperDS(pVM))
149 RTPrintf("VMM: FAILURE - ds=%x expected %x\n", CPUMGetHyperDS(pVM), SELMGetHyperDS(pVM));
150 if (CPUMGetHyperFS(pVM) == SELMGetHyperDS(pVM))
151 RTPrintf("VMM: FAILURE - fs=%x expected %x\n", CPUMGetHyperFS(pVM), SELMGetHyperDS(pVM));
152 if (CPUMGetHyperGS(pVM) == SELMGetHyperDS(pVM))
153 RTPrintf("VMM: FAILURE - gs=%x expected %x\n", CPUMGetHyperGS(pVM), SELMGetHyperDS(pVM));
154 if (CPUMGetHyperEDI(pVM) == 0x01234567)
155 RTPrintf("VMM: FAILURE - edi=%x expected %x\n", CPUMGetHyperEDI(pVM), 0x01234567);
156 if (CPUMGetHyperESI(pVM) == 0x42000042)
157 RTPrintf("VMM: FAILURE - esi=%x expected %x\n", CPUMGetHyperESI(pVM), 0x42000042);
158 if (CPUMGetHyperEBP(pVM) == 0xffeeddcc)
159 RTPrintf("VMM: FAILURE - ebp=%x expected %x\n", CPUMGetHyperEBP(pVM), 0xffeeddcc);
160 if (CPUMGetHyperEBX(pVM) == 0x89abcdef)
161 RTPrintf("VMM: FAILURE - ebx=%x expected %x\n", CPUMGetHyperEBX(pVM), 0x89abcdef);
162 if (CPUMGetHyperECX(pVM) == 0xffffaaaa)
163 RTPrintf("VMM: FAILURE - ecx=%x expected %x\n", CPUMGetHyperECX(pVM), 0xffffaaaa);
164 if (CPUMGetHyperEDX(pVM) == 0x77778888)
165 RTPrintf("VMM: FAILURE - edx=%x expected %x\n", CPUMGetHyperEDX(pVM), 0x77778888);
166 if (CPUMGetHyperEAX(pVM) == u32Eax)
167 RTPrintf("VMM: FAILURE - eax=%x expected %x\n", CPUMGetHyperEAX(pVM), u32Eax);
168 }
169 if (fDump)
170 VMMR3FatalDump(pVM, rc);
171 return rc;
172}
173
174
175/* execute the switch. */
176VMMR3DECL(int) VMMDoTest(PVM pVM)
177{
178#if 1
179#ifdef NO_SUPCALLR0VMM
180 RTPrintf("NO_SUPCALLR0VMM\n");
181 return VINF_SUCCESS;
182#endif
183
184 /*
185 * Setup stack for calling VMMGCEntry().
186 */
187 RTRCPTR RCPtrEP;
188 int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
189 if (RT_SUCCESS(rc))
190 {
191 RTPrintf("VMM: VMMGCEntry=%RRv\n", RCPtrEP);
192
193 /*
194 * Test various crashes which we must be able to recover from.
195 */
196 vmmR3DoTrapTest(pVM, 0x3, 0, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3");
197 vmmR3DoTrapTest(pVM, 0x3, 1, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3 WP");
198
199#if defined(DEBUG_bird) /* guess most people would like to skip these since they write to com1. */
200 vmmR3DoTrapTest(pVM, 0x8, 0, VERR_TRPM_PANIC, 0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG]");
201 SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */
202 bool f;
203 rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "DoubleFault", &f);
204#if !defined(DEBUG_bird)
205 if (RT_SUCCESS(rc) && f)
206#endif
207 {
208 /* see tripple fault warnings in SELM and VMMGC.cpp. */
209 vmmR3DoTrapTest(pVM, 0x8, 1, VERR_TRPM_PANIC, 0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG] WP");
210 SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */
211 }
212#endif
213
214 vmmR3DoTrapTest(pVM, 0xd, 0, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP");
215 ///@todo find a better \#GP case, on intel ltr will \#PF (busy update?) and not \#GP.
216 //vmmR3DoTrapTest(pVM, 0xd, 1, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP WP");
217
218 vmmR3DoTrapTest(pVM, 0xe, 0, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL)");
219 vmmR3DoTrapTest(pVM, 0xe, 1, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL) WP");
220 vmmR3DoTrapTest(pVM, 0xe, 2, VINF_SUCCESS, 0x00000000, NULL, "#PF w/Tmp Handler");
221 vmmR3DoTrapTest(pVM, 0xe, 4, VINF_SUCCESS, 0x00000000, NULL, "#PF w/Tmp Handler and bad fs");
222
223 /*
224 * Set a debug register and perform a context switch.
225 */
226 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
227 if (rc != VINF_SUCCESS)
228 {
229 RTPrintf("VMM: Nop test failed, rc=%Rrc not VINF_SUCCESS\n", rc);
230 return rc;
231 }
232
233 /* a harmless breakpoint */
234 RTPrintf("VMM: testing hardware bp at 0x10000 (not hit)\n");
235 DBGFADDRESS Addr;
236 DBGFR3AddrFromFlat(pVM, &Addr, 0x10000);
237 RTUINT iBp0;
238 rc = DBGFR3BpSetReg(pVM, &Addr, 0, ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp0);
239 AssertReleaseRC(rc);
240 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
241 if (rc != VINF_SUCCESS)
242 {
243 RTPrintf("VMM: DR0=0x10000 test failed with rc=%Rrc!\n", rc);
244 return rc;
245 }
246
247 /* a bad one at VMMGCEntry */
248 RTPrintf("VMM: testing hardware bp at VMMGCEntry (hit)\n");
249 DBGFR3AddrFromFlat(pVM, &Addr, RCPtrEP);
250 RTUINT iBp1;
251 rc = DBGFR3BpSetReg(pVM, &Addr, 0, ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp1);
252 AssertReleaseRC(rc);
253 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
254 if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
255 {
256 RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
257 return rc;
258 }
259
260 /* resume the breakpoint */
261 RTPrintf("VMM: resuming hyper after breakpoint\n");
262 CPUMSetHyperEFlags(pVM, CPUMGetHyperEFlags(pVM) | X86_EFL_RF);
263 rc = VMMR3ResumeHyper(pVM);
264 if (rc != VINF_SUCCESS)
265 {
266 RTPrintf("VMM: failed to resume on hyper breakpoint, rc=%Rrc\n", rc);
267 return rc;
268 }
269
270 /* engage the breakpoint again and try single stepping. */
271 RTPrintf("VMM: testing hardware bp at VMMGCEntry + stepping\n");
272 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
273 if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
274 {
275 RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
276 return rc;
277 }
278
279 RTGCUINTREG OldPc = CPUMGetHyperEIP(pVM);
280 RTPrintf("%RGr=>", OldPc);
281 unsigned i;
282 for (i = 0; i < 8; i++)
283 {
284 CPUMSetHyperEFlags(pVM, CPUMGetHyperEFlags(pVM) | X86_EFL_TF | X86_EFL_RF);
285 rc = VMMR3ResumeHyper(pVM);
286 if (rc != VINF_EM_DBG_HYPER_STEPPED)
287 {
288 RTPrintf("\nVMM: failed to step on hyper breakpoint, rc=%Rrc\n", rc);
289 return rc;
290 }
291 RTGCUINTREG Pc = CPUMGetHyperEIP(pVM);
292 RTPrintf("%RGr=>", Pc);
293 if (Pc == OldPc)
294 {
295 RTPrintf("\nVMM: step failed, PC: %RGr -> %RGr\n", OldPc, Pc);
296 return VERR_GENERAL_FAILURE;
297 }
298 OldPc = Pc;
299 }
300 RTPrintf("ok\n");
301
302 /* done, clear it */
303 if ( RT_FAILURE(DBGFR3BpClear(pVM, iBp0))
304 || RT_FAILURE(DBGFR3BpClear(pVM, iBp1)))
305 {
306 RTPrintf("VMM: Failed to clear breakpoints!\n");
307 return VERR_GENERAL_FAILURE;
308 }
309 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
310 if (rc != VINF_SUCCESS)
311 {
312 RTPrintf("VMM: NOP failed, rc=%Rrc\n", rc);
313 return rc;
314 }
315
316 /*
317 * Interrupt masking.
318 */
319 RTPrintf("VMM: interrupt masking...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250);
320 for (i = 0; i < 10000; i++)
321 {
322 uint64_t StartTick = ASMReadTSC();
323 rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_INTERRUPT_MASKING, 0);
324 if (rc != VINF_SUCCESS)
325 {
326 RTPrintf("VMM: Interrupt masking failed: rc=%Rrc\n", rc);
327 return rc;
328 }
329 uint64_t Ticks = ASMReadTSC() - StartTick;
330 if (Ticks < (SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000))
331 RTPrintf("Warning: Ticks=%RU64 (< %RU64)\n", Ticks, SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000);
332 }
333
334 /*
335 * Interrupt forwarding.
336 */
337 CPUMHyperSetCtxCore(pVM, NULL);
338 CPUMSetHyperESP(pVM, pVM->vmm.s.pbEMTStackBottomRC); /* Clear the stack. */
339 CPUMPushHyper(pVM, 0);
340 CPUMPushHyper(pVM, VMMGC_DO_TESTCASE_HYPER_INTERRUPT);
341 CPUMPushHyper(pVM, pVM->pVMRC);
342 CPUMPushHyper(pVM, 3 * sizeof(RTRCPTR)); /* stack frame size */
343 CPUMPushHyper(pVM, RCPtrEP); /* what to call */
344 CPUMSetHyperEIP(pVM, pVM->vmm.s.pfnCallTrampolineRC);
345 Log(("trampoline=%x\n", pVM->vmm.s.pfnCallTrampolineRC));
346
347 /*
348 * Switch and do da thing.
349 */
350 RTPrintf("VMM: interrupt forwarding...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250);
351 i = 0;
352 uint64_t tsBegin = RTTimeNanoTS();
353 uint64_t TickStart = ASMReadTSC();
354 Assert(CPUMGetHyperCR3(pVM) && CPUMGetHyperCR3(pVM) == PGMGetHyperCR3(pVM));
355 do
356 {
357 rc = SUPCallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
358 if (RT_LIKELY(rc == VINF_SUCCESS))
359 rc = pVM->vmm.s.iLastGZRc;
360 if (RT_FAILURE(rc))
361 {
362 Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i));
363 VMMR3FatalDump(pVM, rc);
364 return rc;
365 }
366 i++;
367 if (!(i % 32))
368 Log(("VMM: iteration %d, esi=%08x edi=%08x ebx=%08x\n",
369 i, CPUMGetHyperESI(pVM), CPUMGetHyperEDI(pVM), CPUMGetHyperEBX(pVM)));
370 } while (rc == VINF_EM_RAW_INTERRUPT_HYPER);
371 uint64_t TickEnd = ASMReadTSC();
372 uint64_t tsEnd = RTTimeNanoTS();
373
374 uint64_t Elapsed = tsEnd - tsBegin;
375 uint64_t PerIteration = Elapsed / (uint64_t)i;
376 uint64_t cTicksElapsed = TickEnd - TickStart;
377 uint64_t cTicksPerIteration = cTicksElapsed / (uint64_t)i;
378
379 RTPrintf("VMM: %8d interrupts in %11llu ns (%11llu ticks), %10llu ns/iteration (%11llu ticks)\n",
380 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration);
381 Log(("VMM: %8d interrupts in %11llu ns (%11llu ticks), %10llu ns/iteration (%11llu ticks)\n",
382 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration));
383
384 /*
385 * These forced actions are not necessary for the test and trigger breakpoints too.
386 */
387 VM_FF_CLEAR(pVM, VM_FF_TRPM_SYNC_IDT);
388 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
389
390 /*
391 * Profile switching.
392 */
393 RTPrintf("VMM: profiling switcher...\n");
394 Log(("VMM: profiling switcher...\n"));
395 uint64_t TickMin = ~0;
396 tsBegin = RTTimeNanoTS();
397 TickStart = ASMReadTSC();
398 Assert(CPUMGetHyperCR3(pVM) && CPUMGetHyperCR3(pVM) == PGMGetHyperCR3(pVM));
399 for (i = 0; i < 1000000; i++)
400 {
401 CPUMHyperSetCtxCore(pVM, NULL);
402 CPUMSetHyperESP(pVM, pVM->vmm.s.pbEMTStackBottomRC); /* Clear the stack. */
403 CPUMPushHyper(pVM, 0);
404 CPUMPushHyper(pVM, VMMGC_DO_TESTCASE_NOP);
405 CPUMPushHyper(pVM, pVM->pVMRC);
406 CPUMPushHyper(pVM, 3 * sizeof(RTRCPTR)); /* stack frame size */
407 CPUMPushHyper(pVM, RCPtrEP); /* what to call */
408 CPUMSetHyperEIP(pVM, pVM->vmm.s.pfnCallTrampolineRC);
409
410 uint64_t TickThisStart = ASMReadTSC();
411 rc = SUPCallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
412 if (RT_LIKELY(rc == VINF_SUCCESS))
413 rc = pVM->vmm.s.iLastGZRc;
414 uint64_t TickThisElapsed = ASMReadTSC() - TickThisStart;
415 if (RT_FAILURE(rc))
416 {
417 Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i));
418 VMMR3FatalDump(pVM, rc);
419 return rc;
420 }
421 if (TickThisElapsed < TickMin)
422 TickMin = TickThisElapsed;
423 }
424 TickEnd = ASMReadTSC();
425 tsEnd = RTTimeNanoTS();
426
427 Elapsed = tsEnd - tsBegin;
428 PerIteration = Elapsed / (uint64_t)i;
429 cTicksElapsed = TickEnd - TickStart;
430 cTicksPerIteration = cTicksElapsed / (uint64_t)i;
431
432 RTPrintf("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
433 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin);
434 Log(("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
435 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin));
436
437 rc = VINF_SUCCESS;
438 }
439 else
440 AssertMsgFailed(("Failed to resolved VMMGC.gc::VMMGCEntry(), rc=%Rrc\n", rc));
441#endif
442 return rc;
443}
444
445#define SYNC_SEL(pHyperCtx, reg) \
446 if (pHyperCtx->reg) \
447 { \
448 SELMSELINFO selInfo; \
449 int rc = SELMR3GetShadowSelectorInfo(pVM, pHyperCtx->reg, &selInfo); \
450 AssertRC(rc); \
451 \
452 pHyperCtx->reg##Hid.u64Base = selInfo.GCPtrBase; \
453 pHyperCtx->reg##Hid.u32Limit = selInfo.cbLimit; \
454 pHyperCtx->reg##Hid.Attr.n.u1Present = selInfo.Raw.Gen.u1Present; \
455 pHyperCtx->reg##Hid.Attr.n.u1DefBig = selInfo.Raw.Gen.u1DefBig; \
456 pHyperCtx->reg##Hid.Attr.n.u1Granularity = selInfo.Raw.Gen.u1Granularity; \
457 pHyperCtx->reg##Hid.Attr.n.u4Type = selInfo.Raw.Gen.u4Type; \
458 pHyperCtx->reg##Hid.Attr.n.u2Dpl = selInfo.Raw.Gen.u2Dpl; \
459 pHyperCtx->reg##Hid.Attr.n.u1DescType = selInfo.Raw.Gen.u1DescType; \
460 pHyperCtx->reg##Hid.Attr.n.u1Long = selInfo.Raw.Gen.u1Long; \
461 }
462
463/* execute the switch. */
464VMMR3DECL(int) VMMDoHwAccmTest(PVM pVM)
465{
466 uint32_t i;
467 int rc;
468 PCPUMCTX pHyperCtx, pGuestCtx;
469 RTGCPHYS CR3Phys = 0x0; /* fake address */
470
471 if (!HWACCMR3IsAllowed(pVM))
472 {
473 RTPrintf("VMM: Hardware accelerated test not available!\n");
474 return VERR_ACCESS_DENIED;
475 }
476
477 /*
478 * These forced actions are not necessary for the test and trigger breakpoints too.
479 */
480 VM_FF_CLEAR(pVM, VM_FF_TRPM_SYNC_IDT);
481 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
482
483 /* Enable mapping of the hypervisor into the shadow page table. */
484 uint32_t cb;
485 rc = PGMR3MappingsSize(pVM, &cb);
486 AssertRCReturn(rc, rc);
487
488 /* Pretend the mappings are now fixed; to force a refresh of the reserved PDEs. */
489 rc = PGMR3MappingsFix(pVM, MM_HYPER_AREA_ADDRESS, cb);
490 AssertRCReturn(rc, rc);
491
492 CPUMQueryHyperCtxPtr(pVM, &pHyperCtx);
493
494 pHyperCtx->cr0 = X86_CR0_PE | X86_CR0_WP | X86_CR0_PG | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE | X86_CR0_MP;
495 pHyperCtx->cr4 = X86_CR4_PGE | X86_CR4_OSFSXR | X86_CR4_OSXMMEEXCPT;
496 PGMChangeMode(pVM, pHyperCtx->cr0, pHyperCtx->cr4, pHyperCtx->msrEFER);
497 PGMSyncCR3(pVM, pHyperCtx->cr0, CR3Phys, pHyperCtx->cr4, true);
498
499 VM_FF_CLEAR(pVM, VM_FF_TO_R3);
500 VM_FF_CLEAR(pVM, VM_FF_TIMER);
501 VM_FF_CLEAR(pVM, VM_FF_REQUEST);
502
503 /*
504 * Setup stack for calling VMMGCEntry().
505 */
506 RTRCPTR RCPtrEP;
507 rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
508 if (RT_SUCCESS(rc))
509 {
510 RTPrintf("VMM: VMMGCEntry=%RRv\n", RCPtrEP);
511
512 CPUMQueryHyperCtxPtr(pVM, &pHyperCtx);
513
514 /* Fill in hidden selector registers for the hypervisor state. */
515 SYNC_SEL(pHyperCtx, cs);
516 SYNC_SEL(pHyperCtx, ds);
517 SYNC_SEL(pHyperCtx, es);
518 SYNC_SEL(pHyperCtx, fs);
519 SYNC_SEL(pHyperCtx, gs);
520 SYNC_SEL(pHyperCtx, ss);
521 SYNC_SEL(pHyperCtx, tr);
522
523 /*
524 * Profile switching.
525 */
526 RTPrintf("VMM: profiling switcher...\n");
527 Log(("VMM: profiling switcher...\n"));
528 uint64_t TickMin = ~0;
529 uint64_t tsBegin = RTTimeNanoTS();
530 uint64_t TickStart = ASMReadTSC();
531 for (i = 0; i < 1000000; i++)
532 {
533 CPUMHyperSetCtxCore(pVM, NULL);
534
535 CPUMSetHyperESP(pVM, pVM->vmm.s.pbEMTStackBottomRC); /* Clear the stack. */
536 CPUMPushHyper(pVM, 0);
537 CPUMPushHyper(pVM, VMMGC_DO_TESTCASE_HWACCM_NOP);
538 CPUMPushHyper(pVM, pVM->pVMRC);
539 CPUMPushHyper(pVM, 3 * sizeof(RTRCPTR)); /* stack frame size */
540 CPUMPushHyper(pVM, RCPtrEP); /* what to call */
541 CPUMSetHyperEIP(pVM, pVM->vmm.s.pfnCallTrampolineRC);
542
543 CPUMQueryHyperCtxPtr(pVM, &pHyperCtx);
544 pGuestCtx = CPUMQueryGuestCtxPtr(pVM);
545
546 /* Copy the hypervisor context to make sure we have a valid guest context. */
547 *pGuestCtx = *pHyperCtx;
548 pGuestCtx->cr3 = CR3Phys;
549
550 VM_FF_CLEAR(pVM, VM_FF_TO_R3);
551 VM_FF_CLEAR(pVM, VM_FF_TIMER);
552
553 uint64_t TickThisStart = ASMReadTSC();
554 rc = SUPCallVMMR0Fast(pVM->pVMR0, VMMR0_DO_HWACC_RUN, 0);
555 uint64_t TickThisElapsed = ASMReadTSC() - TickThisStart;
556 if (RT_FAILURE(rc))
557 {
558 Log(("VMM: R0 returned fatal %Rrc in iteration %d\n", rc, i));
559 VMMR3FatalDump(pVM, rc);
560 return rc;
561 }
562 if (TickThisElapsed < TickMin)
563 TickMin = TickThisElapsed;
564 }
565 uint64_t TickEnd = ASMReadTSC();
566 uint64_t tsEnd = RTTimeNanoTS();
567
568 uint64_t Elapsed = tsEnd - tsBegin;
569 uint64_t PerIteration = Elapsed / (uint64_t)i;
570 uint64_t cTicksElapsed = TickEnd - TickStart;
571 uint64_t cTicksPerIteration = cTicksElapsed / (uint64_t)i;
572
573 RTPrintf("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
574 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin);
575 Log(("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
576 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin));
577
578 rc = VINF_SUCCESS;
579 }
580 else
581 AssertMsgFailed(("Failed to resolved VMMGC.gc::VMMGCEntry(), rc=%Rrc\n", rc));
582
583 return rc;
584}
585
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use