/*++

Copyright (c) 2005 Microsoft Corporation All Rights Reserved

Module Name:

    VidDefs.h

Abstract:

    This module contains the common definitions for Vid.sys shared by 
    the driver, user mode Vid clients and kernel mode Vid clients.

Author:

    John Gregg (jgregg) 06-Jun-2005

--*/

#pragma once

#include <hvgdk.h>

//
// Define an interface Guid for the VID device class.
//
// This Guid is used to register an instance so the the VID node is 
// by VID (both user mode and kernel mode) clients.
//
// {7896e901-fe60-446e-828d-d65920654a23}
//
DEFINE_GUID(GUID_DEVICEINTERFACE_VID,
                  0x7896e901, 0xfe60, 0x446e, 0x82, 0x8d, 
                  0xd6, 0x59, 0x20, 0x65, 0x4a, 0x23);


//
// The maximum size, in characters, of a Vid object name (Partition or Message 
// Queue).
//
#define VID_MAX_OBJECT_NAME_CHAR_COUNT 256


//
// The maximum number of virtual processors supported on a Partition.
//
#define VID_MAX_PROCESSORS_PER_PARTITION ((VID_PROCESSOR_INDEX)(HV_MAX_VP_INDEX + 1))


// 
//
// A partition handle and a queue handle are handles to a file object 
// returned from the Win32 function CreateFile).
//
typedef HANDLE PT_HANDLE;
typedef HANDLE QUEUE_HANDLE;
typedef UINT32 VID_PHYSICAL_NODE_INDEX, *PVID_PHYSICAL_NODE_INDEX;
typedef UINT32 VID_LOGICAL_PROCESSOR_INDEX, *PVID_LOGICAL_PROCESSOR_INDEX;


//
// This structure contain both static and dynamic information about a single 
// NUMA node. When a user mode client queries the VID about a single NUMA 
// node, the node's information is returned within this structure.
//
typedef struct _VID_NUMA_NODE_INFORMATION
{
    //
    // The NUMA node index that identifies the node.
    //
    VID_PHYSICAL_NODE_INDEX NodeIndex;

    //
    // The total number of parent GPA pages that are local to this node. 
    //
    UINT64 TotalPageCount;

    //
    // The number of parent GPA pages that are local to this node and that can 
    // be used for backing the minimum working set of a newly created Memory 
    // Block. Note that the user mode client should treat this number only as 
    // an estmiation, because we don't actually allocate the free pages when 
    // the client queries for the available node memory.
    //
    UINT64 FreePageCount;

    //
    // The number of logical processors contained within the node.
    //
    UINT32 LogicalProcessorCount;    

    //
    // A bit mask that represents the logical processors on the node. The 
    // number of set bits in this mask equal LogicalProcessorCount.
    //
    UINT64 LogicalProcessorMask;    
} VID_NUMA_NODE_INFORMATION, *PVID_NUMA_NODE_INFORMATION;

//
// ISSUE-jgregg-2006/8/2: Temporary code to enabled statistics access.
//
typedef enum _VID_NUMA_NODE_STAT_PROPERTY
{
    PageCount,
    PagesInUse,
    SharedParentGpaPageCount
} VID_NUMA_NODE_STAT_PROPERTY,
*PVID_NUMA_NODE_STAT_PROPERTY;

#define VID_PAGE_SIZE 4096

//
// The following are internal VID handles that don't correspond to Win32 file 
// handles.
//
typedef UINT64 HANDLER_REF;
typedef PVOID TIMER_REF;
typedef PVOID MB_HANDLE;
typedef PVOID RG_HANDLE;


#define INVALID_HANDLER_REF (HANDLER_REF)(-1)
#define INVALID_TIMER_REF (TIMER_REF)(-1)
#define INVALID_MB_HANDLE (MB_HANDLE)(-1)
#define INVALID_RG_HANDLE (RG_HANDLE)(-1)

//
// VID public types and enumerations.
//
typedef UINT16  VID_IO_PORT_ADDRESS;
typedef UINT32  VID_MSR_ADDRESS;
typedef UINT32  VID_PROCESSOR_INDEX;

#define VID_ALL_PROCESSORS 0xFFFF

typedef enum _VID_PROCESSOR_STATUS
{
    VidProcessorStatusStopped = 0x0,
    VidProcessorStatusRunning,
    VidProcessorStatusSuspended,

    VidProcessorStatusUndefined = 0xFFFF
} VID_PROCESSOR_STATUS;

