/* $Id: VBoxGuestInternal.h $ */
/** @file
 * VBoxGuest - Guest Additions Driver, Internal Header.
 */

/*
 * Copyright (C) 2010-2025 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */

#ifndef GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h
#define GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include <iprt/types.h>
#include <iprt/list.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <iprt/timer.h>
#include <VBox/VMMDev.h>
#include <VBox/VBoxGuest.h>
#include <VBox/VBoxGuestLib.h>

/** @def VBOXGUEST_USE_DEFERRED_WAKE_UP
 * Defer wake-up of waiting thread when defined. */
#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
# define VBOXGUEST_USE_DEFERRED_WAKE_UP
#endif

/** @def VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT
 * The mouse notification callback can cause preemption and must not be invoked
 * while holding a high-level spinlock.
 */
#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
# define VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT
#endif

/** Pointer to the VBoxGuest per session data. */
typedef struct VBOXGUESTSESSION *PVBOXGUESTSESSION;

/** Pointer to a wait-for-event entry. */
typedef struct VBOXGUESTWAIT *PVBOXGUESTWAIT;

/**
 * VBox guest wait for event entry.
 *
 * Each waiting thread allocates one of these items and adds
 * it to the wait list before going to sleep on the event sem.
 */
typedef struct VBOXGUESTWAIT
{
    /** The list node. */
    RTLISTNODE                  ListNode;
    /** The events we are waiting on. */
    uint32_t                    fReqEvents;
    /** The events we received. */
    uint32_t volatile           fResEvents;
#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
    /** Set by VGDrvCommonWaitDoWakeUps before leaving the spinlock to call
     *  RTSemEventMultiSignal. */
    bool volatile               fPendingWakeUp;
    /** Set by the requestor thread if it got the spinlock before the
     * signaller.  Deals with the race in VGDrvCommonWaitDoWakeUps. */
    bool volatile               fFreeMe;
#endif
    /** The event semaphore. */
    RTSEMEVENTMULTI             Event;
    /** The session that's waiting. */
    PVBOXGUESTSESSION           pSession;
#ifdef VBOX_WITH_HGCM
    /** The HGCM request we're waiting for to complete. */
    VMMDevHGCMRequestHeader volatile *pHGCMReq;
#endif
} VBOXGUESTWAIT;


/**
 * VBox guest memory balloon.
 */
typedef struct VBOXGUESTMEMBALLOON
{
    /** Mutex protecting the members below from concurrent access. */
    RTSEMFASTMUTEX              hMtx;
    /** The current number of chunks in the balloon. */
    uint32_t                    cChunks;
    /** The maximum number of chunks in the balloon (typically the amount of guest
     * memory / chunksize). */
    uint32_t                    cMaxChunks;
    /** This is true if we are using RTR0MemObjAllocPhysNC() / RTR0MemObjGetPagePhysAddr()
     * and false otherwise. */
    bool                        fUseKernelAPI;
    /** The current owner of the balloon.
     * This is automatically assigned to the first session using the ballooning
     * API and first released when the session closes. */
    PVBOXGUESTSESSION           pOwner;
    /** The pointer to the array of memory objects holding the chunks of the
     *  balloon.  This array is cMaxChunks in size when present. */
    PRTR0MEMOBJ                 paMemObj;
} VBOXGUESTMEMBALLOON;
/** Pointer to a memory balloon. */
typedef VBOXGUESTMEMBALLOON *PVBOXGUESTMEMBALLOON;


/**
 * Per bit usage tracker for a uint32_t mask.
 *
 * Used for optimal handling of guest properties, mouse status and event filter.
 */
typedef struct VBOXGUESTBITUSAGETRACER
{
    /** Per bit usage counters. */
    uint32_t        acPerBitUsage[32];
    /** The current mask according to acPerBitUsage. */
    uint32_t        fMask;
} VBOXGUESTBITUSAGETRACER;
/** Pointer to a per bit usage tracker.  */
typedef VBOXGUESTBITUSAGETRACER *PVBOXGUESTBITUSAGETRACER;
/** Pointer to a const per bit usage tracker.  */
typedef VBOXGUESTBITUSAGETRACER const *PCVBOXGUESTBITUSAGETRACER;


