HGCMInternal.cpp revision 162ca59af4713e3106a6d4f8a28c5c0ac311d3ae
/* $Revision$ */
/** @file
* VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest
*/
/*
* 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;
* 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.
*/
/* Entire file is ifdef'ed with VBGL_VBOXGUEST */
#ifdef VBGL_VBOXGUEST
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "VBGLInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The max parameter buffer size for a user request. */
#define VBGLR0_MAX_HGCM_USER_PARM _1M
/** The max parameter buffer size for a kernel request. */
#ifdef RT_OS_LINUX
/** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted
* side effects. */
# define USE_BOUNCH_BUFFERS
#endif
/* These functions can be only used by VBoxGuest. */
{
int rc;
if (!pConnectInfo || !pAsyncCallback)
return VERR_INVALID_PARAMETER;
pHGCMConnect = NULL;
/* Allocate request */
rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMConnect, sizeof (VMMDevHGCMConnect), VMMDevReq_HGCMConnect);
if (RT_SUCCESS(rc))
{
/* Initialize request memory */
pHGCMConnect->u32ClientID = 0;
/* Issue request */
if (RT_SUCCESS(rc))
{
/* Check if host decides to process the request asynchronously. */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
/* Wait for request completion interrupt notification from host */
}
}
}
return rc;
}
{
int rc;
if (!pDisconnectInfo || !pAsyncCallback)
return VERR_INVALID_PARAMETER;
/* Allocate request */
rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMDisconnect, sizeof (VMMDevHGCMDisconnect), VMMDevReq_HGCMDisconnect);
if (RT_SUCCESS(rc))
{
/* Initialize request memory */
/* Issue request */
if (RT_SUCCESS(rc))
{
/* Check if host decides to process the request asynchronously. */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
/* Wait for request completion interrupt notification from host */
}
}
}
return rc;
}
#if 0 /* new code using page list and whatnot. */
/**
* Lock info structure used by VbglR0HGCMInternalCall and its helpers.
*/
struct VbglR0ParmInfo
{
struct
{
} aLockBufs[10];
};
/**
*
* @returns VBox status code.
*
* @param pCallInfo The call info.
* @param cbCallInfo The size of the call info structure.
* @param fIsUser Is it a user request or kernel request.
* @param pcbExtra Where to return the extra request space needed for
* physical page lists.
*/
static int vbglR0HGCMInternalPreprocessCall(VBoxGuestHGCMCallInfo const *pCallInfo, uint32_t cbCallInfo,
{
/*
* Lock down the any linear buffers so we can get their addresses
* and figure out how much extra storage we need for page lists.
*/
*pcbExtra = 0;
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
if (fIsUser)
return VERR_INVALID_PARAMETER;
{
return VERR_OUT_OF_RANGE;
AssertReturn(RT_OFFSETOF(HGCMPageListInfo, aPages[pPgLst->cPages]) + pSrcParm->u.PageList.offset <= cbCallInfo, VERR_INVALID_PARAMETER);
AssertReturn(pPgLst->flags > VBOX_HGCM_F_PARM_DIRECTION_NONE && pPgLst->flags <= VBOX_HGCM_F_PARM_DIRECTION_BOTH, VERR_INVALID_PARAMETER);
}
break;
if (fIsUser)
return VERR_INVALID_PARAMETER;
if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST())
{
return VERR_OUT_OF_RANGE;
break;
}
/* fall thru */
return VERR_OUT_OF_RANGE;
{
int rc;
if (!fIsUser)
else
{
#ifdef USE_BOUNCH_BUFFERS
false /*fExecutable*/);
if ( RT_SUCCESS(rc)
{
if (RT_FAILURE(rc))
}
#else
#endif
}
if (RT_FAILURE(rc))
return rc;
{
}
}
break;
default:
return VERR_INVALID_PARAMETER;
}
}
return VINF_SUCCESS;
}
/**
* Translates locked linear address to the normal type.
* The locked types are only for the guest side and not handled by the host.
*
* @returns normal linear address type.
* @param enmType The type.
*/
static HGCMFunctionParameterType vbglR0HGCMInternalConvertLinAddrType(HGCMFunctionParameterType enmType)
{
switch (enmType)
{
return VMMDevHGCMParmType_LinAddr_In;
return VMMDevHGCMParmType_LinAddr_Out;
return VMMDevHGCMParmType_LinAddr;
default:
return enmType;
}
}
/**
* Translates linear address types to page list direction flags.
*
* @returns page list flags.
* @param enmType The type.
*/
{
switch (enmType)
{
default: AssertFailed();
return VBOX_HGCM_F_PARM_DIRECTION_BOTH;
}
}
/**
* Initializes the call request that we're sending to the host.
*
* @returns VBox status code.
*
* @param pCallInfo The call info.
* @param cbCallInfo The size of the call info structure.
* @param fIsUser Is it a user request or kernel request.
* @param pcbExtra Where to return the extra request space needed for
* physical page lists.
*/
static void vbglR0HGCMInternalInitCall(VMMDevHGCMCall *pHGCMCall, VBoxGuestHGCMCallInfo const *pCallInfo, uint32_t cbCallInfo,
{
/*
* The call request headers.
*/
/*
* The parameters.
*/
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
{
HGCMPageListInfo const *pSrcPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + pSrcParm->u.PageList.offset);
}
else
break;
if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST())
{
break;
}
/* fall thru */
{
{
#ifdef USE_BOUNCH_BUFFERS
if (fIsUser)
pDstPgLst->offFirstPage = 0;
else
#endif
{
}
}
else
{
#ifdef USE_BOUNCH_BUFFERS
if (fIsUser)
else
#endif
}
iLockBuf++;
}
else
{
}
break;
default:
AssertFailed();
break;
}
}
}
/**
* Performs the call and completion wait.
*
* @returns VBox status code of this operation, not necessarily the call.
*
* @param pHGCMCall The HGCM call info.
* @param pfnAsyncCallback The async callback that will wait for the call
* to complete.
* @param pvAsyncData Argument for the callback.
* @param u32AsyncData Argument for the callback.
*/
static int vbglR0HGCMInternalDoCall(VMMDevHGCMCall *pHGCMCall, VBGLHGCMCALLBACK *pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
{
int rc;
Log(("calling VbglGRPerform\n"));
/*
* If the call failed, but as a result of the request itself, then pretend
* success. Upper layers will interpret the result code in the packet.
*/
if ( RT_FAILURE(rc)
{
rc = VINF_SUCCESS;
}
/*
* Check if host decides to process the request asynchronously,
* if so, we wait for it to complete using the caller supplied callback.
*/
if ( RT_SUCCESS(rc)
&& rc == VINF_HGCM_ASYNC_EXECUTE)
{
Log(("Processing HGCM call asynchronously\n"));
/** @todo timeout vs. interrupted. */
/*
* If the request isn't completed by the time the callback returns
* we will have to try cancel it.
*/
{
/** @todo use a new request for this! See @bugref{4052}. */
}
}
return rc;
}
/**
* Copies the result of the call back to the caller info structure and user
* buffers (if using bounce buffers).
*
* @returns rc, unless RTR0MemUserCopyTo fails.
* @param pCallInfo Call info structure to update.
* @param pHGCMCall HGCM call request.
* @param fIsUser Is it a user (true) or kernel request.
* @param rc The current result code. Passed along to
* preserve informational status codes.
*/
static int vbglR0HGCMInternalCopyBackResult(VBoxGuestHGCMCallInfo *pCallInfo, VMMDevHGCMCall const *pHGCMCall,
{
#ifdef USE_BOUNCH_BUFFERS
#endif
/*
* The call result.
*/
/*
* Copy back parameters.
*/
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
break;
#ifdef USE_BOUNCH_BUFFERS
if ( fIsUser
iLockBuf++;
#endif
break;
if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST())
{
break;
}
/* fall thru */
{
#ifdef USE_BOUNCH_BUFFERS
if (fIsUser)
{
{
if (RT_FAILURE(rc2))
return rc2;
iLockBuf++;
}
iLockBuf++;
}
#endif
break;
}
default:
AssertFailed();
break;
}
}
#ifdef USE_BOUNCH_BUFFERS
#endif
return rc;
}
DECLR0VBGL(int) VbglR0HGCMInternalCall(VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
struct VbglR0ParmInfo ParmInfo;
int rc;
/*
* Basic validation.
*/
AssertMsgReturn(!pCallInfo || !pfnAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
Log (("VbglR0HGCMInternalCall: pCallInfo->cParms = %d, pHGCMCall->u32Function = %d, fFlags=%#x\n",
/*
*/
if (RT_SUCCESS(rc))
{
/*
* Allocate the request buffer and recreate the call request.
*/
if (RT_SUCCESS(rc))
{
/*
* Perform the call.
*/
if (RT_SUCCESS(rc))
{
/*
* Copy back the result (parameters and buffers that changed).
*/
}
}
}
/*
* Copy back buffered data and release buffers & locks.
*/
return rc;
}
# if ARCH_BITS == 64
DECLR0VBGL(int) VbglR0HGCMInternalCall32(VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
int rc;
/*
* Input validation.
*/
AssertMsgReturn(!pCallInfo || !pfnAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
AssertReturn((fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_KERNEL, VERR_INVALID_PARAMETER);
Log(("VbglR0HGCMInternalCall32: cParms=%d, u32Function=%d, fFlags=%#x\n", cParms, pCallInfo->u32Function, fFlags));
/*
* The simple approach, allocate a temporary request and convert the parameters.
*/
pCallInfo64 = (VBoxGuestHGCMCallInfo *)RTMemTmpAllocZ(sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter));
if (!pCallInfo64)
return VERR_NO_TMP_MEMORY;
*pCallInfo64 = *pCallInfo;
{
{
case VMMDevHGCMParmType_32bit:
break;
case VMMDevHGCMParmType_64bit:
break;
break;
default:
break;
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
{
rc = VbglR0HGCMInternalCall(pCallInfo64, sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter), fFlags,
/*
* Copy back.
*/
{
{
case VMMDevHGCMParmType_32bit:
break;
case VMMDevHGCMParmType_64bit:
break;
break;
default:
break;
}
}
*pCallInfo = *pCallInfo64;
}
return rc;
}
# endif /* ARCH_BITS == 64 */
# else /* old code: */
/** @todo merge with the one below (use a header file). Too lazy now. */
DECLR0VBGL(int) VbglR0HGCMInternalCall (VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
unsigned iParm;
int rc;
AssertMsgReturn(!pCallInfo || !pAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
Log (("VbglR0HGCMInternalCall: pCallInfo->cParms = %d, pHGCMCall->u32Function = %d, fFlags=%#x\n",
if (cbCallInfo == 0)
{
/* Caller did not specify the size (a valid value should be at least sizeof(VBoxGuestHGCMCallInfo)).
* Compute the size.
*/
}
else if (cbCallInfo < sizeof (VBoxGuestHGCMCallInfo))
{
return VERR_INVALID_PARAMETER;
}
else
{
}
/* Allocate request */
rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMCall, sizeof (VMMDevHGCMCall) + cbParms, VMMDevReq_HGCMCall);
Log (("VbglR0HGCMInternalCall: Allocated gr %p, rc = %Rrc, cbParms = %d\n", pHGCMCall, rc, cbParms));
if (RT_SUCCESS(rc))
{
void *apvCtx[VBOX_HGCM_MAX_PARMS];
/* Initialize request memory */
if (cbParms)
{
/* Lock user buffers. */
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
else
break;
else
break;
else
break;
break;
/* PORTME: When porting this to Darwin and other systems where the entire kernel isn't mapped
into every process, all linear address will have to be converted to physical SG lists at
this point. Care must also be taken on these guests to not mix kernel and user addresses
it will assume that it's in the current memory context (i.e. use CR3 to translate it).
These kind of problems actually applies to some patched linux kernels too, including older
fedora releases. (The patch is the infamous 4G/4G patch, aka 4g4g, by Ingo Molnar.) */
fFlags);
break;
default:
break;
}
if (RT_FAILURE (rc))
break;
}
}
/* Check that the parameter locking was ok. */
if (RT_SUCCESS(rc))
{
Log (("calling VbglGRPerform\n"));
/* Issue request */
/** If the call failed, but as a result of the request itself, then pretend success
* Upper layers will interpret the result code in the packet.
*/
{
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
/* Check if host decides to process the request asynchronously. */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
/* Wait for request completion interrupt notification from host */
Log (("Processing HGCM call asynchronously\n"));
}
{
if (cbParms)
{
}
}
else
{
/* The callback returns without completing the request,
* that means the wait was interrrupted. That can happen
* if the request times out, the system reboots or the
* VBoxService ended abnormally.
*
* Cancel the request, the host will not write to the
* memory related to the cancelled request.
*/
Log (("Cancelling HGCM call\n"));
}
}
}
/* Unlock user buffers. */
{
{
{
}
}
else
}
else
}
return rc;
}
# if ARCH_BITS == 64
/** @todo merge with the one above (use a header file). Too lazy now. */
DECLR0VBGL(int) VbglR0HGCMInternalCall32 (VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
unsigned iParm;
int rc;
AssertMsgReturn(!pCallInfo || !pAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
Log (("VbglR0HGCMInternalCall32: pCallInfo->cParms = %d, pHGCMCall->u32Function = %d, fFlags=%#x\n",
if (cbCallInfo == 0)
{
/* Caller did not specify the size (a valid value should be at least sizeof(VBoxGuestHGCMCallInfo)).
* Compute the size.
*/
}
else if (cbCallInfo < sizeof (VBoxGuestHGCMCallInfo))
{
return VERR_INVALID_PARAMETER;
}
else
{
}
/* Allocate request */
rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMCall, sizeof (VMMDevHGCMCall) + cbParms, VMMDevReq_HGCMCall32);
Log (("VbglR0HGCMInternalCall32: Allocated gr %p, rc = %Rrc, cbParms = %d\n", pHGCMCall, rc, cbParms));
if (RT_SUCCESS(rc))
{
void *apvCtx[VBOX_HGCM_MAX_PARMS];
/* Initialize request memory */
if (cbParms)
{
/* Lock user buffers. */
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
else
break;
else
break;
else
break;
break;
/* PORTME: When porting this to Darwin and other systems where the entire kernel isn't mapped
into every process, all linear address will have to be converted to physical SG lists at
this point. Care must also be taken on these guests to not mix kernel and user addresses
it will assume that it's in the current memory context (i.e. use CR3 to translate it).
These kind of problems actually applies to some patched linux kernels too, including older
fedora releases. (The patch is the infamous 4G/4G patch, aka 4g4g, by Ingo Molnar.) */
fFlags);
break;
default:
break;
}
if (RT_FAILURE (rc))
break;
}
}
/* Check that the parameter locking was ok. */
if (RT_SUCCESS(rc))
{
Log (("calling VbglGRPerform\n"));
/* Issue request */
/** If the call failed, but as a result of the request itself, then pretend success
* Upper layers will interpret the result code in the packet.
*/
{
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
/* Check if host decides to process the request asynchronously. */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
/* Wait for request completion interrupt notification from host */
Log (("Processing HGCM call asynchronously\n"));
}
{
if (cbParms)
}
else
{
/* The callback returns without completing the request,
* that means the wait was interrrupted. That can happen
* if the request times out, the system reboots or the
* VBoxService ended abnormally.
*
* Cancel the request, the host will not write to the
* memory related to the cancelled request.
*/
Log (("Cancelling HGCM call\n"));
}
}
}
/* Unlock user buffers. */
{
{
{
}
}
else
}
else
}
return rc;
}
# endif /* ARCH_BITS == 64 */
# endif /* old code */
#endif /* VBGL_VBOXGUEST */