//
// Types used to get and set Vp state.  These types
// currently map 1-to-1 with hypervisor registers, but allow
// the possibility of future VID state.
//
typedef HV_REGISTER_NAME VID_VP_STATE_CODE;
typedef HV_REGISTER_VALUE VID_VP_STATE_VALUE;

//
// Flags used by VidSetupPartition
//
//  ..._ENABLE_DEBUGGING - Enable Viridian Unified Debugging (VUD) (see
//      hypervisor TLFS).
//
//  ..._EXPOSE_HYPERTHREADING - Expose hyperthreading (see hypervisor
//      TLFS).
//
#define VID_PARTITION_PROPERTY_FLAG_ENABLE_DEBUGGING        (1 << 0)
#define VID_PARTITION_PROPERTY_FLAG_EXPOSE_HYPERTHREADING   (1 << 1)

//
// Flags used by VidCreateMessageQueue
//
#define VID_QUEUE_ORDER_NOT_ENFORCED  (1 << 0)

//
// Flags used by VidHandleAndGetNextMessage()
//
#define VID_GET_NEXT_MESSAGE          (1 << 0)
#define VID_HANDLE_MESSAGE            (1 << 1)

//
// The maximum number of threads which can be concurrently handling
// messages on one queue.
//
// The max concurrency is limited by the 32-bit bitmasks used in the dispatch
// interface to manage the exchange buffer.
//
#define VID_QUEUE_MAX_CONCURRENCY \
    (32)

//
// Flags used by VidRegisterXxxHandler()
//
#define VID_HANDLER_READ              (1 << 0)
#define VID_HANDLER_WRITE             (1 << 1)
#define VID_HANDLER_IO_PORT_8BIT      (1 << 2)
#define VID_HANDLER_IO_PORT_16BIT     (1 << 3)
#define VID_HANDLER_IO_PORT_32BIT     (1 << 4)
#define VID_HANDLER_CATCH_ALL         (1 << 5)

//
// VSMM related public definitions.
//
typedef UINT64 MB_PAGE_INDEX;
typedef UINT64 GUEST_PHYSICAL_PAGE_INDEX;
typedef UINT64 GUEST_PHYSICAL_ADDRESS;

typedef enum _VID_ACCESS_TYPE
{
    VidAccessInvalid = 0x00,
    VidAccessReadOnly,
    VidAccessReadWrite
} VID_ACCESS_TYPE;

//
// This structure describes a sequence of pages for bulk
// locking and unlocking.
//
typedef struct _VID_BULK_LOCK_RANGE
{
    UINT32      ByteCount;
    UINT32      ByteOffset;
    UINT64      PfnArray[1];
} VID_BULK_LOCK_RANGE, *PVID_BULK_LOCK_RANGE;

//
// Flags values used in VidCreateMemoryBlockGpaRange().
// 
#define VID_ROM_GPA_PAGE_RANGE                  0x0000000000000001
#define VID_MEMORY_BLOCK_PRIMARY_GPA_PAGE_RANGE 0x0000000000000002


//
// Value used to limit the number of PPMs which can be destroyed in a
// single VID call.
// 
#define VID_MAX_PPM_DESTROY_COUNT 256

//
// Flags values used in VidSetMemoryBlockClientNotifications().
// 
#define VID_MB_READ_NOTIFY       0x00000001
#define VID_MB_WRITE_NOTIFY      0x00000002

#define VID_APPLY_READ_SETTINGS  0x00010000
#define VID_APPLY_WRITE_SETTINGS 0x00020000


//
// Flags values used in VidCreateMemoryBlock. These flags can be set 
// explicitly by the client when VidCreateMemoryBlock is invoked or implicitly 
// by the VSMM.
//
#define VID_FLAG_DO_NOT_SHARE_MBPS                              (UINT64)0x1
#define VID_FLAG_ALLOW_NOTIFICATIONS_ON_LOCKED_MBPS             (UINT64)0x2
#define VID_FLAG_DO_NOT_BACK_OVER_COMMIT_MBPS_WITH_SHARED_PAGES (UINT64)0x4
#define VID_FLAG_DO_NOT_BACK_OVER_COMMIT_MBPS_WITH_MM_PAGES     (UINT64)0x8


