HGCMInternal.cpp revision d2d881e581a042c5e6c6651a58c941dfaf5afdd3
/* $Revision$ */
/** @file
* VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE 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.
*/
/* Entire file is ifdef'ed with VBGL_VBOXGUEST */
#ifdef VBGL_VBOXGUEST
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_HGCM
#include "VBGLInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The max parameter buffer size for a user request. */
/** The max parameter buffer size for a kernel request. */
/** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted
* side effects.
# define USE_BOUNCE_BUFFERS
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Lock info structure used by VbglR0HGCMInternalCall and its helpers.
*/
struct VbglR0ParmInfo
{
struct
{
#ifdef USE_BOUNCE_BUFFERS
void *pvSmallBuf;
#endif
} aLockBufs[10];
};
/* These functions can be only used by VBoxGuest. */
{
int rc;
if (!pConnectInfo || !pfnAsyncCallback)
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 || !pfnAsyncCallback)
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;
}
/**
*
* @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.
*
* Note! With kernel mode users we can be assertive. For user mode users
* we should just (debug) log it and fail without any fanfare.
*/
*pcbExtra = 0;
{
{
case VMMDevHGCMParmType_32bit:
break;
case VMMDevHGCMParmType_64bit:
break;
if (fIsUser)
return VERR_INVALID_PARAMETER;
if (cb)
{
AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
AssertMsgReturn(pPgLst->offFirstPage < PAGE_SIZE, ("#x\n", pPgLst->offFirstPage), VERR_INVALID_PARAMETER);
AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pPgLst->flags), ("%#x\n", pPgLst->flags), VERR_INVALID_PARAMETER);
Log4(("GstHGCMCall: parm=%u type=pglst: cb=%#010x cPgs=%u offPg0=%#x flags=%#x\n",
while (u32-- > 0)
{
}
}
else
break;
if (fIsUser)
return VERR_INVALID_PARAMETER;
if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true))
{
AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
if (cb != 0)
Log4(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p\n",
else
break;
}
/* fall thru */
if (cb != 0)
{
#ifdef USE_BOUNCE_BUFFERS
void *pvSmallBuf = NULL;
#endif
int rc;
if (!fIsUser)
{
AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
if (RT_FAILURE(rc))
{
Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockKernel(,%p,%#x) -> %Rrc\n",
return rc;
}
Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked kernel -> %p\n",
}
else if (cb > VBGLR0_MAX_HGCM_USER_PARM)
{
Log(("GstHGCMCall: id=%#x fn=%u parm=%u pv=%p cb=%#x > %#x -> out of range\n",
return VERR_OUT_OF_RANGE;
}
else
{
#ifndef USE_BOUNCE_BUFFERS
rc = RTR0MemObjLockUser(&hObj, (RTR3PTR)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess, NIL_RTR0PROCESS);
if (RT_FAILURE(rc))
{
Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockUser(,%p,%#x,nil) -> %Rrc\n",
return rc;
}
Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked user -> %p\n",
#else /* USE_BOUNCE_BUFFERS */
/*
* This is a bit massive, but we don't want to waste a
* whole page for a 3 byte string buffer (guest props).
*
* The threshold is ASSUMING sizeof(RTMEMHDR) == 16 and
* the system is using some power of two allocator.
*/
/** @todo A more efficient strategy would be to combine buffers. However it
* is probably going to be more massive than the current code, so
* it can wait till later. */
{
if (RT_UNLIKELY(!pvSmallBuf))
return VERR_NO_MEMORY;
if (fCopyIn)
{
if (RT_FAILURE(rc))
{
Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n",
return rc;
}
}
if (RT_FAILURE(rc))
{
Log(("GstHGCMCall: RTR0MemObjLockKernel failed for small buffer: rc=%Rrc pvSmallBuf=%p cb=%#x\n",
return rc;
}
Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p small buffer %p -> %p\n",
}
else
{
if (RT_FAILURE(rc))
return rc;
if (!fCopyIn)
else
{
if (RT_FAILURE(rc))
{
Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n",
return rc;
}
}
Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p big buffer -> %p\n",
}
#endif /* USE_BOUNCE_BUFFERS */
}
#ifdef USE_BOUNCE_BUFFERS
#endif
if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false))
{
}
}
else
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,
{
/*
* 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(/*a_fLocked =*/ true))
{
break;
}
/* fall thru */
{
#ifdef USE_BOUNCE_BUFFERS
#endif
if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false))
{
#ifdef USE_BOUNCE_BUFFERS
if (fIsUser)
else
#endif
{
}
}
else
{
#ifdef USE_BOUNCE_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.
* @param pfLeakIt Where to return the leak it / free it,
* indicator. Cancellation fun.
*/
static int vbglR0HGCMInternalDoCall(VMMDevHGCMCall *pHGCMCall, PFNVBGLHGCMCALLBACK pfnAsyncCallback,
{
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.
*/
*pfLeakIt = false;
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
Log(("Processing HGCM call asynchronously\n"));
{
rc = VINF_SUCCESS;
}
else
{
/*
* The request didn't complete in time or the call was interrupted,
* the RC from the callback indicates which. Try cancel the request.
*
* This is a bit messy because we're racing request completion. Sorry.
*/
/** @todo It would be nice if we could use the waiter callback to do further
* waiting in case of a completion race. If it wasn't for WINNT having its own
* version of all that stuff, I would've done it already. */
int rc2 = VbglGRAlloc((VMMDevRequestHeader **)&pCancelReq, sizeof(*pCancelReq), VMMDevReq_HGCMCancel2);
if (RT_SUCCESS(rc2))
{
}
#if 1 /** @todo ADDVER: Remove this on next minor version change. */
if (rc2 == VERR_NOT_IMPLEMENTED)
{
/* host is too old, or we're out of heap. */
if (rc2 == VERR_INVALID_PARAMETER)
else if (RT_SUCCESS(rc))
RTThreadSleep(1);
}
#endif
if (RT_SUCCESS(rc)) rc = VERR_INTERRUPTED; /** @todo weed this out from the WINNT VBoxGuest code. */
if (RT_SUCCESS(rc2))
{
Log(("vbglR0HGCMInternalDoCall: successfully cancelled\n"));
}
else
{
/*
* Wait for a bit while the host (hopefully) completes it.
*/
if (rc2 != VERR_NOT_FOUND)
{
static unsigned s_cErrors = 0;
if (s_cErrors++ < 32)
}
else
do
{
ASMCompilerBarrier(); /* paranoia */
break;
RTThreadSleep(1);
} while (cElapsed < cMilliesToWait);
ASMCompilerBarrier(); /* paranoia^2 */
rc = VINF_SUCCESS;
else
{
LogRel(("vbglR0HGCMInternalDoCall: Leaking %u bytes. Pending call to %u with %u parms. (rc2=%Rrc)\n",
*pfLeakIt = true;
}
Log(("vbglR0HGCMInternalDoCall: Cancel race ended with rc=%Rrc (rc2=%Rrc) after %llu ms\n", rc, rc2, cElapsed));
}
}
}
Log(("GstHGCMCall: rc=%Rrc result=%Rrc fu32Flags=%#x fLeakIt=%d\n",
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_BOUNCE_BUFFERS
#endif
/*
* The call result.
*/
/*
* Copy back parameters.
*/
{
{
case VMMDevHGCMParmType_32bit:
case VMMDevHGCMParmType_64bit:
break;
break;
#ifdef USE_BOUNCE_BUFFERS
if ( fIsUser
iLockBuf++;
#endif
break;
if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true))
{
break;
}
/* fall thru */
{
#ifdef USE_BOUNCE_BUFFERS
if (fIsUser)
{
if (cbOut)
{
int rc2;
cbOut);
if (RT_FAILURE(rc2))
return rc2;
iLockBuf++;
}
iLockBuf++;
}
#endif
break;
}
default:
AssertFailed();
break;
}
}
#ifdef USE_BOUNCE_BUFFERS
#endif
return rc;
}
DECLR0VBGL(int) VbglR0HGCMInternalCall(VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
struct VbglR0ParmInfo ParmInfo;
int rc;
/*
* Basic validation.
*/
|| !pfnAsyncCallback
|| !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
Log(("GstHGCMCall: u32ClientID=%#x u32Function=%u cParms=%u cbCallInfo=%#x fFlags=%#x\n",
pCallInfo->u32ClientID, pCallInfo->u32ClientID, pCallInfo->u32Function, pCallInfo->cParms, cbCallInfo, fFlags));
/*
* Validate, lock and buffer the parameters for the call.
* This will calculate the amount of extra space for physical page list.
*/
if (RT_SUCCESS(rc))
{
/*
* Allocate the request buffer and recreate the call request.
*/
if (RT_SUCCESS(rc))
{
bool fLeakIt;
/*
* Perform the call.
*/
if (RT_SUCCESS(rc))
{
/*
* Copy back the result (parameters and buffers that changed).
*/
}
else
{
if ( rc != VERR_INTERRUPTED
&& rc != VERR_TIMEOUT)
{
static unsigned s_cErrors = 0;
if (s_cErrors++ < 32)
}
}
if (!fLeakIt)
}
}
else
/*
* Release locks and free bounce buffers.
*/
{
#ifdef USE_BOUNCE_BUFFERS
#endif
}
return rc;
}
#if ARCH_BITS == 64
DECLR0VBGL(int) VbglR0HGCMInternalCall32(VBoxGuestHGCMCallInfo *pCallInfo, uint32_t cbCallInfo, uint32_t fFlags,
{
int rc = VINF_SUCCESS;
/*
* Input validation.
*/
|| !pfnAsyncCallback
|| !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_WINDOWS)
#endif
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,
if (RT_SUCCESS(rc))
{
*pCallInfo = *pCallInfo64;
/*
* Copy back.
*/
{
{
case VMMDevHGCMParmType_32bit:
break;
case VMMDevHGCMParmType_64bit:
break;
break;
default:
break;
}
}
}
else
{
static unsigned s_cErrors = 0;
if (s_cErrors++ < 32)
}
}
else
{
static unsigned s_cErrors = 0;
if (s_cErrors++ < 32)
}
return rc;
}
#endif /* ARCH_BITS == 64 */
#endif /* VBGL_VBOXGUEST */