/**
 * VBox guest device (data) extension.
 */
typedef struct VBOXGUESTDEVEXT
{
    /** VBOXGUESTDEVEXT_INIT_STATE_XXX.   */
    uint32_t                    uInitState;
    /** The base of the adapter I/O ports. */
    RTIOPORT                    IOPortBase;
    /** Pointer to the mapping of the MMIO request region if available. */
    uintptr_t volatile         *pMmioReq;
    /** Pointer to the fast request register in the MMIO region if available. */
    uint32_t volatile          *pMmioReqFast;
    /** Pointer to the mapping of the VMMDev adapter memory. */
    VMMDevMemory volatile      *pVMMDevMemory;
    /** The memory object reserving space for the guest mappings. */
    RTR0MEMOBJ                  hGuestMappings;
    /** Spinlock protecting the signaling and resetting of the wait-for-event
     * semaphores as well as the event acking in the ISR. */
    RTSPINLOCK                  EventSpinlock;
    /** Host feature flags (VMMDEV_HVF_XXX).   */
    uint32_t                    fHostFeatures;
    /** Preallocated VMMDevEvents for the IRQ handler. */
    VMMDevEvents               *pIrqAckEvents;
    /** The physical address of pIrqAckEvents. */
    RTCCPHYS                    PhysIrqAckEvents;
    /** Wait-for-event list for threads waiting for multiple events
     * (VBOXGUESTWAIT). */
    RTLISTANCHOR                WaitList;
#ifdef VBOX_WITH_HGCM
    /** Wait-for-event list for threads waiting on HGCM async completion
     * (VBOXGUESTWAIT).
     *
     * The entire list is evaluated upon the arrival of an HGCM event, unlike
     * the other lists which are only evaluated till the first thread has
     * been woken up. */
    RTLISTANCHOR                HGCMWaitList;
#endif
#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
    /** List of wait-for-event entries that needs waking up
     * (VBOXGUESTWAIT). */
    RTLISTANCHOR                WakeUpList;
#endif
    /** List of wait-for-event entries that has been woken up
     * (VBOXGUESTWAIT). */
    RTLISTANCHOR                WokenUpList;
    /** List of free wait-for-event entries (VBOXGUESTWAIT). */
    RTLISTANCHOR                FreeList;
    /** Mask of pending events. */
    uint32_t volatile           f32PendingEvents;
    /** Current VMMDEV_EVENT_MOUSE_POSITION_CHANGED sequence number.
     * Used to implement polling.  */
    uint32_t volatile           u32MousePosChangedSeq;

    /** Spinlock various items in the VBOXGUESTSESSION. */
    RTSPINLOCK                  SessionSpinlock;
    /** List of guest sessions (VBOXGUESTSESSION).  We currently traverse this
     * but do not search it, so a list data type should be fine.  Use under the
     * #SessionSpinlock lock. */
    RTLISTANCHOR                SessionList;
    /** Number of session. */
    uint32_t                    cSessions;
    /** Flag indicating whether logging to the release log
     *  is enabled. */
    bool                        fLoggingEnabled;
    /** Memory balloon information for RTR0MemObjAllocPhysNC(). */
    VBOXGUESTMEMBALLOON         MemBalloon;
    /** Mouse notification callback function. */
    PFNVBOXGUESTMOUSENOTIFY     pfnMouseNotifyCallback;
    /** The callback argument for the mouse ntofication callback. */
    void                       *pvMouseNotifyCallbackArg;

    /** @name Host Event Filtering
     * @{ */
    /** Events we won't permit anyone to filter out. */
    uint32_t                    fFixedEvents;
    /** Usage counters for the host events. (Fixed events are not included.) */
    VBOXGUESTBITUSAGETRACER     EventFilterTracker;
    /** The event filter last reported to the host (UINT32_MAX on failure). */
    uint32_t                    fEventFilterHost;
    /** @} */

    /** @name Mouse Status
     * @{ */
    /** Usage counters for the mouse statuses (VMMDEV_MOUSE_XXX). */
    VBOXGUESTBITUSAGETRACER     MouseStatusTracker;
    /** The mouse status last reported to the host (UINT32_MAX on failure). */
    uint32_t                    fMouseStatusHost;
    /** @} */

    /** @name Guest Capabilities
     * @{ */
    /** Guest capabilities which have been set to "acquire" mode.  This means
     * that only one session can use them at a time, and that they will be
     * automatically cleaned up if that session exits without doing so.
     *
     * Protected by VBOXGUESTDEVEXT::SessionSpinlock, but is unfortunately read
     * without holding the lock in a couple of places. */
    uint32_t volatile           fAcquireModeGuestCaps;
    /** Guest capabilities which have been set to "set" mode.  This just means
     * that they have been blocked from ever being set to "acquire" mode. */
    uint32_t                    fSetModeGuestCaps;
    /** Mask of all capabilities which are currently acquired by some session
     * and as such reported to the host. */
    uint32_t                    fAcquiredGuestCaps;
    /** Usage counters for guest capabilities in "set" mode. Indexed by
     *  capability bit number, one count per session using a capability. */
    VBOXGUESTBITUSAGETRACER     SetGuestCapsTracker;
    /** The guest capabilities last reported to the host (UINT32_MAX on failure). */
    uint32_t                    fGuestCapsHost;
    /** @} */

    /** Heartbeat timer which fires with interval
      * cNsHearbeatInterval and its handler sends
      * VMMDevReq_GuestHeartbeat to VMMDev. */
    PRTTIMER                    pHeartbeatTimer;
    /** Heartbeat timer interval in nanoseconds. */
    uint64_t                    cNsHeartbeatInterval;
    /** Preallocated VMMDevReq_GuestHeartbeat request. */
    VMMDevRequestHeader         *pReqGuestHeartbeat;
} VBOXGUESTDEVEXT;
/** Pointer to the VBoxGuest driver data. */
typedef VBOXGUESTDEVEXT *PVBOXGUESTDEVEXT;