//
// Structures and definitions used by the Vid clients for handling / receiving 
// messages from the driver.
//
typedef UINT64 VID_MESSAGE_ID;
typedef LONG VID_MSG_STATUS_CODE;


#define EXCEPTION_INSTRUCTION_SIZE 16


//
// The largest batch read/write INS or OUTS that will
// be sent with a single message.  Larger guest operations
// will be sent using multiple messages.
//
#define VID_MAX_IO_STRING (4096)


//
// VID_MESSAGE_TYPE_MASK
//

#define     VID_MESSAGE_TYPE_MASK               0x00FFFFFFUL
#define     VID_MESSAGE_TYPE_FLAG_NO_RETURN     0x01000000UL
#define     VID_MESSAGE_TYPE_FLAG_INTERCEPT     0x02000000UL

//
// VID_MESSAGE_TYPE identifies a message embedded within the VID_MSG_DATA 
// structure.
//
// Note:  This must be kep in sync with the VID message type information array.
//
typedef enum _VID_MESSAGE_TYPE
{
    VidMessageInvalid               = 0x0000,

    //
    // Message types corresponding to hypervisor intercepts.
    //
    VidMessageCpuid                 = 0x0001 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageMmio                  = 0x0002 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageMbpAccess             = 0x0003,
    VidMessageMsr                   = 0x0004 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageIoPort                = 0x0005 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageException             = 0x0006 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageTripleFault           = 0x0007 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageApicEoi               = 0x0008 | VID_MESSAGE_TYPE_FLAG_INTERCEPT,
    VidMessageTimerExpired          = 0x0009,
    VidMessageLegacyFpError         = 0x000A | VID_MESSAGE_TYPE_FLAG_INTERCEPT,

    //
    // Message types corresponding to other monitored events.  These do not
    // require a return message.
    //
    VidMessageHandlerUnregistered   = 0x000B | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
    VidMessageStopRequestComplete   = 0x000C | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
    VidMessageSingleStepComplete    = 0x000D | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
    VidMessageBreakpointHit         = 0x000E | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
    VidMessageMmioRangeDestroyed    = 0x000F | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
    VidMessageHvMemoryPolicy        = 0x0010 | VID_MESSAGE_TYPE_FLAG_NO_RETURN,

    // Must be last
    VidMessageTypeCount             = 0x0011

} VID_MESSAGE_TYPE;

//
// Message structures for CPUID.
//
typedef struct _VID_MSG_DATA_CPUID
{
    UINT32 CpuidFunction;
    
    //
    // The values in the registers when CPUID was executed.
    //
    UINT64 Rax;
    UINT64 Rbx;
    UINT64 Rdx;
    UINT64 Rcx;
    
    //
    // The values suggested by the hypervisor as the default
    // ones to be placed in the registers by CPUID.
    //
    UINT64 RaxDefaultReturn;
    UINT64 RbxDefaultReturn;
    UINT64 RdxDefaultReturn;
    UINT64 RcxDefaultReturn;
} VID_MSG_DATA_CPUID, *PVID_MSG_DATA_CPUID;

typedef struct _VID_MSG_RETURN_DATA_CPUID
{
    //
    // The values to be placed in the registers in order to
    // complete the CPUID instruction.
    //
    UINT64 Rax;
    UINT64 Rbx;
    UINT64 Rdx;
    UINT64 Rcx;
} VID_MSG_RETURN_DATA_CPUID, *PVID_MSG_RETURN_DATA_CPUID;

//
// The maximum size, in bytes, of an MMIO read/write. This value dictates the 
// size of the VID message used for passing MMIO data.
//
#define VID_MAX_MMIO_ACCESS_SIZE 16


typedef struct _VID_MSG_DATA_MMIO
{
    GUEST_PHYSICAL_ADDRESS Gpa;
    UINT32 DataSize;
    BOOLEAN AccessIsWrite;

    BYTE WriteData[VID_MAX_MMIO_ACCESS_SIZE];

    //
    // If this field is set, the VID will allow the client to complete
    // the entire instruction and return InstructionCompleted = TRUE in
    // the return message.
    //
    BOOLEAN InstructionCompletionAllowed;
} VID_MSG_DATA_MMIO, *PVID_MSG_DATA_MMIO;


typedef struct _VID_MSG_DATA_MMIO_RANGE_DESTROYED
{
    UINT8 Reserved;
} VID_MSG_DATA_MMIO_RANGE_DESTROYED, *PVID_MSG_DATA_MMIO_RANGE_DESTROYED;


