/* $Id: VMInternal.h 20374 2009-06-08 00:43:21Z 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 <VBox/cdefs.h>
#include <VBox/vmapi.h>
#include <setjmp.h>



/** @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;
    /** Error flags. */
    uint32_t                        fFlags;
} 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;

    /** 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;

    /** 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;

    /** @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;
    /** @} */

    /** @todo Do NOT add new members here or resue the current, we need to store the config for
     *  each halt method seperately because we're racing on SMP guest rigs. */
    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
        {
            /** 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;
    }                               Halt;

    /** 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 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;
        }                           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;
    /** @} */
} VMINTUSERPERVMCPU;

/** Pointer to the VM internal data kept in the UVM. */
typedef VMINTUSERPERVMCPU *PVMINTUSERPERVMCPU;

RT_C_DECLS_BEGIN

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(int)   vmR3SetRuntimeErrorV(PVM pVM, uint32_t fFlags, const char *pszErrorId, const char *pszFormat, va_list *pVa);
void                vmSetRuntimeErrorCopy(PVM pVM, uint32_t fFlags, const char *pszErrorId, const char *pszFormat, va_list va);
void                vmR3DestroyFinalBitFromEMT(PUVM pUVM);
void                vmR3SetState(PVM pVM, VMSTATE enmStateNew);

RT_C_DECLS_END


/** @} */

#endif

