/* $Id: VMInternal.h 16311 2009-01-28 13:46:41Z vboxsync $ */ /** @file * VM - Internal header file. */ /* * 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; * you can redistribute it and/or modify it under the terms of the GNU * 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. */ #ifndef ___VMInternal_h #define ___VMInternal_h #include #include #include /** @defgroup grp_vm_int Internals * @ingroup grp_vm * @internal * @{ */ /** * At-reset callback type. */ typedef enum VMATRESETTYPE { /** Device callback. */ VMATRESETTYPE_DEV = 1, /** Internal callback . */ VMATRESETTYPE_INTERNAL, /** External callback. */ VMATRESETTYPE_EXTERNAL } VMATRESETTYPE; /** Pointer to at-reset callback. */ typedef struct VMATRESET *PVMATRESET; /** * At reset callback. */ typedef struct VMATRESET { /** Pointer to the next one in the list. */ PVMATRESET pNext; /** Callback type. */ VMATRESETTYPE enmType; /** User argument for the callback. */ void *pvUser; /** Description. */ const char *pszDesc; /** Type specific data. */ union { /** VMATRESETTYPE_DEV. */ struct { /** Callback. */ PFNVMATRESET pfnCallback; /** Device instance. */ PPDMDEVINS pDevIns; } Dev; /** VMATRESETTYPE_INTERNAL. */ struct { /** Callback. */ PFNVMATRESETINT pfnCallback; } Internal; /** VMATRESETTYPE_EXTERNAL. */ struct { /** Callback. */ PFNVMATRESETEXT pfnCallback; } External; } u; } VMATRESET; /** * VM state change callback. */ typedef struct VMATSTATE { /** Pointer to the next one. */ struct VMATSTATE *pNext; /** Pointer to the callback. */ PFNVMATSTATE pfnAtState; /** The user argument. */ void *pvUser; } VMATSTATE; /** Pointer to a VM state change callback. */ typedef VMATSTATE *PVMATSTATE; /** * VM error callback. */ typedef struct VMATERROR { /** Pointer to the next one. */ struct VMATERROR *pNext; /** Pointer to the callback. */ PFNVMATERROR pfnAtError; /** The user argument. */ void *pvUser; } VMATERROR; /** Pointer to a VM error callback. */ typedef VMATERROR *PVMATERROR; /** * Chunk of memory allocated off the hypervisor heap in which * we copy the error details. */ typedef struct VMERROR { /** The size of the chunk. */ uint32_t cbAllocated; /** The current offset into the chunk. * We start by putting the filename and function immediatly * after the end of the buffer. */ uint32_t off; /** Offset from the start of this structure to the file name. */ uint32_t offFile; /** The line number. */ uint32_t iLine; /** Offset from the start of this structure to the function name. */ uint32_t offFunction; /** Offset from the start of this structure to the formatted message text. */ uint32_t offMessage; /** The VBox status code. */ int32_t rc; } VMERROR, *PVMERROR; /** * VM runtime error callback. */ typedef struct VMATRUNTIMEERROR { /** Pointer to the next one. */ struct VMATRUNTIMEERROR *pNext; /** Pointer to the callback. */ PFNVMATRUNTIMEERROR pfnAtRuntimeError; /** The user argument. */ void *pvUser; } VMATRUNTIMEERROR; /** Pointer to a VM error callback. */ typedef VMATRUNTIMEERROR *PVMATRUNTIMEERROR; /** * Chunk of memory allocated off the hypervisor heap in which * we copy the runtime error details. */ typedef struct VMRUNTIMEERROR { /** The size of the chunk. */ uint32_t cbAllocated; /** The current offset into the chunk. * We start by putting the error ID immediatly * after the end of the buffer. */ uint32_t off; /** Offset from the start of this structure to the error ID. */ uint32_t offErrorID; /** Offset from the start of this structure to the formatted message text. */ uint32_t offMessage; /** Whether the error is fatal or not */ bool fFatal; } VMRUNTIMEERROR, *PVMRUNTIMEERROR; /** The halt method. */ typedef enum { /** The usual invalid value. */ VMHALTMETHOD_INVALID = 0, /** Use the method used during bootstrapping. */ VMHALTMETHOD_BOOTSTRAP, /** Use the default method. */ VMHALTMETHOD_DEFAULT, /** The old spin/yield/block method. */ VMHALTMETHOD_OLD, /** The first go at a block/spin method. */ VMHALTMETHOD_1, /** The first go at a more global approach. */ VMHALTMETHOD_GLOBAL_1, /** The end of valid methods. (not inclusive of course) */ VMHALTMETHOD_END, /** The usual 32-bit max value. */ VMHALTMETHOD_32BIT_HACK = 0x7fffffff } VMHALTMETHOD; /** * VM Internal Data (part of the VM structure). * * @todo Move this and all related things to VMM. The VM component was, to some * extent at least, a bad ad hoc design which should all have been put in * VMM. @see pg_vm. */ typedef struct VMINT { /** VM Error Message. */ R3PTRTYPE(PVMERROR) pErrorR3; /** VM Runtime Error Message. */ R3PTRTYPE(PVMRUNTIMEERROR) pRuntimeErrorR3; /** Set by VMR3SuspendNoSave; cleared by VMR3Resume; signals the VM is in an * inconsistent state and saving is not allowed. */ bool fPreventSaveState; } VMINT; /** Pointer to the VM Internal Data (part of the VM structure). */ typedef VMINT *PVMINT; /** * VM internal data kept in the UVM. */ typedef struct VMINTUSERPERVM { /** Head of the request queue. Atomic. */ volatile PVMREQ pReqs; /** The last index used during alloc/free. */ volatile uint32_t iReqFree; /** Number of free request packets. */ volatile uint32_t cReqFree; /** Array of pointers to lists of free request packets. Atomic. */ volatile PVMREQ apReqFree[9]; #ifdef VBOX_WITH_STATISTICS /** Number of VMR3ReqAlloc returning a new packet. */ STAMCOUNTER StatReqAllocNew; /** Number of VMR3ReqAlloc causing races. */ STAMCOUNTER StatReqAllocRaces; /** Number of VMR3ReqAlloc returning a recycled packet. */ STAMCOUNTER StatReqAllocRecycled; /** Number of VMR3ReqFree calls. */ STAMCOUNTER StatReqFree; /** Number of times the request was actually freed. */ STAMCOUNTER StatReqFreeOverflow; #endif /** Pointer to the support library session. * Mainly for creation and destruction.. */ PSUPDRVSESSION pSession; /** Wait event semaphore. */ RTSEMEVENT EventSemWait; /** Wait/Idle indicator. */ bool volatile fWait; /** Force EMT to terminate. */ bool volatile fTerminateEMT; /** If set the EMT does the final VM cleanup when it exits. * If clear the VMR3Destroy() caller does so. */ bool fEMTDoesTheCleanup; /** @name Generic Halt data * @{ */ /** The current halt method. * Can be selected by CFGM option 'VM/HaltMethod'. */ VMHALTMETHOD enmHaltMethod; /** The index into g_aHaltMethods of the current halt method. */ uint32_t volatile iHaltMethod; /** The average time (ns) between two halts in the last second. (updated once per second) */ uint32_t HaltInterval; /** The average halt frequency for the last second. (updated once per second) */ uint32_t HaltFrequency; /** The number of halts in the current period. */ uint32_t cHalts; uint32_t padding; /**< alignment padding. */ /** When we started counting halts in cHalts (RTTimeNanoTS). */ uint64_t u64HaltsStartTS; /** @} */ /** Union containing data and config for the different halt algorithms. */ union { /** * Method 1 & 2 - Block whenever possible, and when lagging behind * switch to spinning with regular blocking every 5-200ms (defaults) * depending on the accumulated lag. The blocking interval is adjusted * with the average oversleeping of the last 64 times. * * The difference between 1 and 2 is that we use native absolute * time APIs for the blocking instead of the millisecond based IPRT * interface. */ struct { /** How many times we've blocked while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cBlocks; /** Avg. time spend oversleeping when blocking. (Re-calculated every so often.) */ uint64_t cNSBlockedTooLongAvg; /** Total time spend oversleeping when blocking. */ uint64_t cNSBlockedTooLong; /** Total time spent blocking. */ uint64_t cNSBlocked; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastBlockTS; /** When we started spinning relentlessly in order to catch up some of the oversleeping. * This is 0 when we're not spinning. */ uint64_t u64StartSpinTS; /** The max interval without blocking (when spinning). */ uint32_t u32MinBlockIntervalCfg; /** The minimum interval between blocking (when spinning). */ uint32_t u32MaxBlockIntervalCfg; /** The value to divide the current lag by to get the raw blocking interval (when spinning). */ uint32_t u32LagBlockIntervalDivisorCfg; /** When to start spinning (lag / nano secs). */ uint32_t u32StartSpinningCfg; /** When to stop spinning (lag / nano secs). */ uint32_t u32StopSpinningCfg; } Method12; #if 0 /** * Method 3 & 4 - Same as method 1 & 2 respectivly, except that we * sprinkle it with yields. */ struct { /** How many times we've blocked while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cBlocks; /** Avg. time spend oversleeping when blocking. (Re-calculated every so often.) */ uint64_t cBlockedTooLongNSAvg; /** Total time spend oversleeping when blocking. */ uint64_t cBlockedTooLongNS; /** Total time spent blocking. */ uint64_t cBlockedNS; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastBlockTS; /** How many times we've yielded while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cYields; /** Avg. time spend oversleeping when yielding. */ uint32_t cYieldTooLongNSAvg; /** Total time spend oversleeping when yielding. */ uint64_t cYieldTooLongNS; /** Total time spent yielding. */ uint64_t cYieldedNS; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastYieldTS; /** When we started spinning relentlessly in order to catch up some of the oversleeping. */ uint64_t u64StartSpinTS; } Method34; #endif } Halt; /** Profiling the halted state; yielding vs blocking. * @{ */ STAMPROFILE StatHaltYield; STAMPROFILE StatHaltBlock; STAMPROFILE StatHaltTimers; STAMPROFILE StatHaltPoll; /** @} */ /** List of registered reset callbacks. */ PVMATRESET pAtReset; /** List of registered reset callbacks. */ PVMATRESET *ppAtResetNext; /** List of registered state change callbacks. */ PVMATSTATE pAtState; /** List of registered state change callbacks. */ PVMATSTATE *ppAtStateNext; /** List of registered error callbacks. */ PVMATERROR pAtError; /** List of registered error callbacks. */ PVMATERROR *ppAtErrorNext; /** List of registered error callbacks. */ PVMATRUNTIMEERROR pAtRuntimeError; /** List of registered error callbacks. */ PVMATRUNTIMEERROR *ppAtRuntimeErrorNext; /** Pointer to the DBGC instance data. */ void *pvDBGC; /** TLS index for the VMINTUSERPERVMCPU pointer. */ RTTLS idxTLS; } VMINTUSERPERVM; /** Pointer to the VM internal data kept in the UVM. */ typedef VMINTUSERPERVM *PVMINTUSERPERVM; /** * VMCPU internal data kept in the UVM. * * Almost a copy of VMINTUSERPERVM. Separate data properly later on. */ typedef struct VMINTUSERPERVMCPU { /** Head of the request queue. Atomic. */ volatile PVMREQ pReqs; /** The handle to the EMT thread. */ RTTHREAD ThreadEMT; /** The native of the EMT thread. */ RTNATIVETHREAD NativeThreadEMT; /** Wait event semaphore. */ RTSEMEVENT EventSemWait; /** Wait/Idle indicator. */ bool volatile fWait; /** Force EMT to terminate. */ bool volatile fTerminateEMT; /** If set the EMT does the final VM cleanup when it exits. * If clear the VMR3Destroy() caller does so. */ bool fEMTDoesTheCleanup; /** @name Generic Halt data * @{ */ /** The current halt method. * Can be selected by CFGM option 'VM/HaltMethod'. */ VMHALTMETHOD enmHaltMethod; /** The index into g_aHaltMethods of the current halt method. */ uint32_t volatile iHaltMethod; /** The average time (ns) between two halts in the last second. (updated once per second) */ uint32_t HaltInterval; /** The average halt frequency for the last second. (updated once per second) */ uint32_t HaltFrequency; /** The number of halts in the current period. */ uint32_t cHalts; uint32_t padding; /**< alignment padding. */ /** When we started counting halts in cHalts (RTTimeNanoTS). */ uint64_t u64HaltsStartTS; /** @} */ /** Union containing data and config for the different halt algorithms. */ union { /** * Method 1 & 2 - Block whenever possible, and when lagging behind * switch to spinning with regular blocking every 5-200ms (defaults) * depending on the accumulated lag. The blocking interval is adjusted * with the average oversleeping of the last 64 times. * * The difference between 1 and 2 is that we use native absolute * time APIs for the blocking instead of the millisecond based IPRT * interface. */ struct { /** How many times we've blocked while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cBlocks; /** Avg. time spend oversleeping when blocking. (Re-calculated every so often.) */ uint64_t cNSBlockedTooLongAvg; /** Total time spend oversleeping when blocking. */ uint64_t cNSBlockedTooLong; /** Total time spent blocking. */ uint64_t cNSBlocked; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastBlockTS; /** When we started spinning relentlessly in order to catch up some of the oversleeping. * This is 0 when we're not spinning. */ uint64_t u64StartSpinTS; /** The max interval without blocking (when spinning). */ uint32_t u32MinBlockIntervalCfg; /** The minimum interval between blocking (when spinning). */ uint32_t u32MaxBlockIntervalCfg; /** The value to divide the current lag by to get the raw blocking interval (when spinning). */ uint32_t u32LagBlockIntervalDivisorCfg; /** When to start spinning (lag / nano secs). */ uint32_t u32StartSpinningCfg; /** When to stop spinning (lag / nano secs). */ uint32_t u32StopSpinningCfg; } Method12; #if 0 /** * Method 3 & 4 - Same as method 1 & 2 respectivly, except that we * sprinkle it with yields. */ struct { /** How many times we've blocked while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cBlocks; /** Avg. time spend oversleeping when blocking. (Re-calculated every so often.) */ uint64_t cBlockedTooLongNSAvg; /** Total time spend oversleeping when blocking. */ uint64_t cBlockedTooLongNS; /** Total time spent blocking. */ uint64_t cBlockedNS; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastBlockTS; /** How many times we've yielded while cBlockedNS and cBlockedTooLongNS has been accumulating. */ uint32_t cYields; /** Avg. time spend oversleeping when yielding. */ uint32_t cYieldTooLongNSAvg; /** Total time spend oversleeping when yielding. */ uint64_t cYieldTooLongNS; /** Total time spent yielding. */ uint64_t cYieldedNS; /** The timestamp (RTTimeNanoTS) of the last block. */ uint64_t u64LastYieldTS; /** When we started spinning relentlessly in order to catch up some of the oversleeping. */ uint64_t u64StartSpinTS; } Method34; #endif } Halt; /** Profiling the halted state; yielding vs blocking. * @{ */ STAMPROFILE StatHaltYield; STAMPROFILE StatHaltBlock; STAMPROFILE StatHaltTimers; STAMPROFILE StatHaltPoll; /** @} */ /** Pointer to the DBGC instance data. */ void *pvDBGC; /** vmR3EmulationThread longjmp buffer. Must be last in the structure. */ jmp_buf emtJumpEnv; } VMINTUSERPERVMCPU; /** Pointer to the VM internal data kept in the UVM. */ typedef VMINTUSERPERVMCPU *PVMINTUSERPERVMCPU; DECLCALLBACK(int) vmR3EmulationThread(RTTHREAD ThreadSelf, void *pvArg); int vmR3SetHaltMethodU(PUVM pUVM, VMHALTMETHOD enmHaltMethod); DECLCALLBACK(int) vmR3Destroy(PVM pVM); DECLCALLBACK(void) vmR3SetErrorUV(PUVM pUVM, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list *args); void vmSetErrorCopy(PVM pVM, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list args); DECLCALLBACK(void) vmR3SetRuntimeErrorV(PVM pVM, bool fFatal, const char *pszErrorID, const char *pszFormat, va_list *args); void vmSetRuntimeErrorCopy(PVM pVM, bool fFatal, const char *pszErrorID, const char *pszFormat, va_list args); void vmR3DestroyFinalBitFromEMT(PUVM pUVM); void vmR3SetState(PVM pVM, VMSTATE enmStateNew); /** @} */ #endif