typedef struct _VID_MSG_RETURN_DATA_MMIO
{
    UINT32 ReadDataSize;
    BYTE ReadData[VID_MAX_MMIO_ACCESS_SIZE];

    //
    // If this field is set in the return data, the VID will not take
    // further action on this intercept but will simply complete it and
    // cause VP execution to resume.  This indicates the client has
    // completely handled the instruction and the VP state is already correct
    // for re-execution.
    // 
    BOOLEAN InstructionCompleted;
} VID_MSG_RETURN_DATA_MMIO, *PVID_MSG_RETURN_DATA_MMIO;

//
// Define the various reasons that can trigger an MBP client notification to 
// be sent to user mode.
//
typedef enum _VID_MBP_NOTIFICATION_REASON
{
    //
    // Notifications triggered when a kernel mode client locks an MBP for 
    // either read or read/write.
    //
    VidNotificationReasonLockForRead,
    VidNotificationReasonLockForReadWrite,

    //
    // Notifications triggered when a user mode client maps an MBP for 
    // either read or read/write.
    //
    VidNotificationReasonMapForRead,
    VidNotificationReasonMapForReadWrite,

    //
    // Notifications triggered when a Virtual Processor in a child Partition 
    // accesses a GPA page that maps to the MBP in question.
    //    
    VidNotificationReasonVpRead,
    VidNotificationReasonVpWrite
} VID_MBP_NOTIFICATION_REASON;


//
// Represents a user mode client notification for a Memory Block Page.
//
typedef struct _VID_MSG_DATA_MBP_ACCESS
{
    VID_MBP_NOTIFICATION_REASON NotificationReason;
    
    MB_HANDLE MemoryBlock;
    MB_PAGE_INDEX MbpIndex;

    //
    // The following fields are only used in case the NotificationReason 
    // field is either VidNotificationReasonVpRead or 
    // VidNotificationReasonVpWrite.
    //
    GUEST_PHYSICAL_PAGE_INDEX GpaPageIndex;
} VID_MSG_DATA_MBP_ACCESS, *PVID_MSG_DATA_MBP_ACCESS;


typedef struct _VID_MSG_DATA_MSR
{
    UINT32 MsrAddress;
    BOOLEAN AccessIsWrite;

    union 
    {
        struct 
        {
            UINT32 LowPart;
            UINT32 HighPart;
        } u;

        UINT64 QuadPart;
    } WriteData;
} VID_MSG_DATA_MSR, *PVID_MSG_DATA_MSR;


typedef union _VID_MSG_RETURN_DATA_MSR
{
    struct 
    {
        UINT32 LowPart;
        UINT32 HighPart;
    } u;

    UINT64 QuadPart;
} VID_MSG_RETURN_DATA_MSR, *PVID_MSG_RETURN_DATA_MSR;


typedef struct _VID_MSG_DATA_IOPORT
{
    UINT16 PortAddress;
    UINT16 AccessSize;
    BOOLEAN AccessIsWrite;
    BOOLEAN AccessIsString;
    UINT16 AccessCount;
    
    //
    // If this field is set, the VID will allow the client to complete
    // the entire instruction and return InstructionCompleted = TRUE in
    // the return message.
    //
    BOOLEAN InstructionCompletionAllowed;

    //
    // Since the data payload is variable length, it should be at the end of
    // the message.
    //
    union 
    {
        UINT32 SingleWrite;
        UINT8 StringWrite[VID_MAX_IO_STRING];
    } WriteData;    

} VID_MSG_DATA_IOPORT, *PVID_MSG_DATA_IOPORT;


typedef struct _VID_MSG_RETURN_DATA_IOPORT
{
    //
    // Sampling data.
    //
    UINT64 MessageReceiptTsc;
    UINT64 MessageReturnTsc;

    UINT16 DataSize;
    UINT16 DataCount;

    //
    // If this field is set in the return data, the VID will not take
    // further action on this intercept but will simply complete it and
    // cause VP execution to resume.  This indicates the client has
    // completely handled the instruction and the VP state is already correct
    // for re-execution.
    // 
    BOOLEAN InstructionCompleted;

    //
    // Since the data payload is variable length, it should be at the end of
    // the message.
    //
    union 
    {
        UINT32 Read;
        UINT8 StringRead[VID_MAX_IO_STRING];
    } Data;

} VID_MSG_RETURN_DATA_IOPORT, *PVID_MSG_RETURN_DATA_IOPORT;