/** @name VBOXGUESTDEVEXT_INIT_STATE_XXX - magic values for validating init
 *        state of the device extension structur.
 * @{ */
#define VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT        UINT32_C(0x0badcafe)
#define VBOXGUESTDEVEXT_INIT_STATE_RESOURCES        UINT32_C(0xcafebabe)
#define VBOXGUESTDEVEXT_INIT_STATE_DELETED          UINT32_C(0xdeadd0d0)
/** @} */

/**
 * The VBoxGuest per session data.
 */
typedef struct VBOXGUESTSESSION
{
    /** The list node. */
    RTLISTNODE                  ListNode;
#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
    /** Pointer to the next session with the same hash. */
    PVBOXGUESTSESSION           pNextHash;
#endif
#if defined(RT_OS_OS2)
    /** The system file number of this session. */
    uint16_t                    sfn;
    uint16_t                    Alignment; /**< Alignment */
#endif
    /** The requestor information to pass to the host for this session.
     * @sa VMMDevRequestHeader::fRequestor */
    uint32_t                    fRequestor;
    /** The process (id) of the session.
     * This is NIL if it's a kernel session. */
    RTPROCESS                   Process;
    /** Which process this session is associated with.
     * This is NIL if it's a kernel session. */
    RTR0PROCESS                 R0Process;
    /** Pointer to the device extension. */
    PVBOXGUESTDEVEXT            pDevExt;

#ifdef VBOX_WITH_HGCM
    /** Array containing HGCM client IDs associated with this session.
     * This will be automatically disconnected when the session is closed. */
    uint32_t volatile           aHGCMClientIds[64];
#endif
    /** The last consumed VMMDEV_EVENT_MOUSE_POSITION_CHANGED sequence number.
     * Used to implement polling.  */
    uint32_t volatile           u32MousePosChangedSeq;
    /** Host events requested by the session.
     * An event type requested in any guest session will be added to the host
     * filter.  Protected by VBOXGUESTDEVEXT::SessionSpinlock. */
    uint32_t                    fEventFilter;
    /** Guest capabilities held in "acquired" by this session.
     * Protected by VBOXGUESTDEVEXT::SessionSpinlock, but is unfortunately read
     * without holding the lock in a couple of places. */
    uint32_t volatile           fAcquiredGuestCaps;
    /** Guest capabilities in "set" mode for this session.
     * These accumulated for sessions via VBOXGUESTDEVEXT::acGuestCapsSet and
     * reported to the host.  Protected by VBOXGUESTDEVEXT::SessionSpinlock.  */
    uint32_t                    fCapabilities;
    /** Mouse features supported.  A feature enabled in any guest session will
     * be enabled for the host.
     * @note We invert the VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR feature in this
     * bitmap.  The logic of this is that the real feature is when the host
     * cursor is not needed, and we tell the host it is not needed if any
     * session explicitly fails to assert it.  Storing it inverted simplifies
     * the checks.
     * Use under the VBOXGUESTDEVEXT#SessionSpinlock lock. */
    uint32_t                    fMouseStatus;
#ifdef RT_OS_DARWIN
    /** Pointer to the associated org_virtualbox_VBoxGuestClient object. */
    void                       *pvVBoxGuestClient;
    /** Whether this session has been opened or not. */
    bool                        fOpened;
#endif
    /** Whether a CANCEL_ALL_WAITEVENTS is pending.  This happens when
     * CANCEL_ALL_WAITEVENTS is called, but no call to WAITEVENT is in process
     * in the current session.  In that case the next call will be interrupted
     * at once. */
    bool volatile               fPendingCancelWaitEvents;
    /** Does this session belong to a root process or a user one? */
    bool                        fUserSession;
} VBOXGUESTSESSION;

