HGSMIHost.cpp revision 2e5c76f5687338e6bdb09e4ccb470404513fb410
/** @file
*
* VBox Host Guest Shared Memory Interface (HGSMI).
* Host part:
* - virtual hardware IO handlers;
* - channel management;
* - low level interface for buffer transfer.
*/
/*
* Copyright (C) 2006-2008 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.
*/
/*
* Async host->guest calls. Completion by an IO write from the guest or a timer timeout.
*
* Sync guest->host calls. Initiated by an IO write from the guest.
*
* Guest->Host
* ___________
*
* Synchronous for the guest, an async result can be also reported later by a host->guest call:
*
* G: Alloc shared memory, fill the structure, issue an IO write (HGSMI_IO_GUEST) with the memory offset.
* H: Verify the shared memory and call the handler.
* G: Continue after the IO completion.
*
*
* Host->Guest
* __________
*
* H: Alloc shared memory, fill in the info.
* Register in the FIFO with a callback, issue IRQ (on EMT).
* Wait on a sem with timeout if necessary.
* G: Read FIFO from HGSMI_IO_HOST_COMMAND.
* H(EMT): Get the shared memory offset from FIFO to return to the guest.
* G: Get offset, process command, issue IO write to HGSMI_IO_HOST_COMMAND.
* H(EMT): Find registered shared mem, run callback, which could post the sem.
* H: Get results and free shared mem (could be freed automatically on EMT too).
*
*
* Implementation notes:
*
* Host->Guest
*
* * Shared memory allocation using a critsect.
* * FIFO manipulation with a critsect.
*
*/
#include <iprt/critsect.h>
#include <iprt/semaphore.h>
#define LOG_GROUP LOG_GROUP_DEV_VGA
#include "HGSMIHost.h"
#include "VBox/HGSMI/HGSMIChannels.h"
#include "VBox/HGSMI/HGSMIChSetup.h"
#include "HGSMIHostHlp.h"
#ifdef DEBUG_sunlover
#define HGSMI_STRICT 1
#endif /* !DEBUG_sunlover */
#ifdef DEBUG_misha
# define VBOXHGSMI_STATE_DEBUG
#endif
#ifdef VBOXHGSMI_STATE_DEBUG
#define VBOXHGSMI_STATE_START_MAGIC 0x12345678
#define VBOXHGSMI_STATE_STOP_MAGIC 0x87654321
#define VBOXHGSMI_STATE_FIFOSTART_MAGIC 0x9abcdef1
#define VBOXHGSMI_STATE_FIFOSTOP_MAGIC 0x1fedcba9
#define VBOXHGSMI_SAVE_START(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_START_MAGIC); AssertRC(rc2);}while(0)
#define VBOXHGSMI_SAVE_STOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_STOP_MAGIC); AssertRC(rc2);}while(0)
#define VBOXHGSMI_SAVE_FIFOSTART(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC); AssertRC(rc2);}while(0)
#define VBOXHGSMI_SAVE_FIFOSTOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTOP_MAGIC); AssertRC(rc2);}while(0)
do{ \
}while(0)
#define VBOXHGSMI_LOAD_FIFOSTART(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC)
#else
#define VBOXHGSMI_SAVE_START(_pSSM) do{ }while(0)
#define VBOXHGSMI_SAVE_STOP(_pSSM) do{ }while(0)
#define VBOXHGSMI_SAVE_FIFOSTART(_pSSM) do{ }while(0)
#define VBOXHGSMI_SAVE_FIFOSTOP(_pSSM) do{ }while(0)
#define VBOXHGSMI_LOAD_START(_pSSM) do{ }while(0)
#define VBOXHGSMI_LOAD_FIFOSTART(_pSSM) do{ }while(0)
#define VBOXHGSMI_LOAD_FIFOSTOP(_pSSM) do{ }while(0)
#define VBOXHGSMI_LOAD_STOP(_pSSM) do{ }while(0)
#endif
/* Assertions for situations which could happen and normally must be processed properly
* but must be investigated during development: guest misbehaving, etc.
*/
#ifdef HGSMI_STRICT
#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
#else
#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
#define HGSMI_STRICT_ASSERT(expr) do {} while (0)
#endif /* !HGSMI_STRICT */
typedef struct _HGSMIINSTANCE
{
const char *pszName; /* A name for the instance. Mostyl used in the log. */
void *pvNotifyGuest; /* Guest notification callback context. */
volatile HGSMIHOSTFLAGS * pHGFlags;
* The array is accessed under the instance lock.
*/
typedef struct _HGSMIHOSTFIFOENTRY
{
/* The list field. Must be the first field. */
/* Backlink to the HGSMI instance. */
#if 0
/* removed to allow saved state handling */
/* The event which is signalled when the command has been processed by the host. */
#endif
/* Status flags of the entry. */
/* Offset in the memory region of the entry data. */
#if 0
/* removed to allow saved state handling */
/* The command completion callback. */
void *pvCallback;
#endif
#define HGSMILISTENTRY_2_FIFOENTRY(_pe) \
//AssertCompile(RT_OFFSETOF(HGSMIHOSTFIFOENTRY, entry) == 0);
#define HGSMI_F_HOST_FIFO_ALLOCATED 0x0001
#define HGSMI_F_HOST_FIFO_QUEUED 0x0002
#define HGSMI_F_HOST_FIFO_READ 0x0004
#define HGSMI_F_HOST_FIFO_PROCESSED 0x0008
#define HGSMI_F_HOST_FIFO_FREE 0x0010
#define HGSMI_F_HOST_FIFO_CANCELED 0x0020
{
return rc;
}
{
}
{
return rc;
}
{
}
//static HGSMICHANNEL *hgsmiChannelFindById (PHGSMIINSTANCE pIns,
// uint8_t u8Channel)
//{
// HGSMICHANNEL *pChannel = &pIns->Channels[u8Channel];
//
// if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED)
// {
// return pChannel;
// }
//
// return NULL;
//}
#if 0
/* Verify that the given offBuffer points to a valid buffer, which is within the area.
*/
{
LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n", offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast));
{
LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n", offBuffer, pArea->offBase, pArea->offLast));
return NULL;
}
/* Quick check of the data size, it should be less than the maximum
* data size for the buffer at this offset.
*/
LogFlowFunc(("datasize check: pHeader->u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n", pHeader->u32DataSize, pArea->offLast - offBuffer));
{
/* At least both pHeader and pTail structures are in the area. Check the checksum. */
LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n", u32Checksum, pTail->u32Checksum));
{
return pHeader;
}
else
{
}
}
else
{
LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n", pHeader->u32DataSize, pArea->offLast - offBuffer));
}
LogFlowFunc(("returning NULL\n"));
return NULL;
}
/*
* Process a guest buffer.
* @thread EMT
*/
const HGSMICHANNEL *pChannel,
const HGSMIBUFFERHEADER *pHeader)
{
pHeader);
return rc;
}
#endif
/*
* Virtual hardware IO handlers.
*/
/* The guest submits a new buffer to the host.
* Called from the HGSMI_IO_GUEST write handler.
* @thread EMT
*/
{
}
/* Called from HGSMI_IO_GUEST read handler. */
{
/* Currently there is no functionality here. */
return HGSMIOFFSET_VOID;
}
bool bCompleteFirst)
{
if(RT_SUCCESS(rc))
{
/* Search the Read list for the given buffer offset. Also find the previous entry. */
while (pEntry)
{
{
break;
}
#ifdef DEBUGVHWASTRICT
/* guest usually completes commands in the order it receives it
* if we're here this would typically means there is some cmd loss */
Assert(0);
#endif
}
if (pEntry)
{
/* Exclude from the Read list. */
/* Save in the Processed list. */
#if 0
/* Inform the submitter. */
if (pEntry->pfnCallback)
{
}
#else
#endif
return true;
}
if(!bCompleteFirst)
LogRel(("HGSMI[%s]: ignored invalid write to the host FIFO: 0x%08X!!!\n", pIns->pszName, offBuffer));
}
return false;
}
/* The the guest has finished processing of a buffer previously submitted by the host.
* Called from HGSMI_IO_HOST write handler.
* @thread EMT
*/
{
}
/* The guest reads a new host buffer to be processed.
* Called from the HGSMI_IO_HOST read handler.
* @thread EMT
*/
{
if(RT_SUCCESS(rc))
{
/* Get the host FIFO head entry. */
{
/* Exclude from the FIFO. */
{
}
/* Save in the Read list. */
/* Return the buffer offset of the host FIFO head. */
}
}
/* Special value that means there is no host buffers to be processed. */
return HGSMIOFFSET_VOID;
}
/* Tells the guest that a new buffer to be processed is available from the host. */
{
if (pIns->pfnNotifyGuest)
{
// pIns->pHGFlags->u32HostFlags |= HGSMIHOSTFLAGS_IRQ;
}
}
{
}
{
}
#if 0
{
}
{
return rc;
}
#endif
#if 0
{
}
#endif
/*
* The host heap.
*
* Uses the RTHeap implementation.
*
*/
{
return rc;
}
{
}
#if 0
{
if (RT_SUCCESS (rc))
{
{
}
else
{
/* A block structure: [header][data][tail].
* 'header' and 'tail' is used to verify memory blocks.
*/
if (pv)
{
/* Store some information which will help to verify memory pointers. */
/* Setup the tail. */
}
else
{
rc = VERR_NO_MEMORY;
}
}
}
return rc;
}
{
if (RT_SUCCESS (rc))
{
}
return rc;
}
{
if (RT_SUCCESS (rc))
{
}
return rc;
}
{
if (RT_SUCCESS (rc))
{
}
return rc;
}
#endif
{
int rc = VINF_SUCCESS;
if (pEntry)
{
#if 0
if (RT_FAILURE (rc))
{
}
#endif
}
else
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS (rc))
{
}
return rc;
}
{
#if 0
{
}
#endif
}
{
if(RT_SUCCESS(rc))
{
if(RT_SUCCESS(rc))
{
/* Deallocate the host heap memory. */
}
}
return rc;
}
void *pvMem)
{
int rc = VINF_SUCCESS;
if (offMem != HGSMIOFFSET_VOID)
{
if(RT_SUCCESS(rc))
{
/* Search the Processed list for the given offMem. Also find the previous entry. */
while (pEntry)
{
{
break;
}
}
if (pEntry)
{
/* Exclude from the Processed list. */
}
else
{
AssertFailed ();
}
if(RT_SUCCESS(rc))
{
/* Deallocate the host heap memory. */
}
if(pEntry)
{
/* Deallocate the entry. */
}
}
}
else
{
AssertFailed ();
}
return rc;
}
#define HGSMI_SET_COMMAND_PROCESSED_STATE(_pe) \
{ \
}
#if 0
{
/* Guest has processed the command. */
/* This is a simple callback, just signal the event. */
}
#endif
{
/* Guest has processed the command. */
/* This is a simple callback, just signal the event. */
}
#if 0
#endif
)
{
if (RT_SUCCESS (rc))
{
/* Initialize the new entry and add it to the FIFO. */
#if 0
#endif
if (RT_SUCCESS (rc))
{
#if 0
*ppvContext = pEntry;
#endif
}
else
{
}
}
return rc;
}
/**
* Append the shared memory block to the FIFO, inform the guest.
*
* @param pIns Pointer to HGSMI instance,
* @param pv The HC memory pointer to the information.
* @param ppvContext Where to store a pointer, which will allow the caller
* to wait for the command completion.
* @param bDoIrq specifies whether the guest interrupt should be generated,
* i.e. in case the command is not urgent(e.g. some guest command completion notification that does not require post-processing)
* the command could be posted without raising an irq.
*
* @thread EMT
*/
#if 0
#endif
bool bDoIrq)
{
// HGSMIOFFSET offMem;
//
// int rc = hgsmiCheckMemPtr (pIns, pvMem, &offMem);
//
// if (RT_SUCCESS (rc))
// {
/* Append the command to FIFO. */
#if 0
#endif
);
if (RT_SUCCESS (rc))
{
if(bDoIrq)
{
/* Now guest can read the FIFO, the notification is informational. */
}
}
// }
// else
// {
// AssertFailed ();
// }
return rc;
}
#if 0
{
for (;;)
{
{
return;
}
}
}
#endif
/**
*
* @param pIns Pointer to HGSMI instance,
* @param ppvMem Where to store the allocated memory pointer to data.
* @param cbMem How many bytes of data to allocate.
*/
void **ppvMem,
{
if(RT_SUCCESS(rc))
{
if (pvMem)
{
}
else
{
LogRel((0, "HGSMIHeapAlloc: HGSMIHeapAlloc failed\n"));
}
}
return rc;
}
/**
* Convenience function that allows posting the host command asynchronously
* and make it freed on completion.
* The caller does not get notified in any way on command complation,
* on success return the pvMem buffer can not be used after being passed to this function
*
* @param pIns Pointer to HGSMI instance,
* @param pvMem The pointer returned by 'HGSMIHostCommandAlloc'.
* @param bDoIrq specifies whether the guest interrupt should be generated,
* i.e. in case the command is not urgent(e.g. some guest command completion notification that does not require post-processing)
* the command could be posted without raising an irq.
*/
void *pvMem,
bool bDoIrq)
{
#if 0
#endif
// /* Have to forward to EMT because FIFO processing is there. */
// int rc = VMR3ReqCallVoid (pIns->pVM, &pReq, RT_INDEFINITE_WAIT,
// (PFNRT) hgsmiHostCommandProcess,
// 3, pIns, offBuffer, &pvContext);
#if 0
#endif
bDoIrq);
return rc;
}
#if 0
/**
* Submit the shared memory block to the guest.
*
* @param pIns Pointer to HGSMI instance,
* @param pvMem The pointer returned by 'HGSMIHostCommandAlloc'.
*/
void *pvMem)
{
// /* Have to forward to EMT because FIFO processing is there. */
// int rc = VMR3ReqCallVoid (pIns->pVM, &pReq, RT_INDEFINITE_WAIT,
// (PFNRT) hgsmiHostCommandProcess,
// 3, pIns, offBuffer, &pvContext);
#if 0
#endif
true);
if (RT_SUCCESS (rc))
{
/* Wait for completion. */
}
return rc;
}
#endif
/**
* Free the shared memory block.
*
* @param pIns Pointer to HGSMI instance,
* @param pvMem The pointer returned by 'HGSMIHostCommandAlloc'.
*/
void *pvMem)
{
}
{
int rc = VINF_SUCCESS;
// if ( offHeap >= pIns->cbMem
// || cbHeap > pIns->cbMem
// || offHeap + cbHeap > pIns->cbMem)
// {
// rc = VERR_INVALID_PARAMETER;
// }
// else
{
if (RT_SUCCESS (rc))
{
{
Assert(0);
/* It is possible to change the heap only if there is no pending allocations. */
}
else
{
offHeap);
}
}
}
return rc;
}
{
}
{
{
++size;
}
{
}
return rc;
}
static int hgsmiHostLoadFifoEntryLocked (PHGSMIINSTANCE pIns, HGSMIHOSTFIFOENTRY **ppEntry, PSSMHANDLE pSSM)
{
if (RT_SUCCESS (rc))
{
if (RT_SUCCESS (rc))
else
}
return rc;
}
{
{
{
}
}
return rc;
}
{
int rc;
HGSMIOFFSET off = pIns->pHGFlags ? HGSMIPointerToOffset(&pIns->area, (const HGSMIBUFFERHEADER *)pIns->pHGFlags) : HGSMIOFFSET_VOID;
if(off != HGSMIOFFSET_VOID)
{
/* need save mem pointer to calculate offset on restore */
if(RT_SUCCESS(rc))
{
}
}
return rc;
}
{
if(u32Version < 3)
return VINF_SUCCESS;
int rc;
pIns->pHGFlags = (off != HGSMIOFFSET_VOID) ? (HGSMIHOSTFLAGS*)HGSMIOffsetToPointer (&pIns->area, off) : NULL;
if(off != HGSMIOFFSET_VOID)
{
if (RT_SUCCESS (rc))
{
off,
offHeap);
}
if (RT_SUCCESS(rc))
{
if(RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
}
}
}
return rc;
}
/*
* Channels management.
*/
const char *pszChannel,
{
/* @todo later */
return VERR_NOT_SUPPORTED;
}
/* Register a new HGSMI channel by a predefined index.
*/
void *pvChannelHandler,
{
LogFlowFunc(("pIns %p, u8Channel %x, pfnChannelHandler %p, pvChannelHandler %p, pOldHandler %p\n",
if (RT_SUCCESS (rc))
{
rc = HGSMIChannelRegister (&pIns->channelInfo, u8Channel, NULL, pfnChannelHandler, pvChannelHandler, pOldHandler);
hgsmiUnlock (pIns);
}
return rc;
}
/* Register a new HGSMI channel by name.
*/
const char *pszChannel,
void *pvChannelHandler,
{
LogFlowFunc(("pIns %p, pszChannel %s, pfnChannelHandler %p, pvChannelHandler %p, pu8Channel %p, pOldHandler %p\n",
int rc;
/* The pointer to the copy will be saved in the channel description. */
if (pszName)
{
if (RT_SUCCESS (rc))
{
if (RT_SUCCESS (rc))
{
rc = HGSMIChannelRegister (&pIns->channelInfo, *pu8Channel, pszName, pfnChannelHandler, pvChannelHandler, pOldHandler);
}
hgsmiUnlock (pIns);
}
if (RT_FAILURE (rc))
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
#if 0
/* A wrapper to safely call the handler.
*/
const HGSMICHANNELHANDLER *pHandler,
const HGSMIBUFFERHEADER *pHeader)
{
int rc;
if ( pHandler
&& pHandler->pfnHandler)
{
}
else
{
/* It is a NOOP case here. */
rc = VINF_SUCCESS;
}
return rc;
}
#endif
{
{
LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n", offBuffer, pArea->offBase, pArea->offLast));
return NULL;
}
}
const void *pv)
{
if ( p < pBegin
|| p > pEnd)
{
return HGSMIOFFSET_VOID;
}
}
{
return p + sizeof (HGSMIINSTANCE);
}
/* The guest submitted a buffer. */
static DECLCALLBACK(int) hgsmiChannelHandler (void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pvHandler %p, u16ChannelInfo %d, pvBuffer %p, cbBuffer %u\n",
switch (u16ChannelInfo)
{
{
if (cbBuffer < sizeof (HGSMIBUFFERLOCATION))
{
break;
}
{
break;
}
} break;
default:
Log(("Unsupported HGSMI guest command %d!!!\n",
break;
}
return rc;
}
const char *pszName,
void *pvNotifyGuest,
{
LogFlowFunc(("ppIns = %p, pVM = %p, pszName = [%s], pu8MemBase = %p, cbMem = 0x%08X, offMemBase = 0x%08X, "
"pfnNotifyGuest = %p, pvNotifyGuest = %p, cbContext = %d\n",
pVM,
));
int rc = VINF_SUCCESS;
if (!pIns)
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS (rc))
{
}
if (RT_SUCCESS (rc))
{
}
if (RT_SUCCESS (rc))
{
}
if (RT_SUCCESS (rc))
{
}
if (RT_SUCCESS (rc))
{
}
pIns,
if (RT_SUCCESS (rc))
{
}
else
{
HGSMIDestroy (pIns);
}
return rc;
}
{
{
/* treat the abandoned commands as read.. */
}
/* .. and complete them */
while(hgsmiProcessHostCmdCompletion (pIns, 0, true)) {}
return flags;
}
{
if (pIns)
{
{
}
{
}
{
}
}
LogFlowFunc(("leave\n"));
}