typedef struct _VID_MSG_DATA_EXCEPTION
{
    UINT8 ExceptionVector;
    UINT32 ErrorCode;
    BOOLEAN ErrorCodeValid;
    UINT64 ExceptionParameter;
} VID_MSG_DATA_EXCEPTION, *PVID_MSG_DATA_EXCEPTION;

typedef struct _VID_MSG_RETURN_DATA_EXCEPTION
{
    BOOLEAN AdvanceIp;
} VID_MSG_RETURN_DATA_EXCEPTION, *PVID_MSG_RETURN_DATA_EXCEPTION;

typedef struct _VID_MSG_DATA_TRIPLEFAULT
{
    UINT8 Reserved;
} VID_MSG_DATA_TRIPLEFAULT, *PVID_MSG_DATA_TRIPLEFAULT;

typedef struct _VID_MSG_RETURN_DATA_TRIPLEFAULT
{
    BOOLEAN StopVp;
} VID_MSG_RETURN_DATA_TRIPLEFAULT, *PVID_MSG_RETURN_DATA_TRIPLEFAULT;

typedef struct _VID_MSG_DATA_BREAKPOINT
{
    UINT64 Gva;
} VID_MSG_DATA_BREAKPOINT, *PVID_MSG_DATA_BREAKPOINT;

typedef struct _VID_MSG_DATA_TIMER
{
    UINT64 ExpirationTime;
    UINT64 CurrentReferenceTime;
} VID_MSG_DATA_TIMER, *PVID_MSG_DATA_TIMER;

typedef struct _VID_MSG_RETURN_DATA_TIMER
{
    BOOLEAN ResetTimer;
    UINT64 NewExpirationTime;
} VID_MSG_RETURN_DATA_TIMER, *PVID_MSG_RETURN_DATA_TIMER;


typedef struct _VID_MSG_DATA_HANDLER_UNREGISTER
{
    HANDLER_REF HandlerRef;
} VID_MSG_DATA_HANDLER_UNREGISTER, *PVID_MSG_DATA_HANDLER_UNREGISTER;

typedef struct _VID_MSG_DATA_STOP
{
    UINT8 Reserved;
} VID_MSG_DATA_STOP;

typedef struct _VID_MSG_DATA_SINGLESTEP
{
    UINT64 Rip;
} VID_MSG_DATA_SINGLESTEP;

typedef struct _VID_MSG_DATA_APIC_EOI
{
    HV_INTERRUPT_VECTOR InterruptVector;
} VID_MSG_DATA_APIC_EOI, *PVID_MSG_DATA_APIC_EOI;

typedef struct _VID_MSG_DATA_LEGACY_FP_ERROR
{
    HV_INTERRUPT_VECTOR InterruptVector;
} VID_MSG_DATA_LEGACY_FP_ERROR, *PVID_MSG_DATA_LEGACY_FP_ERROR;

typedef struct _VID_MSG_HV_MEMORY_POLICY
{
    UINT64 CurrentDepositTotalKb;
} VID_MSG_HV_MEMORY_POLICY, *PVID_MSG_HV_MEMORY_POLICY;

//
// Message information returned by the driver to a registered VID client.
//
typedef struct _VID_MSG_DATA
{
    VID_MESSAGE_ID MessageID;
    VID_MESSAGE_TYPE MessageType;
    UINT32 PayloadSizeInBytes;

    //
    // Sampling data.
    //
    UINT64 MessageReceiptTsc;

    PVOID UserContext;      // Used for all message types.
    UINT32 ProcessorIndex;  // Used for most message types.

    union
    {
        VID_MSG_DATA_CPUID Cpuid;
        VID_MSG_DATA_MMIO Mmio;
        VID_MSG_DATA_MBP_ACCESS MbpAccess;
        VID_MSG_DATA_MSR Msr;
        VID_MSG_DATA_IOPORT IoPort;
        VID_MSG_DATA_EXCEPTION Exception;
        VID_MSG_DATA_TRIPLEFAULT TripleFault;
        VID_MSG_DATA_APIC_EOI ApicEoi;
        VID_MSG_DATA_LEGACY_FP_ERROR LegacyFpError;
        VID_MSG_DATA_BREAKPOINT Breakpoint;
        VID_MSG_DATA_HANDLER_UNREGISTER HandlerUnregister;
        VID_MSG_DATA_STOP Stop;
        VID_MSG_DATA_SINGLESTEP SingleStep;
        VID_MSG_DATA_TIMER Timer;
        VID_MSG_DATA_MMIO_RANGE_DESTROYED MmioRangeDestroyed;
        VID_MSG_HV_MEMORY_POLICY HvMemoryPolicy;
    } Data;
} VID_MSG_DATA, *PVID_MSG_DATA;