RT_C_DECLS_BEGIN

int  VGDrvCommonInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, void *pvMmioReq,
                           void *pvMMIOBase, uint32_t cbMMIO,
                           VBOXOSTYPE enmOSType, uint32_t fEvents);
void VGDrvCommonDeleteDevExt(PVBOXGUESTDEVEXT pDevExt);

int  VGDrvCommonInitLoggers(void);
void VGDrvCommonDestroyLoggers(void);
int  VGDrvCommonInitDevExtFundament(PVBOXGUESTDEVEXT pDevExt);
void VGDrvCommonDeleteDevExtFundament(PVBOXGUESTDEVEXT pDevExt);
int  VGDrvCommonInitDevExtResources(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase,
                                    void *pvMmioReq, void *pvMMIOBase, uint32_t cbMMIO,
                                    VBOXOSTYPE enmOSType, uint32_t fFixedEvents);
void VGDrvCommonDeleteDevExtResources(PVBOXGUESTDEVEXT pDevExt);
int  VGDrvCommonReinitDevExtAfterHibernation(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType);

bool VBDrvCommonIsOptionValueTrue(const char *pszValue);
void VGDrvCommonProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue);
void VGDrvCommonProcessOptionsFromHost(PVBOXGUESTDEVEXT pDevExt);
bool VGDrvCommonIsOurIRQ(PVBOXGUESTDEVEXT pDevExt);
bool VGDrvCommonISR(PVBOXGUESTDEVEXT pDevExt);

#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP
void VGDrvCommonWaitDoWakeUps(PVBOXGUESTDEVEXT pDevExt);
#endif

int  VGDrvCommonCreateUserSession(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession);
int  VGDrvCommonCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession);
void VGDrvCommonCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession);

int  VGDrvCommonIoCtlFast(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession);
int  VGDrvCommonIoCtl(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
                      PVBGLREQHDR pReqHdr, size_t cbReq);

/**
 * ISR callback for notifying threads polling for mouse events.
 *
 * This is called at the end of the ISR, after leaving the event spinlock, if
 * VMMDEV_EVENT_MOUSE_POSITION_CHANGED was raised by the host.
 *
 * @param   pDevExt     The device extension.
 */
void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt);

/**
 * Hook for handling OS specfic options from the host.
 *
 * @returns true if handled, false if not.
 * @param   pDevExt         The device extension.
 * @param   pszName         The option name.
 * @param   pszValue        The option value.
 */
bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue);


#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
int VGDrvNtIOCtl_DpcLatencyChecker(void);
#endif

#ifdef VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT
int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify);
#endif

RT_C_DECLS_END

#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h */

