VMMTests.cpp revision 9493023fda8db05ac27a0dd72805a18eab7d1158
/* $Id$ */
/** @file
* VMM - The Virtual Machine Monitor Core, Tests.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
//#define NO_SUPCALLR0VMM
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_VMM
#include "VMMInternal.h"
/**
* Performs a testcase.
*
* @returns return value from the test.
* @param pVM The VM handle.
* @param enmTestcase The testcase operation to perform.
* @param uVariation The testcase variation id.
*/
{
if (VBOX_FAILURE(rc))
return rc;
return rc;
}
/**
* Performs a trap test.
*
* @returns Return value from the trap test.
* @param pVM The VM handle.
* @param u8Trap The trap number to test.
* @param uVariation The testcase variation.
* @param rcExpect The expected result.
* @param u32Eax The expected eax value.
* @param pszFaultEIP The fault address. Pass NULL if this isn't available or doesn't apply.
* @param pszDesc The test description.
*/
static int vmmR3DoTrapTest(PVM pVM, uint8_t u8Trap, unsigned uVariation, int rcExpect, uint32_t u32Eax, const char *pszFaultEIP, const char *pszDesc)
{
if (VBOX_FAILURE(rc))
return rc;
bool fDump = false;
{
if (rc != VERR_NOT_IMPLEMENTED)
fDump = true;
}
else if ( rcExpect != VINF_SUCCESS
{
fDump = true;
}
else if (pszFaultEIP)
{
if (VBOX_FAILURE(rc2))
{
RTPrintf("VMM: FAILURE - EIP=%VGv expected %VGv (%s)\n", CPUMGetHyperEIP(pVM), GCPtrFault, pszFaultEIP);
fDump = true;
}
}
else if (rcExpect != VINF_SUCCESS)
{
}
if (fDump)
return rc;
}
/* execute the switch. */
{
#if 1
#ifdef NO_SUPCALLR0VMM
RTPrintf("NO_SUPCALLR0VMM\n");
return VINF_SUCCESS;
#endif
/*
* Setup stack for calling VMMGCEntry().
*/
if (VBOX_SUCCESS(rc))
{
/*
* Test various crashes which we must be able to recover from.
*/
vmmR3DoTrapTest(pVM, 0x3, 0, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3");
vmmR3DoTrapTest(pVM, 0x3, 1, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3 WP");
#if defined(DEBUG_bird) /* guess most people would like to skip these since they write to com1. */
bool f;
#if !defined(DEBUG_bird)
if (VBOX_SUCCESS(rc) && f)
#endif
{
/* see tripple fault warnings in SELM and VMMGC.cpp. */
vmmR3DoTrapTest(pVM, 0x8, 1, VERR_TRPM_PANIC, 0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG] WP");
}
#endif
vmmR3DoTrapTest(pVM, 0xd, 0, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP");
///@todo find a better \#GP case, on intel ltr will \#PF (busy update?) and not \#GP.
//vmmR3DoTrapTest(pVM, 0xd, 1, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP WP");
vmmR3DoTrapTest(pVM, 0xe, 0, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL)");
vmmR3DoTrapTest(pVM, 0xe, 1, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL) WP");
/*
* Set a debug register and perform a context switch.
*/
if (rc != VINF_SUCCESS)
{
return rc;
}
/* a harmless breakpoint */
RTPrintf("VMM: testing hardware bp at 0x10000 (not hit)\n");
if (rc != VINF_SUCCESS)
{
return rc;
}
/* a bad one at VMMGCEntry */
RTPrintf("VMM: testing hardware bp at VMMGCEntry (hit)\n");
if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
{
RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Vrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
return rc;
}
/* resume the breakpoint */
RTPrintf("VMM: resuming hyper after breakpoint\n");
if (rc != VINF_SUCCESS)
{
return rc;
}
/* engage the breakpoint again and try single stepping. */
RTPrintf("VMM: testing hardware bp at VMMGCEntry + stepping\n");
if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
{
RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Vrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
return rc;
}
unsigned i;
for (i = 0; i < 8; i++)
{
if (rc != VINF_EM_DBG_HYPER_STEPPED)
{
return rc;
}
{
return VERR_GENERAL_FAILURE;
}
}
RTPrintf("ok\n");
/* done, clear it */
{
RTPrintf("VMM: Failed to clear breakpoints!\n");
return VERR_GENERAL_FAILURE;
}
if (rc != VINF_SUCCESS)
{
return rc;
}
/*
* Interrupt masking.
*/
for (i = 0; i < 10000; i++)
{
if (rc != VINF_SUCCESS)
{
return rc;
}
RTPrintf("Warning: Ticks=%RU64 (< %RU64)\n", Ticks, SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000);
}
/*
* Interrupt forwarding.
*/
CPUMPushHyper(pVM, 0);
/*
* Switch and do da thing.
*/
i = 0;
do
{
if (VBOX_FAILURE(rc))
{
return rc;
}
i++;
if (!(i % 32))
Log(("VMM: iteration %d, esi=%08x edi=%08x ebx=%08x\n",
} while (rc == VINF_EM_RAW_INTERRUPT_HYPER);
/*
* These forced actions are not necessary for the test and trigger breakpoints too.
*/
/*
* Profile switching.
*/
RTPrintf("VMM: profiling switcher...\n");
Log(("VMM: profiling switcher...\n"));
tsBegin = RTTimeNanoTS();
TickStart = ASMReadTSC();
for (i = 0; i < 1000000; i++)
{
CPUMPushHyper(pVM, 0);
if (VBOX_FAILURE(rc))
{
return rc;
}
if (TickThisElapsed < TickMin)
}
TickEnd = ASMReadTSC();
tsEnd = RTTimeNanoTS();
RTPrintf("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
Log(("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
rc = VINF_SUCCESS;
}
else
#endif
return rc;
}
{ \
\
}
/* execute the switch. */
{
uint32_t i;
int rc;
if (!HWACCMR3IsAllowed(pVM))
{
RTPrintf("VMM: Hardware accelerated test not available!\n");
return VERR_ACCESS_DENIED;
}
/*
* These forced actions are not necessary for the test and trigger breakpoints too.
*/
/* Enable mapping of the hypervisor into the shadow page table. */
PGMR3ChangeShwPDMappings(pVM, true);
pHyperCtx->cr0 = X86_CR0_PE | X86_CR0_WP | X86_CR0_PG | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE | X86_CR0_MP;
/*
* Setup stack for calling VMMGCEntry().
*/
if (VBOX_SUCCESS(rc))
{
/* Fill in hidden selector registers for the hypervisor state. */
/*
* Profile switching.
*/
RTPrintf("VMM: profiling switcher...\n");
Log(("VMM: profiling switcher...\n"));
for (i = 0; i < 1000000; i++)
{
CPUMPushHyper(pVM, 0);
/* Copy the hypervisor context to make sure we have a valid guest context. */
if (VBOX_FAILURE(rc))
{
return rc;
}
if (TickThisElapsed < TickMin)
}
RTPrintf("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
Log(("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n",
rc = VINF_SUCCESS;
}
else
return rc;
}