typedef const PVID_MSG_DATA PCVID_MSG_DATA;

//
// Information sent to the driver by a VID client when handling a message.
//
typedef struct _VID_MSG_RETURN_DATA
{
    VID_MESSAGE_ID MessageID;
    VID_MSG_STATUS_CODE StatusCode;
    UINT32 PayloadSizeInBytes;

    //
    // Sampling data.
    //
    UINT64 MessageReturnTsc;

    union
    {
        VID_MSG_RETURN_DATA_CPUID Cpuid;
        VID_MSG_RETURN_DATA_MMIO Mmio;
        VID_MSG_RETURN_DATA_MSR Msr;
        VID_MSG_RETURN_DATA_IOPORT IoPort;
        VID_MSG_RETURN_DATA_EXCEPTION Exception;
        VID_MSG_RETURN_DATA_TRIPLEFAULT TripleFault;
        VID_MSG_RETURN_DATA_TIMER Timer;
    } Data;
} VID_MSG_RETURN_DATA, *PVID_MSG_RETURN_DATA;

//
// A simple wrapper object which contains a data message and the return
// message, which is used to define shared communication memory for a message
// queue object.
//
// FUTURE-kbroas-2007/03/07:  To save memory (and increase the number of
// exchange buffer slots available), It may be possible to make a union of the
// data message and associated return data?  Can clients handle
// overwritting the message data?  Are there cases where both are used
// simultaneously?
//
typedef struct _VID_MESSAGE_EXCHANGE_BUFFER
{
    //
    // The outgoing message.
    //
    VID_MSG_DATA         DataMessage;

    //
    // The return message.
    //
    VID_MSG_RETURN_DATA  ReturnDataMessage;

    // 
    // Sanity check via user context cookie
    UINT64              Cookie;
} VID_MESSAGE_EXCHANGE_BUFFER, *PVID_MESSAGE_EXCHANGE_BUFFER;


//
// Flags for Save & Restore
//
#define VID_SAVE_STATE_HEADER_ONLY     (1 << 1)
#define VID_SAVE_STATE_PREFLIGHT_ONLY  (1 << 2)

//
// This structure defines the descriptor for VID partition saved states,
// which will occur at the beginning of all (otherwise opaque) saved 
// state blobs.
//
typedef struct _VID_SAVED_STATE_DESCRIPTOR
{
    //
    // Size of this descriptor structure.
    //
    UINT64 DescriptorSize;

    //
    // Size of the pre-data sections, which includes
    // the descriptor and header areas.
    //
    UINT64 HeaderSize;

    //
    // Total size of the blob, which includes descriptor,
    // header, and data areas.
    //
    UINT64 TotalSize;

} VID_SAVED_STATE_DESCRIPTOR, *PVID_SAVED_STATE_DESCRIPTOR;


typedef struct _VID_MESSAGE_INFO 
{
    UINT32 DataSendPayloadSizeInBytes;
    UINT32 DataRecvPayloadSizeInBytes;
} VID_MESSAGE_INFO, *PVID_MESSAGE_INFO;

#define     VID_MESSAGE_TYPE_INDEX(_MessageType) \
    ((((_MessageType) & VID_MESSAGE_TYPE_MASK) < VidMessageTypeCount) ? \
        ((_MessageType) & VID_MESSAGE_TYPE_MASK) : VidMessageInvalid)

#define     VID_MESSAGE_INFO_GET(_MessageType) \
    (&VidMessageInfoArray[VID_MESSAGE_TYPE_INDEX(_MessageType)])

#define     VID_MESSAGE_INFO_GET_PROPERTY(_MessageType, _Property) \
    (VID_MESSAGE_INFO_GET(_MessageType)->_Property)

#define     VID_MESSAGE_INFO_GET_DATA_SEND_PAYLOAD_SIZE(_MessageType) \
    (VID_MESSAGE_INFO_GET_PROPERTY(_MessageType, DataSendPayloadSizeInBytes))

#define     VID_MESSAGE_INFO_GET_DATA_RECV_PAYLOAD_SIZE(_MessageType) \
    (VID_MESSAGE_INFO_GET_PROPERTY(_MessageType, DataRecvPayloadSizeInBytes))

#define     VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX \
    (sizeof(VID_MSG_DATA) - offsetof(VID_MSG_DATA, Data))

#define     VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX \
    (sizeof(VID_MSG_RETURN_DATA) - offsetof(VID_MSG_RETURN_DATA, Data))

//
// Copy full message for send data
//

#define     VID_MESSAGE_COPY_DATA_SEND_FULL(_DataSendDst, _DataSendSrc) \
    do \
    { \
        ASSERT(_DataSendDst != NULL); \
        ASSERT(_DataSendSrc != NULL); \
        ASSERT((_DataSendSrc)->PayloadSizeInBytes <= \
            VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX); \
        if ((_DataSendSrc)->PayloadSizeInBytes > \
            VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX) \
        { \
            (_DataSendSrc)->PayloadSizeInBytes =  \
                VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX; \
        } \
        RtlCopyMemory( \
            (_DataSendDst), \
            (_DataSendSrc), \
            offsetof(VID_MSG_DATA, Data) + (_DataSendSrc)->PayloadSizeInBytes); \
    } while (0)

//
// Copy full message for receive / return data
//

#define     VID_MESSAGE_COPY_DATA_RECV_FULL(_DataRecvDst, _DataRecvSrc) \
    do \
    { \
        ASSERT(_DataRecvDst != NULL); \
        ASSERT(_DataRecvSrc != NULL); \
        ASSERT((_DataRecvSrc)->PayloadSizeInBytes <= \
            VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX); \
        if ((_DataRecvSrc)->PayloadSizeInBytes > \
            VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX) \
        { \
            (_DataRecvSrc)->PayloadSizeInBytes =  \
                VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX; \
        } \
        RtlCopyMemory( \
            (_DataRecvDst), \
            (_DataRecvSrc), \
            offsetof(VID_MSG_RETURN_DATA, Data) + (_DataRecvSrc)->PayloadSizeInBytes); \
    } while (0)

//
// Copy payload only for message send data
//

#define     VID_MESSAGE_COPY_DATA_SEND_PAYLOAD_ONLY(_DataSendDst, _DataSendSrc) \
    do \
    { \
        ASSERT(_DataSendDst != NULL); \
        ASSERT(_DataSendSrc != NULL); \
        ASSERT((_DataSendSrc)->PayloadSizeInBytes <= \
            VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX); \
        if ((_DataSendSrc)->PayloadSizeInBytes > \
            VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX) \
        { \
            (_DataSendSrc)->PayloadSizeInBytes =  \
                VID_MESSAGE_DATA_SEND_PAYLOAD_SIZE_MAX; \
        } \
        if ((_DataSendSrc)->PayloadSizeInBytes) \
        { \
            RtlCopyMemory( \
                &(_DataSendDst)->Data, \
                &(_DataSendSrc)->Data, \
                (_DataSendSrc)->PayloadSizeInBytes); \
        } \
        (_DataSendDst)->PayloadSizeInBytes = (_DataSendSrc)->PayloadSizeInBytes; \
    } while (0)

//
// Copy payload only for message receive / return data
//

#define     VID_MESSAGE_COPY_DATA_RECV_PAYLOAD_ONLY(_DataRecvDst, _DataRecvSrc) \
    do \
    { \
        ASSERT(_DataRecvDst != NULL); \
        ASSERT(_DataRecvSrc != NULL); \
        ASSERT((_DataRecvSrc)->PayloadSizeInBytes <= \
            VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX); \
        if ((_DataRecvSrc)->PayloadSizeInBytes > \
            VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX) \
        { \
            (_DataRecvSrc)->PayloadSizeInBytes =  \
                VID_MESSAGE_DATA_RECV_PAYLOAD_SIZE_MAX; \
        } \
        if ((_DataRecvSrc)->PayloadSizeInBytes) \
        { \
            RtlCopyMemory( \
                &(_DataRecvDst)->Data, \
                &(_DataRecvSrc)->Data, \
                (_DataRecvSrc)->PayloadSizeInBytes); \
        } \
        (_DataRecvDst)->PayloadSizeInBytes = (_DataRecvSrc)->PayloadSizeInBytes; \
    } while (0)

