int VMCI_CopyToUser(VA64 dst, const void *src, size_t len, int mode);
#else
int VMCI_CopyToUser(VA64 dst, const void *src, size_t len);
-/*
- * Don't need the following for guests, hence no Solaris code for this
- * function.
- */
+#endif
Bool VMCIWellKnownID_AllowMap(VMCIId wellKnownID,
VMCIPrivilegeFlags privFlags);
-#endif
int VMCIHost_CompareUser(VMCIHostUser *user1, VMCIHostUser *user2);
void VMCI_DeviceShutdownBegin(void);
void VMCI_DeviceShutdownEnd(void);
Bool VMCI_DeviceShutdown(void);
+Bool VMCI_HasGuestDevice(void);
+Bool VMCI_HasHostDevice(void);
#else // _WIN32
# define VMCI_InitQueueMutex(_pq, _cq)
# define VMCI_AcquireQueueMutex(_q)
# define VMCI_RevertToNonLocalQueue(_q, _nlq, _s)
# define VMCI_FreeQueueBuffer(_q, _s)
# define VMCI_DeviceShutdown() FALSE
+#if defined(VMX86_TOOLS)
+# define VMCI_HasGuestDevice() TRUE
+# define VMCI_HasHostDevice() FALSE
+#else // VMX86_TOOLS
+# define VMCI_HasGuestDevice() FALSE
+# define VMCI_HasHostDevice() TRUE
+#endif // VMX86_TOOLS
#endif // !_WIN32
+
#if defined(VMKERNEL)
typedef List_Links VMCIListItem;
typedef List_Links VMCIList;
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2011 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * stubs.c
+ *
+ * Stubs for host functions still missing from the guest driver.
+ *
+ */
+
+#ifdef __linux__
+# include "driver-config.h"
+#endif
+
+#include "vmci_kernel_if.h"
+#include "vmci_defs.h"
+#include "vmciContext.h"
+#include "vmciDoorbell.h"
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIQPBroker_Lock --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIQPBroker_Lock(void)
+{
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIQPBroker_Unlock --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIQPBroker_Unlock(void)
+{
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * QueuePair_Detach --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIQPBroker_Detach(VMCIHandle handle,
+ VMCIContext *context,
+ Bool detach)
+{
+ return VMCI_ERROR_GENERIC;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellHostContextNotify --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIDoorbellHostContextNotify(VMCIId srcCID,
+ VMCIHandle handle)
+{
+ return VMCI_ERROR_GENERIC;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellGetPrivFlags --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIDoorbellGetPrivFlags(VMCIHandle handle,
+ VMCIPrivilegeFlags *privFlags)
+{
+ return VMCI_ERROR_GENERIC;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIUnsetNotify --
+ *
+ * Stub. Not called in the guest driver (yet).
+ *
+ * Result:
+ * Always VMCI_ERROR_GENERIC.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIUnsetNotify(VMCIContext *context)
+{
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciCommonInt.h --
+ *
+ * Struct definitions for VMCI internal common code.
+ */
+
+#ifndef _VMCI_COMMONINT_H_
+#define _VMCI_COMMONINT_H_
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vm_atomic.h"
+#include "vmci_defs.h"
+#include "vmci_call_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmci_handle_array.h"
+#include "vmci_kernel_if.h"
+
+
+/*
+ * The DatagramQueueEntry is a queue header for the in-kernel VMCI
+ * datagram queues. It is allocated in non-paged memory, as the
+ * content is accessed while holding a spinlock. The pending datagram
+ * itself may be allocated from paged memory. We shadow the size of
+ * the datagram in the non-paged queue entry as this size is used
+ * while holding the same spinlock as above.
+ */
+
+typedef struct DatagramQueueEntry {
+ VMCIListItem listItem; /* For queuing. */
+ size_t dgSize; /* Size of datagram. */
+ VMCIDatagram *dg; /* Pending datagram. */
+} DatagramQueueEntry;
+
+
+struct VMCIContext {
+ VMCIListItem listItem; /* For global VMCI list. */
+ VMCIId cid;
+ Atomic_uint32 refCount;
+ VMCIList datagramQueue; /* Head of per VM queue. */
+ uint32 pendingDatagrams;
+ size_t datagramQueueSize;/* Size of datagram queue in bytes. */
+ int userVersion; /*
+ * Version of the code that created
+ * this context; e.g., VMX.
+ */
+ VMCILock lock; /* Locks callQueue and handleArrays. */
+ VMCIHandleArray *wellKnownArray; /* WellKnown mappings owned by context. */
+ VMCIHandleArray *queuePairArray; /*
+ * QueuePairs attached to. The array of
+ * handles for queue pairs is accessed
+ * from the code for QP API, and there
+ * it is protected by the QP lock. It
+ * is also accessed from the context
+ * clean up path, which does not
+ * require a lock. VMCILock is not
+ * used to protect the QP array field.
+ */
+ VMCIHandleArray *doorbellArray; /* Doorbells created by context. */
+ VMCIHandleArray *pendingDoorbellArray; /* Doorbells pending for context. */
+ VMCIHandleArray *notifierArray; /* Contexts current context is subscribing to. */
+ VMCIHost hostContext;
+ VMCIPrivilegeFlags privFlags;
+ VMCIHostUser user;
+ Bool validUser;
+#ifdef VMKERNEL
+ char domainName[VMCI_DOMAIN_NAME_MAXLEN];
+ Bool isQuiesced; /* Whether current VM is quiesced */
+ VMCIId migrateCid; /* The migrate cid if it is migrating */
+#endif
+#ifndef VMX86_SERVER
+ Bool *notify; /* Notify flag pointer - hosted only. */
+# ifdef __linux__
+ struct page *notifyPage; /* Page backing the notify UVA. */
+# endif
+#endif
+};
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDenyInteraction --
+ *
+ * Utilility function that checks whether two entities are allowed
+ * to interact. If one of them is restricted, the other one must
+ * be trusted. On ESX, the vmci domain must match for unrestricted
+ * domains.
+ *
+ * Result:
+ * TRUE if the two entities are not allowed to interact. FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static INLINE Bool
+VMCIDenyInteraction(VMCIPrivilegeFlags partOne, // IN
+ VMCIPrivilegeFlags partTwo, // IN
+ const char *srcDomain, // IN: Unused on hosted
+ const char *dstDomain) // IN: Unused on hosted
+{
+#ifndef VMKERNEL
+ return (((partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) &&
+ !(partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)) ||
+ ((partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED) &&
+ !(partOne & VMCI_PRIVILEGE_FLAG_TRUSTED)));
+#else
+ /*
+ * If source or destination is trusted (hypervisor), we allow the
+ * communication.
+ */
+ if ((partOne & VMCI_PRIVILEGE_FLAG_TRUSTED) ||
+ (partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)) {
+ return FALSE;
+ }
+ /*
+ * If source or destination is restricted, we deny the communication.
+ */
+ if ((partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) ||
+ (partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED)) {
+ return TRUE;
+ }
+ /*
+ * We are here, means that neither of source or destination are trusted, and
+ * both are unrestricted.
+ */
+ ASSERT(!(partOne & VMCI_PRIVILEGE_FLAG_TRUSTED) &&
+ !(partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED));
+ ASSERT(!(partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) &&
+ !(partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED));
+ /*
+ * We now compare the source and destination domain names, and allow
+ * communication iff they match.
+ */
+ return strcmp(srcDomain, dstDomain) ? TRUE : /* Deny. */
+ FALSE; /* Allow. */
+#endif
+}
+
+#endif /* _VMCI_COMMONINT_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciContext.c --
+ *
+ * Platform independent routines for VMCI calls.
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+# include "driver-config.h"
+# include "compat_kernel.h"
+# include "compat_module.h"
+#endif // __linux__
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciContext.h"
+#include "vmciDatagram.h"
+#include "vmciDoorbell.h"
+#include "vmciEvent.h"
+#include "vmciKernelAPI.h"
+#include "vmciQueuePair.h"
+#include "vmciResource.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
+
+#define LGPFX "VMCIContext: "
+
+static void VMCIContextFreeContext(VMCIContext *context);
+static Bool VMCIContextExists(VMCIId cid);
+static int VMCIContextFireNotification(VMCIId contextID,
+ VMCIPrivilegeFlags privFlags,
+ const char *domain);
+
+/*
+ * List of current VMCI contexts.
+ */
+
+static struct {
+ VMCIList head;
+ VMCILock lock;
+ VMCILock firingLock;
+} contextList;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextSignalNotify --
+ *
+ * Sets the notify flag to TRUE. Assumes that the context lock is
+ * held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIContextSignalNotify(VMCIContext *context) // IN:
+{
+#ifndef VMX86_SERVER
+ if (context->notify) {
+ *context->notify = TRUE;
+ }
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextClearNotify --
+ *
+ * Sets the notify flag to FALSE. Assumes that the context lock is
+ * held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIContextClearNotify(VMCIContext *context) // IN:
+{
+#ifndef VMX86_SERVER
+ if (context->notify) {
+ *context->notify = FALSE;
+ }
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextClearNotifyAndCall --
+ *
+ * If nothing requires the attention of the guest, clears both
+ * notify flag and call.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIContextClearNotifyAndCall(VMCIContext *context) // IN:
+{
+ if (context->pendingDatagrams == 0 &&
+ VMCIHandleArray_GetSize(context->pendingDoorbellArray) == 0) {
+ VMCIHost_ClearCall(&context->hostContext);
+ VMCIContextClearNotify(context);
+ }
+}
+
+
+#ifndef VMX86_SERVER
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_CheckAndSignalNotify --
+ *
+ * Sets the context's notify flag iff datagrams are pending for this
+ * context. Called from VMCISetupNotify().
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_CheckAndSignalNotify(VMCIContext *context) // IN:
+{
+ VMCILockFlags flags;
+
+ ASSERT(context);
+ VMCI_GrabLock(&contextList.lock, &flags);
+ if (context->pendingDatagrams) {
+ VMCIContextSignalNotify(context);
+ }
+ VMCI_ReleaseLock(&contextList.lock, flags);
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextGetDomainName --
+ *
+ * Internal function for retrieving a context domain name, if
+ * supported by the platform. The returned pointer can only be
+ * assumed valid while a reference count is held on the given
+ * context.
+ *
+ * Results:
+ * Pointer to name if appropriate. NULL otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE char *
+VMCIContextGetDomainName(VMCIContext *context) // IN
+{
+#ifdef VMKERNEL
+ return context->domainName;
+#else
+ return NULL;
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_Init --
+ *
+ * Initializes the VMCI context module.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_Init(void)
+{
+ VMCIList_Init(&contextList.head);
+ VMCI_InitLock(&contextList.lock, "VMCIContextListLock",
+ VMCI_LOCK_RANK_HIGHER);
+ VMCI_InitLock(&contextList.firingLock, "VMCIContextFiringLock",
+ VMCI_LOCK_RANK_MIDDLE_LOW);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_Exit --
+ *
+ * Cleans up the contexts module.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_Exit(void)
+{
+ VMCI_CleanupLock(&contextList.firingLock);
+ VMCI_CleanupLock(&contextList.lock);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_InitContext --
+ *
+ * Allocates and initializes a VMCI context.
+ *
+ * Results:
+ * Returns 0 on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_InitContext(VMCIId cid, // IN
+ VMCIPrivilegeFlags privFlags, // IN
+ uintptr_t eventHnd, // IN
+ int userVersion, // IN: User's vers no.
+ VMCIHostUser *user, // IN
+ VMCIContext **outContext) // OUT
+{
+ VMCILockFlags flags;
+ VMCIContext *context;
+ int result;
+
+ if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
+ VMCI_WARNING((LGPFX"Invalid flag for VMCI context.\n"));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (userVersion == 0) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCI_AllocKernelMem(sizeof *context, VMCI_MEMORY_NONPAGED);
+ if (context == NULL) {
+ VMCI_WARNING((LGPFX"Failed to allocate memory for VMCI context.\n"));
+ return VMCI_ERROR_NO_MEM;
+ }
+ memset(context, 0, sizeof *context);
+
+ VMCIList_InitEntry(&context->listItem);
+ VMCIList_Init(&context->datagramQueue);
+
+ context->userVersion = userVersion;
+
+ context->wellKnownArray = VMCIHandleArray_Create(0);
+ if (context->wellKnownArray == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->queuePairArray = VMCIHandleArray_Create(0);
+ if (!context->queuePairArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->doorbellArray = VMCIHandleArray_Create(0);
+ if (!context->doorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->pendingDoorbellArray = VMCIHandleArray_Create(0);
+ if (!context->pendingDoorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->notifierArray = VMCIHandleArray_Create(0);
+ if (context->notifierArray == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ VMCI_InitLock(&context->lock,
+ "VMCIContextLock",
+ VMCI_LOCK_RANK_HIGHER);
+ Atomic_Write(&context->refCount, 1);
+
+ /* Inititialize host-specific VMCI context. */
+ VMCIHost_InitContext(&context->hostContext, eventHnd);
+
+ context->privFlags = privFlags;
+
+ /*
+ * If we collide with an existing context we generate a new and use it
+ * instead. The VMX will determine if regeneration is okay. Since there
+ * isn't 4B - 16 VMs running on a given host, the below loop will terminate.
+ */
+ VMCI_GrabLock(&contextList.lock, &flags);
+ ASSERT(cid != VMCI_INVALID_ID);
+ while (VMCIContextExists(cid)) {
+
+ /*
+ * If the cid is below our limit and we collide we are creating duplicate
+ * contexts internally so we want to assert fail in that case.
+ */
+ ASSERT(cid >= VMCI_RESERVED_CID_LIMIT);
+
+ /* We reserve the lowest 16 ids for fixed contexts. */
+ cid = MAX(cid, VMCI_RESERVED_CID_LIMIT-1) + 1;
+ if (cid == VMCI_INVALID_ID) {
+ cid = VMCI_RESERVED_CID_LIMIT;
+ }
+ }
+ ASSERT(!VMCIContextExists(cid));
+ context->cid = cid;
+ context->validUser = user != NULL;
+ if (context->validUser) {
+ context->user = *user;
+ }
+ VMCIList_Insert(&context->listItem, &contextList.head);
+ VMCI_ReleaseLock(&contextList.lock, flags);
+
+#ifdef VMKERNEL
+ /*
+ * Set default domain name.
+ */
+ VMCIContext_SetDomainName(context, "");
+ VMCIContext_SetFSRState(context, FALSE, VMCI_INVALID_ID, eventHnd, FALSE);
+#endif
+
+#ifndef VMX86_SERVER
+ context->notify = NULL;
+# ifdef __linux__
+ context->notifyPage = NULL;
+# endif
+#endif
+
+ *outContext = context;
+ return VMCI_SUCCESS;
+
+error:
+ if (context->notifierArray) {
+ VMCIHandleArray_Destroy(context->notifierArray);
+ }
+ if (context->wellKnownArray) {
+ VMCIHandleArray_Destroy(context->wellKnownArray);
+ }
+ if (context->queuePairArray) {
+ VMCIHandleArray_Destroy(context->queuePairArray);
+ }
+ if (context->doorbellArray) {
+ VMCIHandleArray_Destroy(context->doorbellArray);
+ }
+ if (context->pendingDoorbellArray) {
+ VMCIHandleArray_Destroy(context->pendingDoorbellArray);
+ }
+ VMCI_FreeKernelMem(context, sizeof *context);
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_ReleaseContext --
+ *
+ * Cleans up a VMCI context.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_ReleaseContext(VMCIContext *context) // IN
+{
+ VMCILockFlags flags;
+
+ /* Dequeue VMCI context. */
+
+ VMCI_GrabLock(&contextList.lock, &flags);
+ VMCIList_Remove(&context->listItem, &contextList.head);
+ VMCI_ReleaseLock(&contextList.lock, flags);
+
+ VMCIContext_Release(context);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIContextFreeContext --
+ *
+ * Deallocates all parts of a context datastructure. This
+ * functions doesn't lock the context, because it assumes that
+ * the caller is holding the last reference to context. As paged
+ * memory may be freed as part of the call, the function must be
+ * called without holding any spinlocks as this is not allowed on
+ * Windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Paged memory is freed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VMCIContextFreeContext(VMCIContext *context) // IN
+{
+ VMCIListItem *curr;
+ VMCIListItem *next;
+ DatagramQueueEntry *dqEntry;
+ VMCIHandle tempHandle;
+
+ /* Fire event to all contexts interested in knowing this context is dying. */
+ VMCIContextFireNotification(context->cid, context->privFlags,
+ VMCIContextGetDomainName(context));
+
+ /*
+ * Cleanup all wellknown mappings owned by context. Ideally these would
+ * be removed already but we maintain this list to make sure no resources
+ * are leaked. It is updated by the VMCIDatagramAdd/RemoveWellKnownMap.
+ */
+ ASSERT(context->wellKnownArray);
+ tempHandle = VMCIHandleArray_RemoveTail(context->wellKnownArray);
+ while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) {
+ VMCIDatagramRemoveWellKnownMap(tempHandle.resource, context->cid);
+ tempHandle = VMCIHandleArray_RemoveTail(context->wellKnownArray);
+ }
+
+#ifndef VMKERNEL
+ /*
+ * Cleanup all queue pair resources attached to context. If the VM dies
+ * without cleaning up, this code will make sure that no resources are
+ * leaked.
+ */
+
+ tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0);
+ while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) {
+ VMCIQPBroker_Lock();
+ if (VMCIQPBroker_Detach(tempHandle, context, TRUE) < VMCI_SUCCESS) {
+ /*
+ * When VMCIQPBroker_Detach() succeeds it removes the handle from the
+ * array. If detach fails, we must remove the handle ourselves.
+ */
+ VMCIHandleArray_RemoveEntry(context->queuePairArray, tempHandle);
+ }
+ VMCIQPBroker_Unlock();
+ tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0);
+ }
+#else
+ /*
+ * On ESX, all entries in the queuePairArray have been cleaned up
+ * either by the regular VMCI device destroy path or by the world
+ * cleanup destroy path. We assert that no resources are leaked.
+ */
+
+ ASSERT(VMCI_HANDLE_EQUAL(VMCIHandleArray_GetEntry(context->queuePairArray, 0),
+ VMCI_INVALID_HANDLE));
+#endif /* !VMKERNEL */
+
+ /*
+ * It is fine to destroy this without locking the callQueue, as
+ * this is the only thread having a reference to the context.
+ */
+
+ VMCIList_ScanSafe(curr, next, &context->datagramQueue) {
+ dqEntry = VMCIList_Entry(curr, DatagramQueueEntry, listItem);
+ VMCIList_Remove(curr, &context->datagramQueue);
+ ASSERT(dqEntry && dqEntry->dg);
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ VMCI_FreeKernelMem(dqEntry->dg, dqEntry->dgSize);
+ VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry);
+ }
+
+ VMCIHandleArray_Destroy(context->notifierArray);
+ VMCIHandleArray_Destroy(context->wellKnownArray);
+ VMCIHandleArray_Destroy(context->queuePairArray);
+ VMCIHandleArray_Destroy(context->doorbellArray);
+ VMCIHandleArray_Destroy(context->pendingDoorbellArray);
+ VMCI_CleanupLock(&context->lock);
+ VMCIHost_ReleaseContext(&context->hostContext);
+#ifndef VMX86_SERVER
+# ifdef __linux__
+ /* TODO Windows and Mac OS. */
+ VMCIUnsetNotify(context);
+# endif
+#endif
+ VMCI_FreeKernelMem(context, sizeof *context);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_PendingDatagrams --
+ *
+ * Returns the current number of pending datagrams. The call may
+ * also serve as a synchronization point for the datagram queue,
+ * as no enqueue operations can occur concurrently.
+ *
+ * Results:
+ * Length of datagram queue for the given context.
+ *
+ * Side effects:
+ * Locks datagram queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_PendingDatagrams(VMCIId cid, // IN
+ uint32 *pending) // OUT
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+
+ context = VMCIContext_Get(cid);
+ if (context == NULL) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ if (pending) {
+ *pending = context->pendingDatagrams;
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ * We allow at least 1024 more event datagrams from the hypervisor past the
+ * normally allowed datagrams pending for a given context. We define this
+ * limit on event datagrams from the hypervisor to guard against DoS attack
+ * from a malicious VM which could repeatedly attach to and detach from a queue
+ * pair, causing events to be queued at the destination VM. However, the rate
+ * at which such events can be generated is small since it requires a VM exit
+ * and handling of queue pair attach/detach call at the hypervisor. Event
+ * datagrams may be queued up at the destination VM if it has interrupts
+ * disabled or if it is not draining events for some other reason. 1024
+ * datagrams is a grossly conservative estimate of the time for which
+ * interrupts may be disabled in the destination VM, but at the same time does
+ * not exacerbate the memory pressure problem on the host by much (size of each
+ * event datagram is small).
+ */
+
+#define VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE \
+ (VMCI_MAX_DATAGRAM_QUEUE_SIZE + \
+ 1024 * (sizeof(VMCIDatagram) + sizeof(VMCIEventData_Max)))
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_EnqueueDatagram --
+ *
+ * Queues a VMCI datagram for the appropriate target VM
+ * context.
+ *
+ * Results:
+ * Size of enqueued data on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_EnqueueDatagram(VMCIId cid, // IN: Target VM
+ VMCIDatagram *dg) // IN:
+{
+ DatagramQueueEntry *dqEntry;
+ VMCIContext *context;
+ VMCILockFlags flags;
+ VMCIHandle dgSrc;
+ size_t vmciDgSize;
+
+ ASSERT(dg);
+ vmciDgSize = VMCI_DG_SIZE(dg);
+ ASSERT(vmciDgSize <= VMCI_MAX_DG_SIZE);
+
+ /* Get the target VM's VMCI context. */
+ context = VMCIContext_Get(cid);
+ if (context == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid cid.\n"));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /* Allocate guest call entry and add it to the target VM's queue. */
+ dqEntry = VMCI_AllocKernelMem(sizeof *dqEntry, VMCI_MEMORY_NONPAGED);
+ if (dqEntry == NULL) {
+ VMCI_WARNING((LGPFX"Failed to allocate memory for datagram.\n"));
+ VMCIContext_Release(context);
+ return VMCI_ERROR_NO_MEM;
+ }
+ dqEntry->dg = dg;
+ dqEntry->dgSize = vmciDgSize;
+ dgSrc = dg->src;
+ VMCIList_InitEntry(&dqEntry->listItem);
+
+ VMCI_GrabLock(&context->lock, &flags);
+ /*
+ * We put a higher limit on datagrams from the hypervisor. If the pending
+ * datagram is not from hypervisor, then we check if enqueueing it would
+ * exceed the VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination. If the
+ * pending datagram is from hypervisor, we allow it to be queued at the
+ * destination side provided we don't reach the
+ * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit.
+ */
+ if (context->datagramQueueSize + vmciDgSize >=
+ VMCI_MAX_DATAGRAM_QUEUE_SIZE &&
+ (!VMCI_HANDLE_EQUAL(dgSrc,
+ VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
+ VMCI_CONTEXT_RESOURCE_ID)) ||
+ context->datagramQueueSize + vmciDgSize >=
+ VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) {
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+ VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry);
+ VMCI_DEBUG_LOG(10, (LGPFX"Context 0x%x receive queue is full.\n", cid));
+ return VMCI_ERROR_NO_RESOURCES;
+ }
+
+ VMCIList_Insert(&dqEntry->listItem, &context->datagramQueue);
+ context->pendingDatagrams++;
+ context->datagramQueueSize += vmciDgSize;
+ VMCIContextSignalNotify(context);
+ VMCIHost_SignalCall(&context->hostContext);
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ return vmciDgSize;
+}
+
+#undef VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextExists --
+ *
+ * Internal helper to check if a context with the specified context
+ * ID exists. Assumes the contextList.lock is held.
+ *
+ * Results:
+ * TRUE if a context exists with the given cid.
+ * FALSE otherwise
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool
+VMCIContextExists(VMCIId cid) // IN
+{
+ VMCIContext *context;
+ VMCIListItem *next;
+ Bool rv = FALSE;
+
+ VMCIList_Scan(next, &contextList.head) {
+ context = VMCIList_Entry(next, VMCIContext, listItem);
+ if (context->cid == cid) {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_Exists --
+ *
+ * Verifies whether a context with the specified context ID exists.
+ *
+ * Results:
+ * TRUE if a context exists with the given cid.
+ * FALSE otherwise
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Bool
+VMCIContext_Exists(VMCIId cid) // IN
+{
+ VMCILockFlags flags;
+ Bool rv;
+
+ VMCI_GrabLock(&contextList.lock, &flags);
+ rv = VMCIContextExists(cid);
+ VMCI_ReleaseLock(&contextList.lock, flags);
+ return rv;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_Get --
+ *
+ * Retrieves VMCI context corresponding to the given cid.
+ *
+ * Results:
+ * VMCI context on success, NULL otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCIContext *
+VMCIContext_Get(VMCIId cid) // IN
+{
+ VMCIContext *context = NULL;
+ VMCIListItem *next;
+ VMCILockFlags flags;
+
+ if (cid == VMCI_INVALID_ID) {
+ return NULL;
+ }
+
+ VMCI_GrabLock(&contextList.lock, &flags);
+ if (VMCIList_Empty(&contextList.head)) {
+ goto out;
+ }
+
+ VMCIList_Scan(next, &contextList.head) {
+ context = VMCIList_Entry(next, VMCIContext, listItem);
+ if (context->cid == cid) {
+ /*
+ * At this point, we are sure that the reference count is
+ * larger already than zero. When starting the destruction of
+ * a context, we always remove it from the context list
+ * before decreasing the reference count. As we found the
+ * context here, it hasn't been destroyed yet. This means
+ * that we are not about to increase the reference count of
+ * something that is in the process of being destroyed.
+ */
+
+ Atomic_Inc(&context->refCount);
+ break;
+ }
+ }
+
+out:
+ VMCI_ReleaseLock(&contextList.lock, flags);
+ return (context && context->cid == cid) ? context : NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_Release --
+ *
+ * Releases the VMCI context. If this is the last reference to
+ * the context it will be deallocated. A context is created with
+ * a reference count of one, and on destroy, it is removed from
+ * the context list before its reference count is
+ * decremented. Thus, if we reach zero, we are sure that nobody
+ * else are about to increment it (they need the entry in the
+ * context list for that). This function musn't be called with a
+ * lock held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Paged memory may be deallocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_Release(VMCIContext *context) // IN
+{
+ uint32 refCount;
+ ASSERT(context);
+ refCount = Atomic_FetchAndDec(&context->refCount);
+ if (refCount == 1) {
+ VMCIContextFreeContext(context);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_DequeueDatagram --
+ *
+ * Dequeues the next datagram and returns it to caller.
+ * The caller passes in a pointer to the max size datagram
+ * it can handle and the datagram is only unqueued if the
+ * size is less than maxSize. If larger maxSize is set to
+ * the size of the datagram to give the caller a chance to
+ * set up a larger buffer for the guestcall.
+ *
+ * Results:
+ * On success: 0 if no more pending datagrams, otherwise the size of
+ * the next pending datagram.
+ * On failure: appropriate error code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_DequeueDatagram(VMCIContext *context, // IN
+ size_t *maxSize, // IN/OUT: max size of datagram caller can handle.
+ VMCIDatagram **dg) // OUT:
+{
+ DatagramQueueEntry *dqEntry;
+ VMCIListItem *listItem;
+ VMCILockFlags flags;
+ int rv;
+
+ ASSERT(context && dg);
+
+ /* Dequeue the next datagram entry. */
+ VMCI_GrabLock(&context->lock, &flags);
+ if (context->pendingDatagrams == 0) {
+ VMCIContextClearNotifyAndCall(context);
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCI_DEBUG_LOG(4, (LGPFX"No datagrams pending.\n"));
+ return VMCI_ERROR_NO_MORE_DATAGRAMS;
+ }
+
+ listItem = VMCIList_First(&context->datagramQueue);
+ ASSERT (listItem != NULL);
+
+ dqEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem);
+ ASSERT(dqEntry->dg);
+
+ /* Check size of caller's buffer. */
+ if (*maxSize < dqEntry->dgSize) {
+ *maxSize = dqEntry->dgSize;
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCI_DEBUG_LOG(4, (LGPFX"Caller's buffer is too small. It must be at "
+ "least %"FMTSZ"d bytes.\n", *maxSize));
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ VMCIList_Remove(listItem, &context->datagramQueue);
+ context->pendingDatagrams--;
+ context->datagramQueueSize -= dqEntry->dgSize;
+ if (context->pendingDatagrams == 0) {
+ VMCIContextClearNotifyAndCall(context);
+ rv = VMCI_SUCCESS;
+ } else {
+ /*
+ * Return the size of the next datagram.
+ */
+ DatagramQueueEntry *nextEntry;
+
+ listItem = VMCIList_First(&context->datagramQueue);
+ ASSERT(listItem);
+ nextEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem);
+ ASSERT(nextEntry && nextEntry->dg);
+ /*
+ * The following size_t -> int truncation is fine as the maximum size of
+ * a (routable) datagram is 68KB.
+ */
+ rv = (int)nextEntry->dgSize;
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ /* Caller must free datagram. */
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ *dg = dqEntry->dg;
+ dqEntry->dg = NULL;
+ VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry);
+
+ return rv;
+}
+
+
+#ifdef VMKERNEL
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SetFSRState --
+ *
+ * Set the states related to FSR (quiesced state, migrateCid,
+ * active event handle).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_SetFSRState(VMCIContext *context, // IN
+ Bool isQuiesced, // IN
+ VMCIId migrateCid, // IN
+ uintptr_t eventHnd, // IN
+ Bool isLocked) // IN
+{
+ VMCILockFlags flags;
+ if (!context) {
+ return;
+ }
+ if (!isLocked) {
+ VMCI_GrabLock(&context->lock, &flags);
+ }
+ context->isQuiesced = isQuiesced;
+ context->migrateCid = migrateCid;
+ VMCIHost_SetActiveHnd(&context->hostContext, eventHnd);
+ if (!isLocked) {
+ VMCI_ReleaseLock(&context->lock, flags);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_FindAndUpdateSrcFSR --
+ *
+ * Find the source context for fast-suspend-resume. If found, the
+ * source context's FSR state is changed to reflect the new active
+ * event handle.
+ *
+ * Results:
+ * If found, source context for fast-suspend-resume, NULL otherwise.
+ *
+ * Side effects:
+ * The source context reference count increased and the caller is
+ * supposed to release the context once it is done using it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCIContext *
+VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid, // IN
+ uintptr_t eventHnd, // IN
+ uintptr_t *srcEventHnd) // IN/OUT
+{
+ VMCIContext *contextSrc = VMCIContext_Get(migrateCid);
+
+ if (contextSrc) {
+ VMCILockFlags flags;
+
+ VMCI_GrabLock(&contextSrc->lock, &flags);
+ if (contextSrc->isQuiesced && contextSrc->migrateCid == migrateCid) {
+ if (srcEventHnd) {
+ *srcEventHnd = VMCIHost_GetActiveHnd(&contextSrc->hostContext);
+ ASSERT(*srcEventHnd != VMCI_INVALID_ID);
+ }
+ VMCIContext_SetFSRState(contextSrc, FALSE, VMCI_INVALID_ID,
+ eventHnd, TRUE);
+ VMCI_ReleaseLock(&contextSrc->lock, flags);
+ return contextSrc;
+ }
+ VMCI_ReleaseLock(&contextSrc->lock, flags);
+ VMCIContext_Release(contextSrc);
+ }
+ return NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_IsActiveHnd --
+ *
+ * Whether the curent event handle is the active handle.
+ *
+ * Results:
+ * TRUE if the event handle is active, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Bool
+VMCIContext_IsActiveHnd(VMCIContext *context, // IN
+ uintptr_t eventHnd) // IN
+{
+ VMCILockFlags flags;
+ Bool isActive;
+
+ ASSERT(context);
+ VMCI_GrabLock(&context->lock, &flags);
+ isActive = VMCIHost_IsActiveHnd(&context->hostContext, eventHnd);
+ VMCI_ReleaseLock(&context->lock, flags);
+ return isActive;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SetInactiveHnd --
+ *
+ * Set the handle to be the inactive one.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_SetInactiveHnd(VMCIContext *context, // IN
+ uintptr_t eventHnd) // IN
+{
+ VMCILockFlags flags;
+
+ ASSERT(context);
+ VMCI_GrabLock(&context->lock, &flags);
+ VMCIHost_SetInactiveHnd(&context->hostContext, eventHnd);
+ VMCI_ReleaseLock(&context->lock, flags);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_RemoveHnd --
+ *
+ * Remove the event handle from host context.
+ *
+ * Results:
+ * Whether the handle exists and removed, also number of handles
+ * before removal and number of handles after removal.
+ *
+ * Side effects:
+ * If this is active handle, the inactive handle becomes active.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Bool
+VMCIContext_RemoveHnd(VMCIContext *context, // IN
+ uintptr_t eventHnd, // IN
+ uint32 *numOld, // OUT
+ uint32 *numNew) // OUT
+{
+ VMCILockFlags flags;
+ uint32 numHandleOld, numHandleNew;
+ Bool ret;
+
+ ASSERT(context);
+ VMCI_GrabLock(&context->lock, &flags);
+ numHandleOld = VMCIHost_NumHnds(&context->hostContext);
+ ret = VMCIHost_RemoveHnd(&context->hostContext, eventHnd);
+ numHandleNew = VMCIHost_NumHnds(&context->hostContext);
+ /*
+ * This is needed to prevent FSR to share this
+ * context while this context is being destroyed.
+ */
+ if (ret && numHandleOld == 1 && numHandleNew == 1) {
+ context->migrateCid = VMCI_INVALID_ID;
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ if (numOld) {
+ *numOld = numHandleOld;
+ }
+ if (numNew) {
+ *numNew = numHandleNew;
+ }
+ return ret;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIContext_ClearDatagrams --
+ *
+ * Clear pending datagrams.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIContext_ClearDatagrams(VMCIContext *context) // IN
+{
+ int retval;
+ VMCIDatagram *dg = NULL;
+ size_t size = VMCI_MAX_DG_SIZE;
+ uint32 pending;
+
+ /* Drop all datagrams that are currently pending for given context. */
+ if (context == NULL) {
+ return;
+ }
+ retval = VMCIContext_PendingDatagrams(context->cid, &pending);
+ if (retval != VMCI_SUCCESS) {
+ /*
+ * This shouldn't happen as we already verified that the context
+ * exists.
+ */
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ /*
+ * We drain the queue for any datagrams pending at the beginning of
+ * the loop. As datagrams may arrive at any point in time, we
+ * cannot guarantee that the queue is empty after this point. Only
+ * removing a fixed number of pending datagrams prevents us from
+ * looping forever.
+ */
+
+ while (pending > 0 &&
+ VMCIContext_DequeueDatagram(context, &size, &dg) >= 0) {
+ ASSERT(dg);
+ VMCI_FreeKernelMem(dg, VMCI_DG_SIZE(dg));
+ --pending;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SetId --
+ *
+ * Set the cid of given VMCI context.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_SetId(VMCIContext *context, // IN
+ VMCIId cid) // IN:
+{
+ VMCILockFlags flags;
+
+ if (!context) {
+ return;
+ }
+ VMCI_GrabLock(&context->lock, &flags);
+ context->cid = cid;
+ VMCI_ReleaseLock(&context->lock, flags);
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_GetId --
+ *
+ * Retrieves cid of given VMCI context.
+ *
+ * Results:
+ * VMCIId of context on success, VMCI_INVALID_ID otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCIId
+VMCIContext_GetId(VMCIContext *context) // IN:
+{
+ if (!context) {
+ return VMCI_INVALID_ID;
+ }
+ ASSERT(context->cid != VMCI_INVALID_ID);
+ return context->cid;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_GetPrivFlags --
+ *
+ * Retrieves the privilege flags of the given VMCI context ID.
+ *
+ * Results:
+ * Context's privilege flags.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIContext_GetPrivFlags)
+VMCIPrivilegeFlags
+VMCIContext_GetPrivFlags(VMCIId contextID) // IN
+{
+#if defined(VMX86_TOOLS)
+ return VMCI_NO_PRIVILEGE_FLAGS;
+#else // VMX86_TOOLS
+ VMCIPrivilegeFlags flags;
+ VMCIContext *context;
+
+ context = VMCIContext_Get(contextID);
+ if (!context) {
+ return VMCI_LEAST_PRIVILEGE_FLAGS;
+ }
+ flags = context->privFlags;
+ VMCIContext_Release(context);
+ return flags;
+#endif // VMX86_TOOLS
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_AddWellKnown --
+ *
+ * Wrapper to call VMCIHandleArray_AppendEntry().
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * As in VMCIHandleArray_AppendEntry().
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_AddWellKnown(VMCIId contextID, // IN:
+ VMCIId wellKnownID) // IN:
+{
+ VMCILockFlags flags;
+ VMCIHandle wkHandle;
+ VMCIContext *context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, wellKnownID);
+ VMCI_GrabLock(&context->lock, &flags);
+ VMCIHandleArray_AppendEntry(&context->wellKnownArray, wkHandle);
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_RemoveWellKnown --
+ *
+ * Wrapper to call VMCIHandleArray_RemoveEntry().
+ *
+ * Results:
+ * VMCI_SUCCESS if removed, error code otherwise.
+ *
+ * Side effects:
+ * As in VMCIHandleArray_RemoveEntry().
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_RemoveWellKnown(VMCIId contextID, // IN:
+ VMCIId wellKnownID) // IN:
+{
+ VMCILockFlags flags;
+ VMCIHandle wkHandle, tmpHandle;
+ VMCIContext *context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, wellKnownID);
+ VMCI_GrabLock(&context->lock, &flags);
+ tmpHandle = VMCIHandleArray_RemoveEntry(context->wellKnownArray, wkHandle);
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE)) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_AddNotification --
+ *
+ * Add remoteCID to list of contexts current contexts wants
+ * notifications from/about.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * As in VMCIHandleArray_AppendEntry().
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_AddNotification(VMCIId contextID, // IN:
+ VMCIId remoteCID) // IN:
+{
+ int result = VMCI_ERROR_ALREADY_EXISTS;
+ VMCILockFlags flags;
+ VMCILockFlags firingFlags;
+ VMCIHandle notifierHandle;
+ VMCIContext *context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+
+ notifierHandle = VMCI_MAKE_HANDLE(remoteCID, VMCI_EVENT_HANDLER);
+ VMCI_GrabLock(&contextList.firingLock, &firingFlags);
+ VMCI_GrabLock(&context->lock, &flags);
+ if (!VMCIHandleArray_HasEntry(context->notifierArray, notifierHandle)) {
+ VMCIHandleArray_AppendEntry(&context->notifierArray, notifierHandle);
+ result = VMCI_SUCCESS;
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCI_ReleaseLock(&contextList.firingLock, firingFlags);
+out:
+ VMCIContext_Release(context);
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_RemoveNotification --
+ *
+ * Remove remoteCID from current context's list of contexts it is
+ * interested in getting notifications from/about.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_RemoveNotification(VMCIId contextID, // IN:
+ VMCIId remoteCID) // IN:
+{
+ VMCILockFlags flags;
+ VMCILockFlags firingFlags;
+ VMCIContext *context = VMCIContext_Get(contextID);
+ VMCIHandle tmpHandle;
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ VMCI_GrabLock(&contextList.firingLock, &firingFlags);
+ VMCI_GrabLock(&context->lock, &flags);
+ tmpHandle =
+ VMCIHandleArray_RemoveEntry(context->notifierArray,
+ VMCI_MAKE_HANDLE(remoteCID,
+ VMCI_EVENT_HANDLER));
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCI_ReleaseLock(&contextList.firingLock, firingFlags);
+ VMCIContext_Release(context);
+
+ if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE)) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContextFireNotification --
+ *
+ * Fire notification for all contexts interested in given cid.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+VMCIContextFireNotification(VMCIId contextID, // IN
+ VMCIPrivilegeFlags privFlags, // IN
+ const char *domain) // IN
+{
+ uint32 i, arraySize;
+ VMCIListItem *next;
+ VMCILockFlags flags;
+ VMCILockFlags firingFlags;
+ VMCIHandleArray *subscriberArray;
+ VMCIHandle contextHandle = VMCI_MAKE_HANDLE(contextID, VMCI_EVENT_HANDLER);
+
+ /*
+ * We create an array to hold the subscribers we find when scanning through
+ * all contexts.
+ */
+ subscriberArray = VMCIHandleArray_Create(0);
+ if (subscriberArray == NULL) {
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ /*
+ * Scan all contexts to find who is interested in being notified about
+ * given contextID. We have a special firingLock that we use to synchronize
+ * across all notification operations. This avoids us having to take the
+ * context lock for each HasEntry call and it solves a lock ranking issue.
+ */
+ VMCI_GrabLock(&contextList.firingLock, &firingFlags);
+ VMCI_GrabLock(&contextList.lock, &flags);
+ VMCIList_Scan(next, &contextList.head) {
+ VMCIContext *subCtx = VMCIList_Entry(next, VMCIContext, listItem);
+
+ /*
+ * We only deliver notifications of the removal of contexts, if
+ * the two contexts are allowed to interact.
+ */
+
+ if (VMCIHandleArray_HasEntry(subCtx->notifierArray, contextHandle) &&
+ !VMCIDenyInteraction(privFlags, subCtx->privFlags, domain,
+ VMCIContextGetDomainName(subCtx))) {
+ VMCIHandleArray_AppendEntry(&subscriberArray,
+ VMCI_MAKE_HANDLE(subCtx->cid,
+ VMCI_EVENT_HANDLER));
+ }
+ }
+ VMCI_ReleaseLock(&contextList.lock, flags);
+ VMCI_ReleaseLock(&contextList.firingLock, firingFlags);
+
+ /* Fire event to all subscribers. */
+ arraySize = VMCIHandleArray_GetSize(subscriberArray);
+ for (i = 0; i < arraySize; i++) {
+ int result;
+ VMCIEventMsg *eMsg;
+ VMCIEventPayload_Context *evPayload;
+ char buf[sizeof *eMsg + sizeof *evPayload];
+
+ eMsg = (VMCIEventMsg *)buf;
+
+ /* Clear out any garbage. */
+ memset(eMsg, 0, sizeof *eMsg + sizeof *evPayload);
+ eMsg->hdr.dst = VMCIHandleArray_GetEntry(subscriberArray, i);
+ eMsg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
+ VMCI_CONTEXT_RESOURCE_ID);
+ eMsg->hdr.payloadSize = sizeof *eMsg + sizeof *evPayload -
+ sizeof eMsg->hdr;
+ eMsg->eventData.event = VMCI_EVENT_CTX_REMOVED;
+ evPayload = VMCIEventMsgPayload(eMsg);
+ evPayload->contextID = contextID;
+
+ result = VMCIDatagram_Dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
+ (VMCIDatagram *)eMsg, FALSE);
+ if (result < VMCI_SUCCESS) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to enqueue event datagram %d for "
+ "context %d.\n",
+ eMsg->eventData.event, eMsg->hdr.dst.context));
+ /* We continue to enqueue on next subscriber. */
+ }
+ }
+ VMCIHandleArray_Destroy(subscriberArray);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_GetCheckpointState --
+ *
+ * Get current context's checkpoint state of given type.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_GetCheckpointState(VMCIId contextID, // IN:
+ uint32 cptType, // IN:
+ uint32 *bufSize, // IN/OUT:
+ char **cptBufPtr) // OUT:
+{
+ int i, result;
+ VMCILockFlags flags;
+ uint32 arraySize, cptDataSize;
+ VMCIHandleArray *array;
+ VMCIContext *context;
+ char *cptBuf;
+ Bool getContextID;
+
+ ASSERT(bufSize && cptBufPtr);
+
+ context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ if (cptType == VMCI_NOTIFICATION_CPT_STATE) {
+ ASSERT(context->notifierArray);
+ array = context->notifierArray;
+ getContextID = TRUE;
+ } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) {
+ ASSERT(context->wellKnownArray);
+ array = context->wellKnownArray;
+ getContextID = FALSE;
+ } else if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ASSERT(context->doorbellArray);
+ array = context->doorbellArray;
+ getContextID = FALSE;
+ } else {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state type %d.\n", cptType));
+ result = VMCI_ERROR_INVALID_ARGS;
+ goto release;
+ }
+
+ arraySize = VMCIHandleArray_GetSize(array);
+ if (arraySize > 0) {
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ cptDataSize = arraySize * sizeof(VMCIDoorbellCptState);
+ } else {
+ cptDataSize = arraySize * sizeof(VMCIId);
+ }
+ if (*bufSize < cptDataSize) {
+ *bufSize = cptDataSize;
+ result = VMCI_ERROR_MORE_DATA;
+ goto release;
+ }
+
+ cptBuf = VMCI_AllocKernelMem(cptDataSize,
+ VMCI_MEMORY_NONPAGED | VMCI_MEMORY_ATOMIC);
+ if (cptBuf == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto release;
+ }
+
+ for (i = 0; i < arraySize; i++) {
+ VMCIHandle tmpHandle = VMCIHandleArray_GetEntry(array, i);
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ((VMCIDoorbellCptState *)cptBuf)[i].handle = tmpHandle;
+ } else {
+ ((VMCIId *)cptBuf)[i] =
+ getContextID ? tmpHandle.context : tmpHandle.resource;
+ }
+ }
+ *bufSize = cptDataSize;
+ *cptBufPtr = cptBuf;
+ } else {
+ *bufSize = 0;
+ *cptBufPtr = NULL;
+ }
+ result = VMCI_SUCCESS;
+
+ release:
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SetCheckpointState --
+ *
+ * Set current context's checkpoint state of given type.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_SetCheckpointState(VMCIId contextID, // IN:
+ uint32 cptType, // IN:
+ uint32 bufSize, // IN:
+ char *cptBuf) // IN:
+{
+ uint32 i;
+ VMCIId currentID;
+ int result = VMCI_SUCCESS;
+ uint32 numIDs = bufSize / sizeof(VMCIId);
+ ASSERT(cptBuf);
+
+ if (cptType != VMCI_NOTIFICATION_CPT_STATE &&
+ cptType != VMCI_WELLKNOWN_CPT_STATE) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state type %d.\n", cptType));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ for (i = 0; i < numIDs && result == VMCI_SUCCESS; i++) {
+ currentID = ((VMCIId *)cptBuf)[i];
+ if (cptType == VMCI_NOTIFICATION_CPT_STATE) {
+ result = VMCIContext_AddNotification(contextID, currentID);
+ } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) {
+ result = VMCIDatagramRequestWellKnownMap(currentID, contextID,
+ VMCIContext_GetPrivFlags(contextID));
+ }
+ }
+ if (result != VMCI_SUCCESS) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to set cpt state type %d, error %d.\n",
+ cptType, result));
+ }
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_ReceiveNotificationsGet --
+ *
+ * Retrieves the specified context's pending notifications in the
+ * form of a handle array. The handle arrays returned are the
+ * actual data - not a copy and should not be modified by the
+ * caller. They must be released using
+ * VMCIContext_ReceiveNotificationsRelease.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_ReceiveNotificationsGet(VMCIId contextID, // IN
+ VMCIHandleArray **dbHandleArray, // OUT
+ VMCIHandleArray **qpHandleArray) // OUT
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+ int result = VMCI_SUCCESS;
+
+ ASSERT(dbHandleArray && qpHandleArray);
+
+ context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ VMCI_GrabLock(&context->lock, &flags);
+
+ *dbHandleArray = context->pendingDoorbellArray;
+ context->pendingDoorbellArray = VMCIHandleArray_Create(0);
+ if (!context->pendingDoorbellArray) {
+ context->pendingDoorbellArray = *dbHandleArray;
+ *dbHandleArray = NULL;
+ result = VMCI_ERROR_NO_MEM;
+ }
+ *qpHandleArray = NULL;
+
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_ReceiveNotificationsRelease --
+ *
+ * Releases handle arrays with pending notifications previously
+ * retrieved using VMCIContext_ReceiveNotificationsGet. If the
+ * notifications were not successfully handed over to the guest,
+ * success must be false.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_ReceiveNotificationsRelease(VMCIId contextID, // IN
+ VMCIHandleArray *dbHandleArray, // IN
+ VMCIHandleArray *qpHandleArray, // IN
+ Bool success) // IN
+{
+ VMCIContext *context = VMCIContext_Get(contextID);
+
+ if (context) {
+ VMCILockFlags flags;
+
+ VMCI_GrabLock(&context->lock, &flags);
+ if (!success) {
+ VMCIHandle handle;
+
+ /*
+ * New notifications may have been added while we were not
+ * holding the context lock, so we transfer any new pending
+ * doorbell notifications to the old array, and reinstate the
+ * old array.
+ */
+
+ handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray);
+ while (!VMCI_HANDLE_INVALID(handle)) {
+ ASSERT(VMCIHandleArray_HasEntry(context->doorbellArray, handle));
+ if (!VMCIHandleArray_HasEntry(dbHandleArray, handle)) {
+ VMCIHandleArray_AppendEntry(&dbHandleArray, handle);
+ }
+ handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray);
+ }
+ VMCIHandleArray_Destroy(context->pendingDoorbellArray);
+ context->pendingDoorbellArray = dbHandleArray;
+ dbHandleArray = NULL;
+ } else {
+ VMCIContextClearNotifyAndCall(context);
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+ VMCIContext_Release(context);
+ } else {
+ /*
+ * The OS driver part is holding on to the context for the
+ * duration of the receive notification ioctl, so it should
+ * still be here.
+ */
+
+ ASSERT(FALSE);
+ }
+
+ if (dbHandleArray) {
+ VMCIHandleArray_Destroy(dbHandleArray);
+ }
+ if (qpHandleArray) {
+ VMCIHandleArray_Destroy(qpHandleArray);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_DoorbellCreate --
+ *
+ * Registers that a new doorbell handle has been allocated by the
+ * context. Only doorbell handles registered can be notified.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherewise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_DoorbellCreate(VMCIId contextID, // IN
+ VMCIHandle handle) // IN
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+ int result;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ if (!VMCIHandleArray_HasEntry(context->doorbellArray, handle)) {
+ VMCIHandleArray_AppendEntry(&context->doorbellArray, handle);
+ result = VMCI_SUCCESS;
+ } else {
+ result = VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ VMCIContext_Release(context);
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_DoorbellDestroy --
+ *
+ * Unregisters a doorbell handle that was previously registered
+ * with VMCIContext_DoorbellCreate.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherewise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_DoorbellDestroy(VMCIId contextID, // IN
+ VMCIHandle handle) // IN
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+ VMCIHandle removedHandle;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ removedHandle = VMCIHandleArray_RemoveEntry(context->doorbellArray, handle);
+ VMCIHandleArray_RemoveEntry(context->pendingDoorbellArray, handle);
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ VMCIContext_Release(context);
+
+ if (VMCI_HANDLE_INVALID(removedHandle)) {
+ return VMCI_ERROR_NOT_FOUND;
+ } else {
+ return VMCI_SUCCESS;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_DoorbellDestroyAll --
+ *
+ * Unregisters all doorbell handles that were previously
+ * registered with VMCIContext_DoorbellCreate.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherewise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_DoorbellDestroyAll(VMCIId contextID) // IN
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+ VMCIHandle removedHandle;
+
+ if (contextID == VMCI_INVALID_ID) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCIContext_Get(contextID);
+ if (context == NULL) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ do {
+ removedHandle = VMCIHandleArray_RemoveTail(context->doorbellArray);
+ } while(!VMCI_HANDLE_INVALID(removedHandle));
+ do {
+ removedHandle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray);
+ } while(!VMCI_HANDLE_INVALID(removedHandle));
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ VMCIContext_Release(context);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_NotifyDoorbell --
+ *
+ * Registers a notification of a doorbell handle initiated by the
+ * specified source context. The notification of doorbells are
+ * subject to the same isolation rules as datagram delivery. To
+ * allow host side senders of notifications a finer granularity
+ * of sender rights than those assigned to the sending context
+ * itself, the host context is required to specify a different
+ * set of privilege flags that will override the privileges of
+ * the source context.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherewise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_NotifyDoorbell(VMCIId srcCID, // IN
+ VMCIHandle handle, // IN
+ VMCIPrivilegeFlags srcPrivFlags) // IN
+{
+ VMCIContext *dstContext;
+ VMCILockFlags flags;
+ int result;
+
+ if (VMCI_HANDLE_INVALID(handle)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /* Get the target VM's VMCI context. */
+ dstContext = VMCIContext_Get(handle.context);
+ if (dstContext == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid cid.\n"));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (srcCID != handle.context) {
+ VMCIPrivilegeFlags dstPrivFlags;
+#if !defined(VMKERNEL)
+ char *srcDomain = NULL;
+#else
+ char srcDomain[VMCI_DOMAIN_NAME_MAXLEN];
+
+ result = VMCIContext_GetDomainName(srcCID, srcDomain, sizeof srcDomain);
+ if (result < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to get domain name for source context %u.\n",
+ srcCID));
+ goto out;
+ }
+#endif
+ result = VMCIDoorbellGetPrivFlags(handle, &dstPrivFlags);
+ if (result < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to get privilege flags for destination "
+ "handle 0x%x:0x%x.\n", handle.context, handle.resource));
+ goto out;
+ }
+
+ if (srcCID != VMCI_HOST_CONTEXT_ID ||
+ srcPrivFlags == VMCI_NO_PRIVILEGE_FLAGS) {
+ srcPrivFlags = VMCIContext_GetPrivFlags(srcCID);
+ }
+
+ if (VMCIDenyInteraction(srcPrivFlags, dstPrivFlags, srcDomain,
+ VMCIContextGetDomainName(dstContext))) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+ }
+
+ if (handle.context == VMCI_HOST_CONTEXT_ID) {
+ result = VMCIDoorbellHostContextNotify(srcCID, handle);
+ } else {
+ VMCI_GrabLock(&dstContext->lock, &flags);
+
+ if (!VMCIHandleArray_HasEntry(dstContext->doorbellArray, handle)) {
+ result = VMCI_ERROR_NOT_FOUND;
+ } else {
+ if (!VMCIHandleArray_HasEntry(dstContext->pendingDoorbellArray, handle)) {
+ VMCIHandleArray_AppendEntry(&dstContext->pendingDoorbellArray, handle);
+
+ VMCIContextSignalNotify(dstContext);
+#if defined(VMKERNEL)
+ VMCIHost_SignalBitmap(&dstContext->hostContext);
+#else
+ VMCIHost_SignalCall(&dstContext->hostContext);
+#endif
+ }
+ result = VMCI_SUCCESS;
+ }
+ VMCI_ReleaseLock(&dstContext->lock, flags);
+ }
+
+out:
+ VMCIContext_Release(dstContext);
+
+ return result;
+}
+
+
+#ifdef VMKERNEL
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SignalPendingDoorbells --
+ *
+ * Signals the guest if any doorbell notifications are
+ * pending. This is used after the VMCI device is unquiesced to
+ * ensure that no pending notifications go unnoticed, since
+ * signals may not be fully processed while the device is
+ * quiesced.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+VMCIContext_SignalPendingDoorbells(VMCIId contextID)
+{
+ VMCIContext *context;
+ VMCILockFlags flags;
+ Bool pending;
+
+ context = VMCIContext_Get(contextID);
+ if (!context) {
+ ASSERT(FALSE);
+ return;
+ }
+
+ VMCI_GrabLock(&context->lock, &flags);
+ pending = VMCIHandleArray_GetSize(context->pendingDoorbellArray) > 0;
+ VMCI_ReleaseLock(&context->lock, flags);
+
+ if (pending) {
+ VMCIHost_SignalBitmap(&context->hostContext);
+ }
+
+ VMCIContext_Release(context);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SetDomainName --
+ *
+ * Sets the domain name of the given context.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_SetDomainName(VMCIContext *context, // IN;
+ const char *domainName) // IN:
+{
+ size_t domainNameLen;
+
+ if (!context || !domainName) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ domainNameLen = strlen(domainName);
+ if (domainNameLen >= sizeof context->domainName) {
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ memcpy(context->domainName, domainName, domainNameLen + 1);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_GetDomainName --
+ *
+ * Returns the domain name of the given context.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIContext_GetDomainName(VMCIId contextID, // IN:
+ char *domainName, // OUT:
+ size_t domainNameBufSize) // IN:
+{
+ VMCIContext *context;
+ int rv = VMCI_SUCCESS;
+ size_t domainNameLen;
+
+ if (contextID == VMCI_INVALID_ID || !domainName || !domainNameBufSize) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCIContext_Get(contextID);
+ if (!context) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ domainNameLen = strlen(context->domainName);
+ if (domainNameLen >= domainNameBufSize) {
+ rv = VMCI_ERROR_NO_MEM;
+ goto out;
+ }
+
+ memcpy(domainName, context->domainName, domainNameLen + 1);
+
+out:
+ VMCIContext_Release(context);
+ return rv;
+}
+
+
+#endif // defined(VMKERNEL)
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCI_ContextID2HostVmID --
+ *
+ * Maps a context ID to the host specific (process/world) ID
+ * of the VM/VMX.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCI_ContextID2HostVmID)
+int
+VMCI_ContextID2HostVmID(VMCIId contextID, // IN
+ void *hostVmID, // OUT
+ size_t hostVmIDLen) // IN
+{
+#if defined(VMKERNEL)
+ VMCIContext *context;
+ VMCIHostVmID vmID;
+ int result;
+
+ context = VMCIContext_Get(contextID);
+ if (!context) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ result = VMCIHost_ContextToHostVmID(&context->hostContext, &vmID);
+ if (result == VMCI_SUCCESS) {
+ if (sizeof vmID == hostVmIDLen) {
+ memcpy(hostVmID, &vmID, hostVmIDLen);
+ } else {
+ result = VMCI_ERROR_INVALID_ARGS;
+ }
+ }
+
+ VMCIContext_Release(context);
+
+ return result;
+#else // !defined(VMKERNEL)
+ return VMCI_ERROR_UNAVAILABLE;
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCI_IsContextOwner --
+ *
+ * Determines whether a given host OS specific representation of
+ * user is the owner of the VM/VMX.
+ *
+ * Results:
+ * VMCI_SUCCESS if the hostUser is owner, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCI_IsContextOwner)
+int
+VMCI_IsContextOwner(VMCIId contextID, // IN
+ void *hostUser) // IN
+{
+#if defined(VMX86_TOOLS)
+ return VMCI_ERROR_UNAVAILABLE;
+#else // VMX86_TOOLS
+ VMCIContext *context;
+ VMCIHostUser *user = (VMCIHostUser *)hostUser;
+ int retval;
+
+ if (vmkernel) {
+ return VMCI_ERROR_UNAVAILABLE;
+ }
+
+ if (!hostUser) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ context = VMCIContext_Get(contextID);
+ if (!context) {
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ if (context->validUser) {
+ retval = VMCIHost_CompareUser(user, &context->user);
+ } else {
+ retval = VMCI_ERROR_UNAVAILABLE;
+ }
+ VMCIContext_Release(context);
+
+ return retval;
+#endif // VMX86_TOOLS
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIContext_SupportsHostQP --
+ *
+ * Can host QPs be connected to this user process. The answer is
+ * FALSE unless a sufficient version number has previously been set
+ * by this caller.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Bool
+VMCIContext_SupportsHostQP(VMCIContext *context) // IN: Context structure
+{
+#ifdef VMKERNEL
+ return TRUE;
+#else
+ if (!context || context->userVersion < VMCI_VERSION_HOSTQP) {
+ return FALSE;
+ }
+ return TRUE;
+#endif
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciContext.h --
+ *
+ * VMCI state to enable sending calls between VMs.
+ */
+
+#ifndef _VMCI_CONTEXT_H_
+#define _VMCI_CONTEXT_H_
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_defs.h"
+#include "vmci_call_defs.h"
+#include "vmci_handle_array.h"
+#include "vmci_infrastructure.h"
+
+#define MAX_QUEUED_GUESTCALLS_PER_VM 100
+
+typedef struct VMCIContext VMCIContext;
+
+int VMCIContext_Init(void);
+void VMCIContext_Exit(void);
+int VMCIContext_InitContext(VMCIId cid, VMCIPrivilegeFlags flags,
+ uintptr_t eventHnd, int version,
+ VMCIHostUser *user, VMCIContext **context);
+#ifdef VMKERNEL
+int VMCIContext_SetDomainName(VMCIContext *context, const char *domainName);
+int VMCIContext_GetDomainName(VMCIId contextID, char *domainName,
+ size_t domainNameBufSize);
+void VMCIContext_SetFSRState(VMCIContext *context,
+ Bool isQuiesced,
+ VMCIId migrateCid,
+ uintptr_t eventHnd,
+ Bool isLocked);
+VMCIContext *VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid,
+ uintptr_t eventHnd,
+ uintptr_t *srcEventHnd);
+Bool VMCIContext_IsActiveHnd(VMCIContext *context,
+ uintptr_t eventHnd);
+void VMCIContext_SetInactiveHnd(VMCIContext *context,
+ uintptr_t eventHnd);
+Bool VMCIContext_RemoveHnd(VMCIContext *context,
+ uintptr_t eventHnd,
+ uint32 *numOld,
+ uint32 *numNew);
+void VMCIContext_ClearDatagrams(VMCIContext *context);
+void VMCIContext_SetId(VMCIContext *context, VMCIId cid);
+#endif
+Bool VMCIContext_SupportsHostQP(VMCIContext *context);
+void VMCIContext_ReleaseContext(VMCIContext *context);
+int VMCIContext_EnqueueDatagram(VMCIId cid, VMCIDatagram *dg);
+int VMCIContext_DequeueDatagram(VMCIContext *context, size_t *maxSize,
+ VMCIDatagram **dg);
+int VMCIContext_PendingDatagrams(VMCIId cid, uint32 *pending);
+VMCIContext *VMCIContext_Get(VMCIId cid);
+void VMCIContext_Release(VMCIContext *context);
+Bool VMCIContext_Exists(VMCIId cid);
+
+VMCIId VMCIContext_GetId(VMCIContext *context);
+int VMCIContext_AddGroupEntry(VMCIContext *context,
+ VMCIHandle entryHandle);
+VMCIHandle VMCIContext_RemoveGroupEntry(VMCIContext *context,
+ VMCIHandle entryHandle);
+int VMCIContext_AddWellKnown(VMCIId contextID, VMCIId wellKnownID);
+int VMCIContext_RemoveWellKnown(VMCIId contextID, VMCIId wellKnownID);
+int VMCIContext_AddNotification(VMCIId contextID, VMCIId remoteCID);
+int VMCIContext_RemoveNotification(VMCIId contextID, VMCIId remoteCID);
+int VMCIContext_GetCheckpointState(VMCIId contextID, uint32 cptType,
+ uint32 *numCIDs, char **cptBufPtr);
+int VMCIContext_SetCheckpointState(VMCIId contextID, uint32 cptType,
+ uint32 numCIDs, char *cptBuf);
+#ifndef VMX86_SERVER
+void VMCIContext_CheckAndSignalNotify(VMCIContext *context);
+# ifdef __linux__
+/* TODO Windows and Mac OS. */
+void VMCIUnsetNotify(VMCIContext *context);
+# endif
+#endif
+
+int VMCIContext_DoorbellCreate(VMCIId contextID, VMCIHandle handle);
+int VMCIContext_DoorbellDestroy(VMCIId contextID, VMCIHandle handle);
+int VMCIContext_DoorbellDestroyAll(VMCIId contextID);
+int VMCIContext_NotifyDoorbell(VMCIId cid, VMCIHandle handle,
+ VMCIPrivilegeFlags srcPrivFlags);
+
+int VMCIContext_ReceiveNotificationsGet(VMCIId contextID,
+ VMCIHandleArray **dbHandleArray,
+ VMCIHandleArray **qpHandleArray);
+void VMCIContext_ReceiveNotificationsRelease(VMCIId contextID,
+ VMCIHandleArray *dbHandleArray,
+ VMCIHandleArray *qpHandleArray,
+ Bool success);
+#if defined(VMKERNEL)
+void VMCIContext_SignalPendingDoorbells(VMCIId contextID);
+
+int VMCIContextID2HostVmID(VMCIId contextID, void *hostVmID, size_t hostVmIDLen);
+#endif
+
+#endif // _VMCI_CONTEXT_H_
/*
* vmciDatagram.c --
*
- * Simple Datagram API for the guest driver.
+ * This file implements the VMCI Simple Datagram API on the host.
*/
-#ifdef __linux__
+#if defined(__linux__) && !defined(VMKERNEL)
# include "driver-config.h"
-# include <linux/module.h>
# include "compat_kernel.h"
-# include "compat_pci.h"
-#elif defined(_WIN32)
-# include <ntddk.h>
-#elif defined(SOLARIS)
-# include <sys/ddi.h>
-# include <sys/sunddi.h>
-#elif defined(__APPLE__)
-#else
-# error "Platform not support by VMCI datagram API."
-#endif // linux
-
-#define LGPFX "VMCIDatagram: "
-
+# include "compat_module.h"
+#endif // __linux__
#include "vmci_kernel_if.h"
-#include "vm_basic_types.h"
#include "vm_assert.h"
#include "vmci_defs.h"
#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciContext.h"
#include "vmciDatagram.h"
-#include "vmciInt.h"
+#include "vmciEvent.h"
+#include "vmciHashtable.h"
#include "vmciKernelAPI.h"
-#include "vmciUtil.h"
-
-typedef struct DatagramHashEntry {
- struct DatagramHashEntry *next;
- int refCount;
-
- VMCIHandle handle;
- uint32 flags;
- Bool runDelayed;
- VMCIDatagramRecvCB recvCB;
- void *clientData;
- VMCIEvent destroyEvent;
-} DatagramHashEntry;
+#include "vmciResource.h"
+#include "vmciRoute.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
-#define HASH_TABLE_SIZE 64
+#define LGPFX "VMCIDatagram: "
-#define VMCI_HASHRESOURCE(handle, size) \
- VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(handle), (size))
/*
- * Hash table containing all the datagram handles for this VM. It is
- * synchronized using a single lock but we should consider making it more
- * fine grained, e.g. a per bucket lock or per set of buckets' lock.
+ * DatagramEntry describes the datagram entity. It is used for datagram
+ * entities created only on the host.
*/
-
-typedef struct DatagramHashTable {
- VMCILock lock;
- DatagramHashEntry *entries[HASH_TABLE_SIZE];
-} DatagramHashTable;
+typedef struct DatagramEntry {
+ VMCIResource resource;
+ uint32 flags;
+ Bool runDelayed;
+ VMCIDatagramRecvCB recvCB;
+ void *clientData;
+ VMCIEvent destroyEvent;
+ VMCIPrivilegeFlags privFlags;
+} DatagramEntry;
+
+/* Mapping between wellknown resource and context. */
+typedef struct DatagramWKMapping {
+ VMCIHashEntry entry;
+ VMCIId contextID;
+} DatagramWKMapping;
typedef struct VMCIDelayedDatagramInfo {
- DatagramHashEntry *entry;
+ Bool inDGHostQueue;
+ DatagramEntry *entry;
VMCIDatagram msg;
} VMCIDelayedDatagramInfo;
+/* Wellknown mapping hashtable. */
+static VMCIHashTable *wellKnownTable = NULL;
+
+static Atomic_uint32 delayedDGHostQueueSize;
+
+static int VMCIDatagramGetPrivFlagsInt(VMCIId contextID, VMCIHandle handle,
+ VMCIPrivilegeFlags *privFlags);
+static void DatagramFreeCB(void *resource);
static int DatagramReleaseCB(void *clientData);
-static int DatagramHashAddEntry(DatagramHashEntry *entry, VMCIId contextID);
-static int DatagramHashRemoveEntry(VMCIHandle handle);
-static DatagramHashEntry *DatagramHashGetEntry(VMCIHandle handle);
-static DatagramHashEntry *DatagramHashGetEntryAnyCid(VMCIHandle handle);
-static void DatagramHashReleaseEntry(DatagramHashEntry *entry);
-static Bool DatagramHandleUniqueLockedAnyCid(VMCIHandle handle);
-DatagramHashTable hashTable;
+static DatagramWKMapping *DatagramGetWellKnownMap(VMCIId wellKnownID);
+static void DatagramReleaseWellKnownMap(DatagramWKMapping *wkMap);
+
+
+/*------------------------------ Helper functions ----------------------------*/
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * DatagramFreeCB --
+ * Callback to free datagram structure when resource is no longer used,
+ * ie. the reference count reached 0.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static void
+DatagramFreeCB(void *clientData)
+{
+ DatagramEntry *entry = (DatagramEntry *)clientData;
+ ASSERT(entry);
+ VMCI_SignalEvent(&entry->destroyEvent);
+
+ /*
+ * The entry is freed in VMCIDatagram_DestroyHnd, who is waiting for the
+ * above signal.
+ */
+}
+
/*
*------------------------------------------------------------------------------
*
* DatagramReleaseCB --
*
- * Callback to release the datagram entry reference. It is called by the
+ * Callback to release the resource reference. It is called by the
* VMCI_WaitOnEvent function before it blocks.
*
* Result:
static int
DatagramReleaseCB(void *clientData)
{
- DatagramHashEntry *entry = (DatagramHashEntry *)clientData;
+ DatagramEntry *entry = (DatagramEntry *)clientData;
ASSERT(entry);
- DatagramHashReleaseEntry(entry);
-
+ VMCIResource_Release(&entry->resource);
return 0;
}
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * DatagramHashAddEntry --
- * Given a datagram handle entry, adds it to the hashtable of datagram
- * entries. Allocates a resource id iff the handle of the given entry
- * is an invalid one. 0 through VMCI_RESERVED_RESOURCE_ID_MAX are
- * reserved resource ids.
+ * DatagramCreateHnd --
*
- * Result:
- * VMCI_SUCCESS if added, error if not.
+ * Internal function to create a datagram entry given a handle.
*
- *-------------------------------------------------------------------------
+ * Results:
+ * VMCI_SUCCESS if created, negative errno value otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
*/
static int
-DatagramHashAddEntry(DatagramHashEntry *entry, // IN:
- VMCIId contextID) // IN:
+DatagramCreateHnd(VMCIId resourceID, // IN:
+ uint32 flags, // IN:
+ VMCIPrivilegeFlags privFlags, // IN:
+ VMCIDatagramRecvCB recvCB, // IN:
+ void *clientData, // IN:
+ VMCIHandle *outHandle) // OUT:
+
{
- int idx;
- VMCILockFlags flags;
- static VMCIId datagramRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
+ int result;
+ VMCIId contextID;
+ VMCIHandle handle;
+ DatagramEntry *entry;
- ASSERT(entry);
+ ASSERT(recvCB != NULL);
+ ASSERT(outHandle != NULL);
+ ASSERT(!(privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS));
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
+ if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
+ return VMCI_ERROR_INVALID_ARGS;
+ } else {
+ if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) {
+ contextID = VMCI_INVALID_ID;
+ } else {
+ contextID = VMCI_GetContextID();
+ if (contextID == VMCI_INVALID_ID) {
+ return VMCI_ERROR_NO_RESOURCES;
+ }
+ }
- /* Do not allow addition of a new handle if the device is being shutdown. */
- if (VMCI_DeviceShutdown()) {
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
- return VMCI_ERROR_DEVICE_NOT_FOUND;
- }
+ if (resourceID == VMCI_INVALID_ID) {
+ resourceID = VMCIResource_GetID(contextID);
+ if (resourceID == VMCI_INVALID_ID) {
+ return VMCI_ERROR_NO_HANDLE;
+ }
+ }
- if (!VMCI_HANDLE_INVALID(entry->handle) &&
- !DatagramHandleUniqueLockedAnyCid(entry->handle)) {
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
- return VMCI_ERROR_DUPLICATE_ENTRY;
- } else if (VMCI_HANDLE_INVALID(entry->handle)) {
- VMCIId oldRID = datagramRID;
- VMCIHandle handle;
- Bool foundRID = FALSE;
+ handle = VMCI_MAKE_HANDLE(contextID, resourceID);
+ }
- /*
- * Generate a unique datagram rid. Keep on trying until we wrap around
- * in the RID space.
- */
- ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX);
- do {
- handle = VMCI_MAKE_HANDLE(contextID, datagramRID);
- foundRID = DatagramHandleUniqueLockedAnyCid(handle);
- datagramRID++;
- if (UNLIKELY(!datagramRID)) {
- /*
- * Skip the reserved rids.
- */
- datagramRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
- }
- } while (!foundRID && datagramRID != oldRID);
+ entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED);
+ if (entry == NULL) {
+ VMCI_WARNING((LGPFX"Failed allocating memory for datagram entry.\n"));
+ return VMCI_ERROR_NO_MEM;
+ }
- if (LIKELY(foundRID)) {
- entry->handle = handle;
- } else {
- /*
- * We wrapped around --- no rids were free.
- */
- ASSERT(datagramRID == oldRID);
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
- return VMCI_ERROR_NO_HANDLE;
+ if (!VMCI_CanScheduleDelayedWork()) {
+ if (flags & VMCI_FLAG_DG_DELAYED_CB) {
+ VMCI_FreeKernelMem(entry, sizeof *entry);
+ return VMCI_ERROR_INVALID_ARGS;
}
+ entry->runDelayed = FALSE;
+ } else {
+ entry->runDelayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? TRUE : FALSE;
}
- ASSERT(!VMCI_HANDLE_INVALID(entry->handle));
- idx = VMCI_HASHRESOURCE(entry->handle, HASH_TABLE_SIZE);
+ entry->flags = flags;
+ entry->recvCB = recvCB;
+ entry->clientData = clientData;
+ VMCI_CreateEvent(&entry->destroyEvent);
+ entry->privFlags = privFlags;
- /* New entry is added to top/front of hash bucket. */
- entry->refCount++;
- entry->next = hashTable.entries[idx];
- hashTable.entries[idx] = entry;
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
+ /* Make datagram resource live. */
+ result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DATAGRAM,
+ handle, DatagramFreeCB, entry);
+ if (result != VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to add new resource %d:%d.\n",
+ handle.context, handle.resource));
+ VMCI_DestroyEvent(&entry->destroyEvent);
+ VMCI_FreeKernelMem(entry, sizeof *entry);
+ return result;
+ }
+ *outHandle = handle;
return VMCI_SUCCESS;
}
+/*------------------------------ Init functions ----------------------------*/
+
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagram_Init --
*
- * DatagramHashRemoveEntry --
+ * Initialize Datagram API, ie. register the API functions with their
+ * corresponding vectors.
*
* Result:
- * VMCI_SUCCESS if removed, VMCI_ERROR_NO_HANDLE if not found.
+ * None.
+ *
+ * Side effects:
+ * None.
*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-static int
-DatagramHashRemoveEntry(VMCIHandle handle)
+int
+VMCIDatagram_Init(void)
{
- int result = VMCI_ERROR_NOT_FOUND;
- VMCILockFlags flags;
- DatagramHashEntry *prev, *cur;
- int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE);
-
- prev = NULL;
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
- cur = hashTable.entries[idx];
- while (TRUE) {
- if (cur == NULL) {
- break;
- }
- if (VMCI_HANDLE_EQUAL(cur->handle, handle)) {
- /* Remove entry and break. */
- if (prev) {
- prev->next = cur->next;
- } else {
- hashTable.entries[idx] = cur->next;
- }
-
- cur->refCount--;
-
- /*
- * We know that DestroyHnd still has a reference so refCount must be
- * at least 1.
- */
- ASSERT(cur->refCount > 0);
- result = VMCI_SUCCESS;
- break;
- }
- prev = cur;
- cur = cur->next;
+ /* Create hash table for wellknown mappings. */
+ wellKnownTable = VMCIHashTable_Create(32);
+ if (wellKnownTable == NULL) {
+ return VMCI_ERROR_NO_RESOURCES;
}
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
- return result;
+ Atomic_Write(&delayedDGHostQueueSize, 0);
+ return VMCI_SUCCESS;
}
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * DatagramHashGetEntry --
+ * VMCIDatagram_Exit --
*
- * Gets the given datagram hashtable entry based on handle lookup.
+ * Cleanup Datagram API.
*
* Result:
- * Datagram hash entry if found. NULL otherwise.
+ * None.
*
- *-------------------------------------------------------------------------
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
*/
-static DatagramHashEntry *
-DatagramHashGetEntry(VMCIHandle handle) // IN
+void
+VMCIDatagram_Exit(void)
{
- VMCILockFlags flags;
- DatagramHashEntry *cur;
- int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE);
+ if (wellKnownTable != NULL) {
+ VMCIHashTable_Destroy(wellKnownTable);
+ wellKnownTable = NULL;
+ }
+}
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
- for (cur = hashTable.entries[idx]; cur != NULL; cur = cur->next) {
- if (VMCI_HANDLE_EQUAL(cur->handle, handle)) {
- cur->refCount++;
- break;
- }
+/*------------------------------ Public API functions ------------------------*/
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagram_CreateHnd --
+ *
+ * Creates a host context datagram endpoint and returns a handle to it.
+ *
+ * Results:
+ * VMCI_SUCCESS if created, negative errno value otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHnd)
+int
+VMCIDatagram_CreateHnd(VMCIId resourceID, // IN: Optional, generated
+ // if VMCI_INVALID_ID
+ uint32 flags, // IN:
+ VMCIDatagramRecvCB recvCB, // IN:
+ void *clientData, // IN:
+ VMCIHandle *outHandle) // OUT: newly created handle
+{
+ if (outHandle == NULL) {
+ return VMCI_ERROR_INVALID_ARGS;
}
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
+ if (recvCB == NULL) {
+ VMCI_DEBUG_LOG(4,
+ (LGPFX"Client callback needed when creating datagram.\n"));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
- return cur;
+ return DatagramCreateHnd(resourceID, flags, VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS,
+ recvCB, clientData, outHandle);
}
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagram_CreateHndPriv --
*
- * DatagramHashGetEntryAnyCid --
+ * Creates a host context datagram endpoint and returns a handle to it.
*
- * Gets the given datagram hashtable entry based on handle lookup.
- * Will match "any" or specific cid.
+ * Results:
+ * VMCI_SUCCESS if created, negative errno value otherwise.
*
- * Result:
- * Datagram hash entry if found. NULL otherwise.
+ * Side effects:
+ * None.
*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-static DatagramHashEntry *
-DatagramHashGetEntryAnyCid(VMCIHandle handle) // IN
+VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHndPriv)
+int
+VMCIDatagram_CreateHndPriv(VMCIId resourceID, // IN: Optional, generated
+ // if VMCI_INVALID_ID
+ uint32 flags, // IN:
+ VMCIPrivilegeFlags privFlags,// IN:
+ VMCIDatagramRecvCB recvCB, // IN:
+ void *clientData, // IN:
+ VMCIHandle *outHandle) // OUT: newly created handle
{
- VMCILockFlags flags;
- DatagramHashEntry *cur;
- int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE);
-
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
-
- for (cur = hashTable.entries[idx]; cur != NULL; cur = cur->next) {
- if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) ==
- VMCI_HANDLE_TO_RESOURCE_ID(handle)) {
- if (VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) == VMCI_INVALID_ID ||
- VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) ==
- VMCI_HANDLE_TO_CONTEXT_ID(handle)) {
- cur->refCount++;
- break;
- }
- }
+ if (outHandle == NULL) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (recvCB == NULL) {
+ VMCI_DEBUG_LOG(4,
+ (LGPFX"Client callback needed when creating datagram.\n"));
+ return VMCI_ERROR_INVALID_ARGS;
}
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
+ if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
- return cur;
+ return DatagramCreateHnd(resourceID, flags, privFlags, recvCB, clientData,
+ outHandle);
}
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * DatagramHashReleaseEntry --
+ * VMCIDatagram_DestroyHnd --
*
- * Drops a reference to the current hash entry. If this is the last
- * reference then the entry is freed.
+ * Destroys a handle.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDatagram_DestroyHnd)
+int
+VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN
+{
+ DatagramEntry *entry;
+ VMCIResource *resource = VMCIResource_Get(handle,
+ VMCI_RESOURCE_TYPE_DATAGRAM);
+ if (resource == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy handle 0x%x:0x%x.\n",
+ handle.context, handle.resource));
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ entry = RESOURCE_CONTAINER(resource, DatagramEntry, resource);
+
+ VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DATAGRAM);
+
+ /*
+ * We now wait on the destroyEvent and release the reference we got
+ * above.
+ */
+ VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry);
+
+ if ((entry->flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
+ VMCIDatagramRemoveWellKnownMap(handle.resource, VMCI_HOST_CONTEXT_ID);
+ }
+
+ /*
+ * We know that we are now the only reference to the above entry so
+ * can safely free it.
+ */
+ VMCI_DestroyEvent(&entry->destroyEvent);
+ VMCI_FreeKernelMem(entry, sizeof *entry);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagramGetPrivFlagsInt --
+ *
+ * Internal utilility function with the same purpose as
+ * VMCIDatagram_GetPrivFlags that also takes a contextID.
*
* Result:
+ * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
+ *
+ * Side effects:
* None.
*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-static void
-DatagramHashReleaseEntry(DatagramHashEntry *entry) // IN
+static int
+VMCIDatagramGetPrivFlagsInt(VMCIId contextID, // IN
+ VMCIHandle handle, // IN
+ VMCIPrivilegeFlags *privFlags) // OUT
{
- VMCILockFlags flags;
+ ASSERT(privFlags);
+ ASSERT(contextID != VMCI_INVALID_ID);
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
- entry->refCount--;
+ if (contextID == VMCI_HOST_CONTEXT_ID) {
+ DatagramEntry *srcEntry;
+ VMCIResource *resource;
- /* Check if this is last reference and signal the destroy event if so. */
- if (entry->refCount == 0) {
- VMCI_SignalEvent(&entry->destroyEvent);
+ resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DATAGRAM);
+ if (resource == NULL) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+ srcEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource);
+ *privFlags = srcEntry->privFlags;
+ VMCIResource_Release(resource);
+ } else if (contextID == VMCI_HYPERVISOR_CONTEXT_ID) {
+ *privFlags = VMCI_MAX_PRIVILEGE_FLAGS;
+ } else {
+ *privFlags = VMCIContext_GetPrivFlags(contextID);
}
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
+
+ return VMCI_SUCCESS;
}
/*
*------------------------------------------------------------------------------
*
- * DatagramHandleUniqueLockedAnyCid --
+ * VMCIDatagram_GetPrivFlags --
*
- * Checks whether the resource id (with any context id) is already in the
- * hash table.
- * Assumes that the caller has the hash table lock.
+ * Utilility function that retrieves the privilege flags
+ * associated with a given datagram handle. For hypervisor and
+ * guest endpoints, the privileges are determined by the context
+ * ID, but for host endpoints privileges are associated with the
+ * complete handle.
*
* Result:
- * TRUE if the handle is unique. FALSE otherwise.
+ * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
+ *
+ * Side effects:
+ * None.
*
*------------------------------------------------------------------------------
*/
-static Bool
-DatagramHandleUniqueLockedAnyCid(VMCIHandle handle) // IN
+int
+VMCIDatagram_GetPrivFlags(VMCIHandle handle, // IN
+ VMCIPrivilegeFlags *privFlags) // OUT
{
- Bool unique = TRUE;
- DatagramHashEntry *entry;
- int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE);
-
- entry = hashTable.entries[idx];
- while (entry) {
- if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) ==
- VMCI_HANDLE_TO_RESOURCE_ID(handle)) {
- unique = FALSE;
- break;
- }
- entry = entry->next;
+ if (privFlags == NULL || handle.context == VMCI_INVALID_ID) {
+ return VMCI_ERROR_INVALID_ARGS;
}
- return unique;
+ return VMCIDatagramGetPrivFlagsInt(handle.context, handle, privFlags);
}
/*
*-----------------------------------------------------------------------------
*
- * VMCIDatagram_CreateHnd --
+ * VMCIDatagramDelayedDispatchCB --
*
- * Creates a datagram endpoint and returns a handle to it.
+ * Calls the specified callback in a delayed context.
*
* Results:
- * Returns handle if success, negative errno value otherwise.
+ * None.
*
* Side effects:
- * Datagram endpoint is created both in guest and on host.
+ * None.
*
*-----------------------------------------------------------------------------
*/
-VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHnd)
-int
-VMCIDatagram_CreateHnd(VMCIId resourceID, // IN
- uint32 flags, // IN
- VMCIDatagramRecvCB recvCB, // IN
- void *clientData, // IN
- VMCIHandle *outHandle) // OUT
+static void
+VMCIDatagramDelayedDispatchCB(void *data) // IN
{
- int result;
- DatagramHashEntry *entry;
- VMCIHandle handle;
- VMCIId contextID;
+ Bool inDGHostQueue;
+ VMCIDelayedDatagramInfo *dgInfo = (VMCIDelayedDatagramInfo *)data;
- if (!recvCB || !outHandle) {
- return VMCI_ERROR_INVALID_ARGS;
+ ASSERT(data);
+
+ dgInfo->entry->recvCB(dgInfo->entry->clientData, &dgInfo->msg);
+
+ VMCIResource_Release(&dgInfo->entry->resource);
+
+ inDGHostQueue = dgInfo->inDGHostQueue;
+ VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dgInfo->msg.payloadSize);
+
+ if (inDGHostQueue) {
+ Atomic_Dec(&delayedDGHostQueueSize);
}
+}
- if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) {
- contextID = VMCI_INVALID_ID;
- } else {
- contextID = VMCI_GetContextID();
- /* Validate contextID. */
- if (contextID == VMCI_INVALID_ID) {
- return VMCI_ERROR_NO_RESOURCES;
- }
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagramDispatchFromHostToSelfOrGuest --
+ *
+ * Dispatch datagram to host or other vm context. This function cannot
+ * dispatch to hypervisor context handlers. This should have been handled
+ * before we get here by VMCIDatagramDispatch.
+ *
+ * Result:
+ * Number of bytes sent on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDatagramDispatchFromHostToSelfOrGuest(VMCIId contextID, // IN:
+ VMCIDatagram *dg) // IN:
+{
+ int retval;
+ size_t dgSize;
+ VMCIId dstContext;
+ VMCIPrivilegeFlags srcPrivFlags;
+ char srcDomain[VMCI_DOMAIN_NAME_MAXLEN]; /* Not used on hosted. */
+ char dstDomain[VMCI_DOMAIN_NAME_MAXLEN]; /* Not used on hosted. */
+
+ ASSERT(dg);
+
+ dgSize = VMCI_DG_SIZE(dg);
+
+ if (contextID == VMCI_HOST_CONTEXT_ID &&
+ dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID) {
+ return VMCI_ERROR_DST_UNREACHABLE;
}
- if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
- VMCIDatagramWellKnownMapMsg wkMsg;
- if (resourceID == VMCI_INVALID_ID) {
- return VMCI_ERROR_INVALID_ARGS;
- }
- wkMsg.hdr.dst.context = VMCI_HYPERVISOR_CONTEXT_ID;
- wkMsg.hdr.dst.resource = VMCI_DATAGRAM_REQUEST_MAP;
- wkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- wkMsg.hdr.payloadSize = sizeof wkMsg - VMCI_DG_HEADERSIZE;
- wkMsg.wellKnownID = resourceID;
- result = VMCI_SendDatagram((VMCIDatagram *)&wkMsg);
- if (result < VMCI_SUCCESS) {
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to reserve wellknown id %d, "
- "error %d.\n", resourceID, result));
- return result;
- }
+ ASSERT(dg->dst.context != VMCI_HYPERVISOR_CONTEXT_ID);
- handle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, resourceID);
- } else {
- if (resourceID == VMCI_INVALID_ID) {
- handle = VMCI_INVALID_HANDLE;
+ VMCI_DEBUG_LOG(10, (LGPFX"Sending from handle 0x%x:0x%x to handle 0x%x:0x%x, "
+ "datagram size %u.\n",
+ dg->src.context, dg->src.resource,
+ dg->dst.context, dg->dst.resource, (uint32)dgSize));
+
+ /*
+ * Check that source handle matches sending context.
+ */
+ if (dg->src.context != contextID) {
+ if (dg->src.context == VMCI_WELL_KNOWN_CONTEXT_ID) {
+ /* Determine mapping. */
+ DatagramWKMapping *wkMap = DatagramGetWellKnownMap(dg->src.resource);
+ if (wkMap == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Sending from invalid well-known resource "
+ "id 0x%x:0x%x.\n",
+ dg->src.context, dg->src.resource));
+ return VMCI_ERROR_INVALID_RESOURCE;
+ }
+ if (wkMap->contextID != contextID) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Sender context 0x%x is not owner of "
+ "well-known src datagram entry with handle "
+ "0x%x:0x%x.\n",
+ contextID, dg->src.context, dg->src.resource));
+ DatagramReleaseWellKnownMap(wkMap);
+ return VMCI_ERROR_NO_ACCESS;
+ }
+ DatagramReleaseWellKnownMap(wkMap);
} else {
- handle = VMCI_MAKE_HANDLE(contextID, resourceID);
+ VMCI_DEBUG_LOG(4, (LGPFX"Sender context 0x%x is not owner of src "
+ "datagram entry with handle 0x%x:0x%x.\n",
+ contextID, dg->src.context, dg->src.resource));
+ return VMCI_ERROR_NO_ACCESS;
}
}
- /* Update local datastructure. */
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED);
- if (entry == NULL) {
- return VMCI_ERROR_NO_MEM;
+ if (dg->dst.context == VMCI_WELL_KNOWN_CONTEXT_ID) {
+ /* Determine mapping. */
+ DatagramWKMapping *wkMap = DatagramGetWellKnownMap(dg->dst.resource);
+ if (wkMap == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid wellknown destination "
+ "0x%x:0x%x.\n",
+ dg->dst.context, dg->dst.resource));
+ return VMCI_ERROR_DST_UNREACHABLE;
+ }
+ dstContext = wkMap->contextID;
+ DatagramReleaseWellKnownMap(wkMap);
+ } else {
+ dstContext = dg->dst.context;
}
- if (!VMCI_CanScheduleDelayedWork()) {
- if (flags & VMCI_FLAG_DG_DELAYED_CB) {
- VMCI_FreeKernelMem(entry, sizeof *entry);
- return VMCI_ERROR_INVALID_ARGS;
+ /*
+ * Get hold of privileges of sending endpoint.
+ */
+
+ retval = VMCIDatagramGetPrivFlagsInt(contextID, dg->src, &srcPrivFlags);
+ if (retval != VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Couldn't get privileges for handle 0x%x:0x%x.\n",
+ dg->src.context, dg->src.resource));
+ return retval;
+ }
+
+#ifdef VMKERNEL
+ /*
+ * In the vmkernel, all communicating contexts except the
+ * hypervisor context must belong to the same domain. If the
+ * hypervisor is the source, the domain doesn't matter.
+ */
+
+ if (contextID != VMCI_HYPERVISOR_CONTEXT_ID) {
+ retval = VMCIContext_GetDomainName(contextID, srcDomain,
+ sizeof srcDomain);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to get domain name for context %u.\n",
+ contextID));
+ return retval;
+ }
+ }
+#endif
+
+ /* Determine if we should route to host or guest destination. */
+ if (dstContext == VMCI_HOST_CONTEXT_ID) {
+ /* Route to host datagram entry. */
+ DatagramEntry *dstEntry;
+ VMCIResource *resource;
+
+ if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
+ dg->dst.resource == VMCI_EVENT_HANDLER) {
+ return VMCIEvent_Dispatch(dg);
+ }
+
+ resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM);
+ if (resource == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid destination handle "
+ "0x%x:0x%x.\n", dg->dst.context, dg->dst.resource));
+ return VMCI_ERROR_INVALID_RESOURCE;
+ }
+ dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource);
+#ifdef VMKERNEL
+ retval = VMCIContext_GetDomainName(VMCI_HOST_CONTEXT_ID, dstDomain,
+ sizeof dstDomain);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to get domain name for context %u.\n",
+ VMCI_HOST_CONTEXT_ID));
+ VMCIResource_Release(resource);
+ return retval;
+ }
+#endif
+ if (VMCIDenyInteraction(srcPrivFlags, dstEntry->privFlags, srcDomain,
+ dstDomain)) {
+ VMCIResource_Release(resource);
+ return VMCI_ERROR_NO_ACCESS;
+ }
+ ASSERT(dstEntry->recvCB);
+
+ /*
+ * If a VMCI datagram destined for the host is also sent by the
+ * host, we always run it delayed. This ensures that no locks
+ * are held when the datagram callback runs.
+ */
+
+ if (dstEntry->runDelayed ||
+ (dg->src.context == VMCI_HOST_CONTEXT_ID &&
+ VMCI_CanScheduleDelayedWork())) {
+ VMCIDelayedDatagramInfo *dgInfo;
+
+ if (Atomic_FetchAndAdd(&delayedDGHostQueueSize, 1) ==
+ VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) {
+ Atomic_Dec(&delayedDGHostQueueSize);
+ VMCIResource_Release(resource);
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize,
+ (VMCI_MEMORY_ATOMIC |
+ VMCI_MEMORY_NONPAGED));
+ if (NULL == dgInfo) {
+ Atomic_Dec(&delayedDGHostQueueSize);
+ VMCIResource_Release(resource);
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ dgInfo->inDGHostQueue = TRUE;
+ dgInfo->entry = dstEntry;
+ memcpy(&dgInfo->msg, dg, dgSize);
+
+ retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram, "
+ "result %d.\n", retval));
+ VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize);
+ VMCIResource_Release(resource);
+ Atomic_Dec(&delayedDGHostQueueSize);
+ return retval;
+ }
+ } else {
+ retval = dstEntry->recvCB(dstEntry->clientData, dg);
+ VMCIResource_Release(resource);
+ if (retval < VMCI_SUCCESS) {
+ return retval;
+ }
}
- entry->runDelayed = FALSE;
} else {
- entry->runDelayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? TRUE : FALSE;
+ /* Route to destination VM context. */
+ VMCIDatagram *newDG;
+
+#ifdef VMKERNEL
+ retval = VMCIContext_GetDomainName(dstContext, dstDomain,
+ sizeof dstDomain);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to get domain name for context %u.\n",
+ dstContext));
+ return retval;
+ }
+#endif
+ if (contextID != dstContext &&
+ VMCIDenyInteraction(srcPrivFlags, VMCIContext_GetPrivFlags(dstContext),
+ srcDomain, dstDomain)) {
+ return VMCI_ERROR_NO_ACCESS;
+ }
+
+ /* We make a copy to enqueue. */
+ newDG = VMCI_AllocKernelMem(dgSize, VMCI_MEMORY_NORMAL);
+ if (newDG == NULL) {
+ return VMCI_ERROR_NO_MEM;
+ }
+ memcpy(newDG, dg, dgSize);
+ retval = VMCIContext_EnqueueDatagram(dstContext, newDG);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_FreeKernelMem(newDG, dgSize);
+ return retval;
+ }
}
+ /* The datagram is freed when the context reads it. */
+ VMCI_DEBUG_LOG(10, (LGPFX"Sent datagram of size %u.\n", (uint32)dgSize));
- entry->handle = handle;
- entry->flags = flags;
- entry->recvCB = recvCB;
- entry->clientData = clientData;
- entry->refCount = 0;
- VMCI_CreateEvent(&entry->destroyEvent);
+ /*
+ * We currently truncate the size to signed 32 bits. This doesn't
+ * matter for this handler as it only support 4Kb messages.
+ */
+ return (int)dgSize;
+}
- result = DatagramHashAddEntry(entry, contextID);
- if (result != VMCI_SUCCESS) {
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to add new entry, err 0x%x.\n", result));
- VMCI_DestroyEvent(&entry->destroyEvent);
- VMCI_FreeKernelMem(entry, sizeof *entry);
- return result;
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDatagramDispatchFromGuestToHost --
+ *
+ * Dispatch datagram down through the VMX, and potentially to the host.
+ *
+ * Result:
+ * Number of bytes sent on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDatagramDispatchFromGuestToHost(VMCIDatagram *dg)
+{
+#if defined(VMKERNEL)
+ VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
+#else // VMKERNEL
+ int retval;
+ VMCIResource *resource;
+ extern int VMCI_SendDatagram(VMCIDatagram *);
+
+ resource = VMCIResource_Get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM);
+ if (NULL == resource) {
+ return VMCI_ERROR_NO_HANDLE;
}
- ASSERT(!VMCI_HANDLE_INVALID(entry->handle));
- *outHandle = entry->handle;
- return VMCI_SUCCESS;
+ retval = VMCI_SendDatagram(dg);
+ VMCIResource_Release(resource);
+ return retval;
+#endif // VMKERNEL
}
+
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * VMCIDatagram_CreateHndPriv --
+ * VMCIDatagram_Dispatch --
*
- * API provided for compatibility with the host vmci API. This function
- * doesn't ever succeed since you can't ask for elevated privileges from
- * the guest. Use VMCIDatagram_CreateHnd instead.
+ * Dispatch datagram. This will determine the routing for the datagram
+ * and dispatch it accordingly.
*
- * Results:
- * Returns a negative error.
+ * Result:
+ * Number of bytes sent on success, appropriate error code otherwise.
*
- * Side effects:
- * None.
+ * Side effects:
+ * None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHndPriv)
int
-VMCIDatagram_CreateHndPriv(VMCIId resourceID, // IN:
- uint32 flags, // IN:
- VMCIPrivilegeFlags privFlags, // IN:
- VMCIDatagramRecvCB recvCB, // IN:
- void *clientData, // IN:
- VMCIHandle *outHandle) // OUT:
+VMCIDatagram_Dispatch(VMCIId contextID,
+ VMCIDatagram *dg,
+ Bool fromGuest)
{
- return VMCI_ERROR_NO_ACCESS;
+ int retval;
+ VMCIRoute route;
+
+ ASSERT(dg);
+ ASSERT_ON_COMPILE(sizeof(VMCIDatagram) == 24);
+
+ if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Payload size %"FMT64"u too big to send.\n",
+ dg->payloadSize));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ retval = VMCI_Route(&dg->src, &dg->dst, fromGuest, &route);
+ if (retval < VMCI_SUCCESS) {
+ return retval;
+ }
+
+ if (VMCI_ROUTE_HOST_TO_SELF == route ||
+ VMCI_ROUTE_HOST_TO_GUEST == route) {
+ if (VMCI_INVALID_ID == contextID) {
+ contextID = VMCI_HOST_CONTEXT_ID;
+ }
+ return VMCIDatagramDispatchFromHostToSelfOrGuest(contextID, dg);
+ }
+
+ if (VMCI_ROUTE_GUEST_TO_HOST == route ||
+ VMCI_ROUTE_GUEST_TO_HYPERVISOR == route) {
+ return VMCIDatagramDispatchFromGuestToHost(dg);
+ }
+
+ VMCI_WARNING((LGPFX"Unknown route for datagram.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
}
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * VMCIDatagram_DestroyHnd --
+ * VMCIDatagram_InvokeGuestHandler --
*
- * Destroys a handle.
+ * Invoke the handler for the given datagram. This is intended to be
+ * called only when acting as a guest and receiving a datagram from the
+ * virtual device.
*
- * Results:
- * VMCI_SUCCESS or error code.
+ * Result:
+ * VMCI_SUCCESS on success, other error values on failure.
*
- * Side effects:
- * Host and guest state is cleaned up.
+ * Side effects:
+ * None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-VMCI_EXPORT_SYMBOL(VMCIDatagram_DestroyHnd)
int
-VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN
+VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg) // IN
{
- DatagramHashEntry *entry = DatagramHashGetEntry(handle);
- if (entry == NULL) {
- return VMCI_ERROR_NOT_FOUND;
+#if defined(VMKERNEL)
+ VMCI_WARNING((LGPFX"Cannot dispatch within guest in VMKERNEL.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
+#else // VMKERNEL
+ int retval;
+ VMCIResource *resource;
+ DatagramEntry *dstEntry;
+
+ ASSERT(dg);
+
+ resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM);
+ if (NULL == resource) {
+ VMCI_DEBUG_LOG(4, (LGPFX"destination handle 0x%x:0x%x doesn't exist.\n",
+ dg->dst.context, dg->dst.resource));
+ return VMCI_ERROR_NO_HANDLE;
}
- DatagramHashRemoveEntry(entry->handle);
-
- /*
- * We wait for destroyEvent to be signalled. The resource is released
- * as part of the wait.
- */
- VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry);
+ dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource);
+ if (dstEntry->runDelayed) {
+ VMCIDelayedDatagramInfo *dgInfo;
+ dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize,
+ (VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED));
+ if (NULL == dgInfo) {
+ VMCIResource_Release(resource);
+ retval = VMCI_ERROR_NO_MEM;
+ goto exit;
+ }
- if ((entry->flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
- int result;
- VMCIDatagramWellKnownMapMsg wkMsg;
-
- wkMsg.hdr.dst.context = VMCI_HYPERVISOR_CONTEXT_ID;
- wkMsg.hdr.dst.resource = VMCI_DATAGRAM_REMOVE_MAP;
- wkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- wkMsg.hdr.payloadSize = sizeof wkMsg - VMCI_DG_HEADERSIZE;
- wkMsg.wellKnownID = entry->handle.resource;
- result = VMCI_SendDatagram((VMCIDatagram *)&wkMsg);
- if (result < VMCI_SUCCESS) {
- VMCI_WARNING((LGPFX"Failed to remove well-known mapping for "
- "resource %d.\n", entry->handle.resource));
+ dgInfo->inDGHostQueue = FALSE;
+ dgInfo->entry = dstEntry;
+ memcpy(&dgInfo->msg, dg, VMCI_DG_SIZE(dg));
+
+ retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo);
+ if (retval < VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram, "
+ "result %d.\n", retval));
+ VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize);
+ VMCIResource_Release(resource);
+ dgInfo = NULL;
+ goto exit;
}
+ } else {
+ dstEntry->recvCB(dstEntry->clientData, dg);
+ VMCIResource_Release(resource);
+ retval = VMCI_SUCCESS;
}
- /* We know we are now holding the last reference so we can free the entry. */
- VMCI_DestroyEvent(&entry->destroyEvent);
- VMCI_FreeKernelMem(entry, sizeof *entry);
-
- return VMCI_SUCCESS;
+exit:
+ return retval;
+#endif // VMKERNEL
}
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIDatagram_Send --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
VMCI_EXPORT_SYMBOL(VMCIDatagram_Send)
int
VMCIDatagram_Send(VMCIDatagram *msg) // IN
{
- uint32 retval;
- DatagramHashEntry *entry;
- VMCIId contextId;
-
if (msg == NULL) {
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid datagram.\n"));
return VMCI_ERROR_INVALID_ARGS;
}
- if (VMCI_DG_SIZE(msg) > VMCI_MAX_DG_SIZE) {
- VMCI_DEBUG_LOG(4, (LGPFX"Payload size %"FMT64"u too big to send.\n",
- msg->payloadSize));
- return VMCI_ERROR_INVALID_ARGS;
- }
+ return VMCIDatagram_Dispatch(VMCI_INVALID_ID, msg, FALSE);
+}
- /* Check srcHandle exists otherwise fail. */
- entry = DatagramHashGetEntry(msg->src);
- if (entry == NULL) {
- VMCI_DEBUG_LOG(4, (LGPFX"Couldn't find handle 0x%x:0x%x.\n",
- msg->src.context, msg->src.resource));
- return VMCI_ERROR_INVALID_ARGS;
- }
- contextId = VMCI_HANDLE_TO_CONTEXT_ID(msg->src);
+/*
+ *------------------------------------------------------------------------------
+ *
+ * DatagramGetWellKnownMap --
+ *
+ * Gets a mapping between handle and wellknown resource.
+ *
+ * Results:
+ * DatagramWKMapping * if found, NULL if not.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
- if (contextId == VMCI_INVALID_ID) {
- msg->src = VMCI_MAKE_HANDLE(VMCI_GetContextID(),
- VMCI_HANDLE_TO_RESOURCE_ID(msg->src));
+static DatagramWKMapping *
+DatagramGetWellKnownMap(VMCIId wellKnownID) // IN:
+{
+ VMCIHashEntry *entry;
+ DatagramWKMapping *wkMap = NULL;
+ VMCIHandle wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID,
+ wellKnownID);
+ entry = VMCIHashTable_GetEntry(wellKnownTable, wkHandle);
+ if (entry != NULL) {
+ wkMap = RESOURCE_CONTAINER(entry, DatagramWKMapping, entry);
}
-
- retval = VMCI_SendDatagram(msg);
- DatagramHashReleaseEntry(entry);
-
- return retval;
+ return wkMap;
}
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * VMCIDatagramDelayedDispatchCB --
+ * DatagramReleaseWellKnownMap --
*
- * Calls the specified callback in a delayed context.
+ * Releases a wellknown mapping.
*
* Results:
* None.
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
static void
-VMCIDatagramDelayedDispatchCB(void *data) // IN
+DatagramReleaseWellKnownMap(DatagramWKMapping *wkMap) // IN:
{
- VMCIDelayedDatagramInfo *dgInfo = (VMCIDelayedDatagramInfo *)data;
-
- ASSERT(data);
-
- dgInfo->entry->recvCB(dgInfo->entry->clientData, &dgInfo->msg);
-
- DatagramHashReleaseEntry(dgInfo->entry);
- VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dgInfo->msg.payloadSize);
+ if (VMCIHashTable_ReleaseEntry(wellKnownTable, &wkMap->entry) ==
+ VMCI_SUCCESS_ENTRY_DEAD) {
+ VMCI_FreeKernelMem(wkMap, sizeof *wkMap);
+ }
}
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * VMCIDatagram_Dispatch --
+ * VMCIDatagramRequestWellKnownMap --
*
- * Forwards the datagram to the corresponding entry's callback. This will
- * defer the datagram if requested by the client, so that the callback
- * is invoked in a delayed context.
+ * Creates a mapping between handle and wellknown resource. If resource
+ * is already used we fail the request.
*
* Results:
- * VMCI_SUCCESS on success, error code if not.
+ * VMCI_SUCCESS if created, negative errno value otherwise.
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
int
-VMCIDatagram_Dispatch(VMCIId contextID, // IN: unused
- VMCIDatagram *msg) // IN
+VMCIDatagramRequestWellKnownMap(VMCIId wellKnownID, // IN:
+ VMCIId contextID, // IN:
+ VMCIPrivilegeFlags privFlags) // IN:
{
- int32 err = VMCI_SUCCESS;
- DatagramHashEntry *entry;
-
- ASSERT(msg);
+ int result;
+ DatagramWKMapping *wkMap;
+ VMCIHandle wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID,
+ wellKnownID);
- entry = DatagramHashGetEntryAnyCid(msg->dst);
- if (entry == NULL) {
- VMCI_DEBUG_LOG(4, (LGPFX"destination handle 0x%x:0x%x doesn't exist.\n",
- msg->dst.context, msg->dst.resource));
- return VMCI_ERROR_NO_HANDLE;
+ if (privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED ||
+ !VMCIWellKnownID_AllowMap(wellKnownID, privFlags)) {
+ return VMCI_ERROR_NO_ACCESS;
}
- if (!entry->recvCB) {
- VMCI_WARNING((LGPFX"no handle callback for handle 0x%x:0x%x payload of "
- "size %"FMT64"d.\n", msg->dst.context, msg->dst.resource,
- msg->payloadSize));
- goto out;
+ wkMap = VMCI_AllocKernelMem(sizeof *wkMap, VMCI_MEMORY_NONPAGED);
+ if (wkMap == NULL) {
+ return VMCI_ERROR_NO_MEM;
}
- if (entry->runDelayed) {
- VMCIDelayedDatagramInfo *dgInfo;
-
- dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)msg->payloadSize,
- VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED);
- if (NULL == dgInfo) {
- err = VMCI_ERROR_NO_MEM;
- goto out;
- }
-
- dgInfo->entry = entry;
- memcpy(&dgInfo->msg, msg, VMCI_DG_SIZE(msg));
-
- err = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo);
- if (VMCI_SUCCESS == err) {
- return err;
- }
+ VMCIHashTable_InitEntry(&wkMap->entry, wkHandle);
+ wkMap->contextID = contextID;
- VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)msg->payloadSize);
- } else {
- entry->recvCB(entry->clientData, msg);
+ /* Fails if wkHandle (wellKnownID) already exists. */
+ result = VMCIHashTable_AddEntry(wellKnownTable, &wkMap->entry);
+ if (result != VMCI_SUCCESS) {
+ VMCI_FreeKernelMem(wkMap, sizeof *wkMap);
+ return result;
}
-
-out:
- DatagramHashReleaseEntry(entry);
- return err;
+ result = VMCIContext_AddWellKnown(contextID, wellKnownID);
+ if (UNLIKELY(result < VMCI_SUCCESS)) {
+ VMCIHashTable_RemoveEntry(wellKnownTable, &wkMap->entry);
+ VMCI_FreeKernelMem(wkMap, sizeof *wkMap);
+ }
+ return result;
}
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
- * VMCIDatagram_Init --
+ * VMCIDatagramRemoveWellKnownMap --
*
- * Register guest call handlers.
+ * Removes a mapping between handle and wellknown resource. Checks if
+ * mapping belongs to calling context.
*
* Results:
- * None
+ * VMCI_SUCCESS if removed, negative errno value otherwise.
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
-void
-VMCIDatagram_Init(void)
+int
+VMCIDatagramRemoveWellKnownMap(VMCIId wellKnownID, // IN:
+ VMCIId contextID) // IN:
{
- int i;
+ int result = VMCI_ERROR_NO_ACCESS;
+ DatagramWKMapping *wkMap = DatagramGetWellKnownMap(wellKnownID);
+ if (wkMap == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to remove well-known mapping between "
+ "resource %d and context %d.\n", wellKnownID,
+ contextID));
+ return VMCI_ERROR_NOT_FOUND;
+ }
- VMCI_InitLock(&hashTable.lock,
- "VMCIDatagramHashtable",
- VMCI_LOCK_RANK_MIDDLE_BH);
- for (i = 0; i < HASH_TABLE_SIZE; i++) {
- hashTable.entries[i] = NULL;
+ if (contextID == wkMap->contextID) {
+ VMCIHashTable_RemoveEntry(wellKnownTable, &wkMap->entry);
+ VMCIContext_RemoveWellKnown(contextID, wellKnownID);
+ result = VMCI_SUCCESS;
}
+ DatagramReleaseWellKnownMap(wkMap);
+ return result;
}
/*
*-----------------------------------------------------------------------------
*
- * VMCIDatagram_CheckHostCapabilities --
+ * VMCIDatagram_Sync --
*
- * Verify that the host supports the resources we need.
- * None are required for datagrams since they are implicitly supported.
+ * Use this as a synchronization point when setting globals, for example,
+ * during device shutdown.
*
* Results:
- * TRUE.
+ * None.
*
* Side effects:
* None.
*-----------------------------------------------------------------------------
*/
-Bool
-VMCIDatagram_CheckHostCapabilities(void)
+void
+VMCIDatagram_Sync(void)
{
- return TRUE;
+ VMCIResource_Sync();
}
/*
*-----------------------------------------------------------------------------
*
- * VMCIDatagram_Sync --
+ * VMCIDatagram_CheckHostCapabilities --
*
- * Use this as a synchronization point when setting globals, for example,
- * during device shutdown.
+ * Verify that the host supports the resources we need.
+ * None are required for datagrams since they are implicitly supported.
*
* Results:
- * None.
+ * TRUE.
*
* Side effects:
* None.
*-----------------------------------------------------------------------------
*/
-void
-VMCIDatagram_Sync(void)
+Bool
+VMCIDatagram_CheckHostCapabilities(void)
{
- VMCILockFlags flags;
- VMCI_GrabLock_BH(&hashTable.lock, &flags);
- VMCI_ReleaseLock_BH(&hashTable.lock, flags);
+ return TRUE;
}
/*
* vmciDatagram.h --
*
- * Simple Datagram API for the Linux guest driver.
+ * Internal functions in the VMCI Simple Datagram API.
*/
-#ifndef __VMCI_DATAGRAM_H__
-#define __VMCI_DATAGRAM_H__
+#ifndef _VMCI_DATAGRAM_H_
+#define _VMCI_DATAGRAM_H_
#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"
-#include "vmci_defs.h"
+#include "vmciContext.h"
#include "vmci_call_defs.h"
-#include "vmci_kernel_if.h"
-#include "vmci_infrastructure.h"
+#ifndef VMX86_SERVER
#include "vmci_iocontrols.h"
+#endif // !VMX86_SERVER
-void VMCIDatagram_Init(void);
+#define VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE 256
+
+/* Init functions. */
+int VMCIDatagram_Init(void);
+void VMCIDatagram_Exit(void);
+
+
+/* Datagram API for non-public use. */
+int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *dg, Bool fromGuest);
+int VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg);
+int VMCIDatagram_GetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags);
+
+/* Misc. */
void VMCIDatagram_Sync(void);
Bool VMCIDatagram_CheckHostCapabilities(void);
-int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *msg);
-#endif //__VMCI_DATAGRAM_H__
+/* Non public datagram API. */
+int VMCIDatagramRequestWellKnownMap(VMCIId wellKnownID, VMCIId contextID,
+ VMCIPrivilegeFlags privFlags);
+int VMCIDatagramRemoveWellKnownMap(VMCIId wellKnownID, VMCIId contextID);
+
+#endif // _VMCI_DATAGRAM_H_
+
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2010 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciDoorbell.h --
+ *
+ * Internal functions in the VMCI Doorbell API.
+ */
+
+#ifndef VMCI_DOORBELL_H
+#define VMCI_DOORBELL_H
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_kernel_if.h"
+#include "vmci_defs.h"
+
+int VMCIDoorbellHostContextNotify(VMCIId srcCID, VMCIHandle handle);
+int VMCIDoorbellGetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags);
+void VMCIDoorbell_Sync(void);
+
+#endif // VMCI_DOORBELL_H
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciDriver.h --
+ *
+ * VMCI host driver interface.
+ */
+
+#ifndef _VMCI_DRIVER_H_
+#define _VMCI_DRIVER_H_
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciContext.h"
+
+/*
+ * A few macros to encapsulate logging in common code. The macros
+ * result in LOG/LOGThrottled on vmkernel and Log on hosted.
+ */
+
+#ifdef _WIN32
+# include "vmciLog.h"
+#else // _WIN32
+#ifdef VMKERNEL
+# define LOGLEVEL_MODULE_LEN 0
+# define LOGLEVEL_MODULE VMCIVMK
+# include "log.h"
+# ifdef VMX86_LOG
+# define _VMCILOG(_args...) LOG(i, _args)
+# define VMCI_DEBUG_LOG(_level, _args) \
+ do { \
+ int i = _level; \
+ _VMCILOG _args ; \
+ } while(FALSE)
+# else
+# define VMCI_DEBUG_LOG(_level, _args)
+# endif
+#else
+# define VMCI_DEBUG_LEVEL 4
+# define VMCI_DEBUG_LOG(_level, _args) \
+ do { \
+ if (_level < VMCI_DEBUG_LEVEL) { \
+ Log _args ; \
+ } \
+ } while(FALSE)
+#endif
+#define VMCI_LOG(_args) Log _args
+#define VMCI_WARNING(_args) Warning _args
+#endif // _WIN32
+
+int VMCI_Init(void);
+void VMCI_Cleanup(void);
+VMCIId VMCI_GetContextID(void);
+
+#endif // _VMCI_DRIVER_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciHashtable.c --
+ *
+ * Implementation of the VMCI Hashtable.
+ * TODO: Look into what is takes to use lib/misc/hashTable.c instead of
+ * our own implementation.
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+# include "driver-config.h"
+# include "compat_kernel.h"
+# include "compat_module.h"
+#endif // __linux__
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciHashtable.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
+
+#define LGPFX "VMCIHashTable: "
+
+#define VMCI_HASHTABLE_HASH(_h, _sz) \
+ VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(_h), (_sz))
+
+
+static int HashTableUnlinkEntry(VMCIHashTable *table, VMCIHashEntry *entry);
+static Bool VMCIHashTableEntryExistsLocked(VMCIHashTable *table,
+ VMCIHandle handle);
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_Create --
+ * XXX Factor out the hashtable code to be shared amongst host and guest.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCIHashTable *
+VMCIHashTable_Create(int size)
+{
+ VMCIHashTable *table = VMCI_AllocKernelMem(sizeof *table,
+ VMCI_MEMORY_NONPAGED);
+ if (table == NULL) {
+ return NULL;
+ }
+
+ table->entries = VMCI_AllocKernelMem(sizeof *table->entries * size,
+ VMCI_MEMORY_NONPAGED);
+ if (table->entries == NULL) {
+ VMCI_FreeKernelMem(table, sizeof *table);
+ return NULL;
+ }
+ memset(table->entries, 0, sizeof *table->entries * size);
+ table->size = size;
+ VMCI_InitLock(&table->lock,
+ "VMCIHashTableLock",
+ VMCI_LOCK_RANK_HIGH);
+
+ return table;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_Destroy --
+ * This function should be called at module exit time.
+ * We rely on the module ref count to insure that no one is accessing any
+ * hash table entries at this point in time. Hence we should be able to just
+ * remove all entries from the hash table.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIHashTable_Destroy(VMCIHashTable *table)
+{
+ VMCILockFlags flags;
+#if 0
+ DEBUG_ONLY(int i;)
+ DEBUG_ONLY(int leakingEntries = 0;)
+#endif
+
+ ASSERT(table);
+
+ VMCI_GrabLock(&table->lock, &flags);
+#if 0
+#ifdef VMX86_DEBUG
+ for (i = 0; i < table->size; i++) {
+ VMCIHashEntry *head = table->entries[i];
+ while (head) {
+ leakingEntries++;
+ head = head->next;
+ }
+ }
+ if (leakingEntries) {
+ VMCI_WARNING((LGPFX"Leaking %d hash table entries for table %p.\n",
+ leakingEntries, table));
+ }
+#endif // VMX86_DEBUG
+#endif
+ VMCI_FreeKernelMem(table->entries, sizeof *table->entries * table->size);
+ table->entries = NULL;
+ VMCI_ReleaseLock(&table->lock, flags);
+ VMCI_CleanupLock(&table->lock);
+ VMCI_FreeKernelMem(table, sizeof *table);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_InitEntry --
+ * Initializes a hash entry;
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+void
+VMCIHashTable_InitEntry(VMCIHashEntry *entry, // IN
+ VMCIHandle handle) // IN
+{
+ ASSERT(entry);
+ entry->handle = handle;
+ entry->refCount = 0;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_AddEntry --
+ * XXX Factor out the hashtable code to be shared amongst host and guest.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIHashTable_AddEntry(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN
+{
+ int idx;
+ VMCILockFlags flags;
+
+ ASSERT(entry);
+ ASSERT(table);
+
+ VMCI_GrabLock(&table->lock, &flags);
+
+ /* Do not allow addition of a new entry if the device is being shutdown. */
+ if (VMCI_DeviceShutdown()) {
+ VMCI_ReleaseLock(&table->lock, flags);
+ return VMCI_ERROR_DEVICE_NOT_FOUND;
+ }
+
+ if (VMCIHashTableEntryExistsLocked(table, entry->handle)) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Entry's handle 0x%x:0x%x already exists.\n",
+ entry->handle.context, entry->handle.resource));
+ VMCI_ReleaseLock(&table->lock, flags);
+ return VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+
+ idx = VMCI_HASHTABLE_HASH(entry->handle, table->size);
+ ASSERT(idx < table->size);
+
+ /* New entry is added to top/front of hash bucket. */
+ entry->refCount++;
+ entry->next = table->entries[idx];
+ table->entries[idx] = entry;
+ VMCI_ReleaseLock(&table->lock, flags);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_RemoveEntry --
+ * XXX Factor out the hashtable code to shared amongst API and perhaps
+ * host and guest.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIHashTable_RemoveEntry(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN
+{
+ int result;
+ VMCILockFlags flags;
+
+ ASSERT(table);
+ ASSERT(entry);
+
+ VMCI_GrabLock(&table->lock, &flags);
+
+ /* First unlink the entry. */
+ result = HashTableUnlinkEntry(table, entry);
+ if (result != VMCI_SUCCESS) {
+ /* We failed to find the entry. */
+ goto done;
+ }
+
+ /* Decrement refcount and check if this is last reference. */
+ entry->refCount--;
+ if (entry->refCount == 0) {
+ result = VMCI_SUCCESS_ENTRY_DEAD;
+ goto done;
+ }
+
+ done:
+ VMCI_ReleaseLock(&table->lock, flags);
+
+ return result;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTableGetEntryLocked --
+ *
+ * Looks up an entry in the hash table, that is already locked.
+ *
+ * Result:
+ * If the element is found, a pointer to the element is returned.
+ * Otherwise NULL is returned.
+ *
+ * Side effects:
+ * The reference count of the returned element is increased.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static INLINE VMCIHashEntry *
+VMCIHashTableGetEntryLocked(VMCIHashTable *table, // IN
+ VMCIHandle handle) // IN
+{
+ VMCIHashEntry *cur = NULL;
+ int idx;
+
+ ASSERT(!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE));
+ ASSERT(table);
+
+ idx = VMCI_HASHTABLE_HASH(handle, table->size);
+
+ cur = table->entries[idx];
+ while (TRUE) {
+ if (cur == NULL) {
+ break;
+ }
+
+ if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) ==
+ VMCI_HANDLE_TO_RESOURCE_ID(handle)) {
+ if ((VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) ==
+ VMCI_HANDLE_TO_CONTEXT_ID(handle)) ||
+ (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(cur->handle))) {
+ cur->refCount++;
+ break;
+ }
+ }
+ cur = cur->next;
+ }
+
+ return cur;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_GetEntry --
+ * XXX Factor out the hashtable code to shared amongst API and perhaps
+ * host and guest.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCIHashEntry *
+VMCIHashTable_GetEntry(VMCIHashTable *table, // IN
+ VMCIHandle handle) // IN
+{
+ VMCIHashEntry *entry;
+ VMCILockFlags flags;
+
+ if (VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) {
+ return NULL;
+ }
+
+ ASSERT(table);
+
+ VMCI_GrabLock(&table->lock, &flags);
+ entry = VMCIHashTableGetEntryLocked(table, handle);
+ VMCI_ReleaseLock(&table->lock, flags);
+
+ return entry;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTableReleaseEntryLocked --
+ *
+ * Releases an element previously obtained with
+ * VMCIHashTableGetEntryLocked.
+ *
+ * Result:
+ * If the entry is removed from the hash table, VMCI_SUCCESS_ENTRY_DEAD
+ * is returned. Otherwise, VMCI_SUCCESS is returned.
+ *
+ * Side effects:
+ * The reference count of the entry is decreased and the entry is removed
+ * from the hash table on 0.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static INLINE int
+VMCIHashTableReleaseEntryLocked(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN
+{
+ int result = VMCI_SUCCESS;
+
+ ASSERT(table);
+ ASSERT(entry);
+
+ entry->refCount--;
+ /* Check if this is last reference and report if so. */
+ if (entry->refCount == 0) {
+
+ /*
+ * Remove entry from hash table if not already removed. This could have
+ * happened already because VMCIHashTable_RemoveEntry was called to unlink
+ * it. We ignore if it is not found. Datagram handles will often have
+ * RemoveEntry called, whereas SharedMemory regions rely on ReleaseEntry
+ * to unlink the entry, since the creator does not call RemoveEntry when
+ * it detaches.
+ */
+
+ HashTableUnlinkEntry(table, entry);
+ result = VMCI_SUCCESS_ENTRY_DEAD;
+ }
+
+ return result;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_ReleaseEntry --
+ * XXX Factor out the hashtable code to shared amongst API and perhaps
+ * host and guest.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIHashTable_ReleaseEntry(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN
+{
+ VMCILockFlags flags;
+ int result;
+
+ ASSERT(table);
+ VMCI_GrabLock(&table->lock, &flags);
+ result = VMCIHashTableReleaseEntryLocked(table, entry);
+ VMCI_ReleaseLock(&table->lock, flags);
+
+ return result;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_EntryExists --
+ * XXX Factor out the hashtable code to shared amongst API and perhaps
+ * host and guest.
+ *
+ * Result:
+ * TRUE if handle already in hashtable. FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+Bool
+VMCIHashTable_EntryExists(VMCIHashTable *table, // IN
+ VMCIHandle handle) // IN
+{
+ Bool exists;
+ VMCILockFlags flags;
+
+ ASSERT(table);
+
+ VMCI_GrabLock(&table->lock, &flags);
+ exists = VMCIHashTableEntryExistsLocked(table, handle);
+ VMCI_ReleaseLock(&table->lock, flags);
+
+ return exists;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTableEntryExistsLocked --
+ *
+ * Unlocked version of VMCIHashTable_EntryExists.
+ *
+ * Result:
+ * TRUE if handle already in hashtable. FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static Bool
+VMCIHashTableEntryExistsLocked(VMCIHashTable *table, // IN
+ VMCIHandle handle) // IN
+
+{
+ VMCIHashEntry *entry;
+ int idx;
+
+ ASSERT(table);
+
+ idx = VMCI_HASHTABLE_HASH(handle, table->size);
+
+ entry = table->entries[idx];
+ while (entry) {
+ if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) ==
+ VMCI_HANDLE_TO_RESOURCE_ID(handle)) {
+ if ((VMCI_HANDLE_TO_CONTEXT_ID(entry->handle) ==
+ VMCI_HANDLE_TO_CONTEXT_ID(handle)) ||
+ (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(handle))) {
+ return TRUE;
+ }
+ }
+ entry = entry->next;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * HashTableUnlinkEntry --
+ * XXX Factor out the hashtable code to shared amongst API and perhaps
+ * host and guest.
+ * Assumes caller holds table lock.
+ *
+ * Result:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+HashTableUnlinkEntry(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN
+{
+ int result;
+ VMCIHashEntry *prev, *cur;
+ int idx;
+
+ idx = VMCI_HASHTABLE_HASH(entry->handle, table->size);
+
+ prev = NULL;
+ cur = table->entries[idx];
+ while (TRUE) {
+ if (cur == NULL) {
+ result = VMCI_ERROR_NOT_FOUND;
+ break;
+ }
+ if (VMCI_HANDLE_EQUAL(cur->handle, entry->handle)) {
+ ASSERT(cur == entry);
+
+ /* Remove entry and break. */
+ if (prev) {
+ prev->next = cur->next;
+ } else {
+ table->entries[idx] = cur->next;
+ }
+ cur->next = NULL;
+ result = VMCI_SUCCESS;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIHashTable_Sync --
+ *
+ * Use this as a synchronization point when setting globals, for example,
+ * during device shutdown.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIHashTable_Sync(VMCIHashTable *table)
+{
+ VMCILockFlags flags;
+ ASSERT(table);
+ VMCI_GrabLock(&table->lock, &flags);
+ VMCI_ReleaseLock(&table->lock, flags);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciHashtable.h --
+ *
+ * Hash table for use in the APIs.
+ */
+
+#ifndef _VMCI_HASHTABLE_H_
+#define _VMCI_HASHTABLE_H_
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_kernel_if.h"
+#include "vmci_defs.h"
+
+typedef struct VMCIHashEntry {
+ VMCIHandle handle;
+ int refCount;
+ struct VMCIHashEntry *next;
+} VMCIHashEntry;
+
+typedef struct VMCIHashTable {
+ VMCIHashEntry **entries;
+ int size; // Number of buckets in above array.
+ VMCILock lock;
+} VMCIHashTable;
+
+VMCIHashTable *VMCIHashTable_Create(int size);
+void VMCIHashTable_Destroy(VMCIHashTable *table);
+void VMCIHashTable_InitEntry(VMCIHashEntry *entry, VMCIHandle handle);
+int VMCIHashTable_AddEntry(VMCIHashTable *table, VMCIHashEntry *entry);
+int VMCIHashTable_RemoveEntry(VMCIHashTable *table, VMCIHashEntry *entry);
+VMCIHashEntry *VMCIHashTable_GetEntry(VMCIHashTable *table, VMCIHandle handle);
+int VMCIHashTable_ReleaseEntry(VMCIHashTable *table, VMCIHashEntry *entry);
+Bool VMCIHashTable_EntryExists(VMCIHashTable *table, VMCIHandle handle);
+void VMCIHashTable_Sync(VMCIHashTable *table);
+
+#endif // _VMCI_HASHTABLE_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2007 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciQueuePair.h --
+ *
+ * VMCI QueuePair API implementation in the host driver.
+ */
+
+#ifndef _VMCI_QUEUE_PAIR_H_
+#define _VMCI_QUEUE_PAIR_H_
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_defs.h"
+#include "vmciContext.h"
+#include "vmci_iocontrols.h"
+#include "vmciQueue.h"
+#ifdef VMKERNEL
+#include "vm_atomic.h"
+#include "util_copy_dist.h"
+#include "shm.h"
+
+/*
+ * On vmkernel, the queue pairs are either backed by shared memory or
+ * kernel memory allocated on the VMCI heap. Shared memory is used for
+ * guest to guest and guest to host queue pairs, whereas the heap
+ * allocated queue pairs are used for host local queue pairs.
+ */
+
+typedef struct QueuePairPageStore {
+ Bool shared; // Indicates whether the pages are stored in shared memory
+ union {
+ Shm_ID shmID;
+ void *ptr;
+ } store;
+} QueuePairPageStore;
+
+#else
+
+typedef struct QueuePairPageStore {
+ Bool user; // Whether the page file strings are userspace pointers
+ VA64 producePageFile; // Name of the file
+ VA64 consumePageFile; // Name of the file
+ uint64 producePageFileSize; // Size of the string
+ uint64 consumePageFileSize; // Size of the string
+ VA64 producePageUVA; // User space VA of the mapped file in VMX
+ VA64 consumePageUVA; // User space VA of the mapped file in VMX
+} QueuePairPageStore;
+
+#endif // !VMKERNEL
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCI_QP_PAGESTORE_IS_WELLFORMED --
+ *
+ * Utility function that checks whether the fields of the page
+ * store contain valid values.
+ *
+ * Result:
+ * TRUE if the page store is wellformed. FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static INLINE Bool
+VMCI_QP_PAGESTORE_IS_WELLFORMED(QueuePairPageStore *pageStore) // IN
+{
+#ifdef VMKERNEL
+ return (pageStore->shared && pageStore->store.shmID != SHM_INVALID_ID) ||
+ (!pageStore->shared && pageStore->store.ptr != NULL);
+#else
+ return pageStore->producePageFile && pageStore->consumePageFile &&
+ pageStore->producePageFileSize && pageStore->consumePageFileSize;
+#endif // !VMKERNEL
+}
+
+
+int VMCIQPBroker_Init(void);
+void VMCIQPBroker_Exit(void);
+void VMCIQPBroker_Lock(void);
+void VMCIQPBroker_Unlock(void);
+int VMCIQPBroker_Alloc(VMCIHandle handle, VMCIId peer, uint32 flags,
+ VMCIPrivilegeFlags privFlags,
+ uint64 produceSize, uint64 consumeSize,
+ QueuePairPageStore *pageStore,
+ VMCIContext *context);
+int VMCIQPBroker_SetPageStore(VMCIHandle handle,
+ QueuePairPageStore *pageStore,
+ VMCIContext *context);
+int VMCIQPBroker_Detach(VMCIHandle handle, VMCIContext *context, Bool detach);
+
+#if (defined(__linux__) || defined(_WIN32) || defined(__APPLE__)) && !defined(VMKERNEL)
+struct VMCIQueue;
+
+typedef struct PageStoreAttachInfo {
+ char producePageFile[VMCI_PATH_MAX];
+ char consumePageFile[VMCI_PATH_MAX];
+ uint64 numProducePages;
+ uint64 numConsumePages;
+
+ /* User VAs in the VMX task */
+ VA64 produceBuffer;
+ VA64 consumeBuffer;
+
+ /*
+ * Platform-specific references to the physical pages backing the
+ * queue. These include a page for the header.
+ *
+ * PR361589 tracks this, too.
+ */
+
+#if defined(__linux__)
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+ struct kiobuf *produceIoBuf;
+ struct kiobuf *consumeIoBuf;
+# else
+ struct page **producePages;
+ struct page **consumePages;
+# endif
+#elif defined(_WIN32)
+ void *kmallocPtr;
+ size_t kmallocSize;
+ PMDL produceMDL;
+ PMDL consumeMDL;
+#elif defined(__APPLE__)
+ /*
+ * All the Mac OS X fields are members of the VMCIQueue
+ */
+#endif
+} PageStoreAttachInfo;
+
+#endif // (__linux__ || _WIN32 || __APPLE__) && !VMKERNEL
+
+
+int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ,
+ uint64 produceSize, VMCIQueue **consumeQ,
+ uint64 consumeSize, VMCIId peer, uint32 flags,
+ VMCIPrivilegeFlags privFlags);
+int VMCIQueuePair_Detach(VMCIHandle handle);
+
+
+#endif /* !_VMCI_QUEUE_PAIR_H_ */
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciResource.c --
+ *
+ * Implementation of the VMCI Resource Access Control API.
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+# include "driver-config.h"
+# include "compat_kernel.h"
+# include "compat_module.h"
+#endif // __linux__
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciHashtable.h"
+#include "vmciResource.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
+
+#define LGPFX "VMCIResource: "
+
+/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */
+static uint32 resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
+static VMCILock resourceIdLock;
+
+static void VMCIResourceDoRemove(VMCIResource *resource);
+
+static VMCIHashTable *resourceTable = NULL;
+
+
+/* Public Resource Access Control API. */
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Init --
+ *
+ * Initializes the VMCI Resource Access Control API. Creates a hashtable
+ * to hold all resources, and registers vectors and callbacks for hypercalls.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+int
+VMCIResource_Init(void)
+{
+ resourceTable = VMCIHashTable_Create(128);
+ if (resourceTable == NULL) {
+ VMCI_WARNING((LGPFX"Failed creating a resource hash table for VMCI.\n"));
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ VMCI_InitLock(&resourceIdLock, "VMCIRIDLock",
+ VMCI_LOCK_RANK_HIGHEST);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Exit --
+ *
+ * Cleans up resources.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+void
+VMCIResource_Exit(void)
+{
+ /* Cleanup resources.*/
+ VMCI_CleanupLock(&resourceIdLock);
+
+ if (resourceTable) {
+ VMCIHashTable_Destroy(resourceTable);
+ }
+}
+
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * VMCIResource_GetID --
+ *
+ * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are
+ * reserved so we start from its value + 1.
+ *
+ * Result:
+ * VMCI resource id on success, VMCI_INVALID_ID on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+
+VMCIId
+VMCIResource_GetID(VMCIId contextID)
+{
+ VMCIId oldRID = resourceID;
+ VMCIId currentRID;
+ Bool foundRID = FALSE;
+
+ /*
+ * Generate a unique resource ID. Keep on trying until we wrap around
+ * in the RID space.
+ */
+ ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX);
+
+ do {
+ VMCILockFlags flags;
+ VMCIHandle handle;
+
+ VMCI_GrabLock(&resourceIdLock, &flags);
+ currentRID = resourceID;
+ handle = VMCI_MAKE_HANDLE(contextID, currentRID);
+ resourceID++;
+ if (UNLIKELY(resourceID == VMCI_INVALID_ID)) {
+ /*
+ * Skip the reserved rids.
+ */
+
+ resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
+ }
+ VMCI_ReleaseLock(&resourceIdLock, flags);
+ foundRID = !VMCIHashTable_EntryExists(resourceTable, handle);
+ } while (!foundRID && resourceID != oldRID);
+
+ if (UNLIKELY(!foundRID)) {
+ return VMCI_INVALID_ID;
+ } else {
+ return currentRID;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Add --
+ *
+ * Results:
+ * VMCI_SUCCESS if successful, error code if not.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+int
+VMCIResource_Add(VMCIResource *resource, // IN
+ VMCIResourceType resourceType, // IN
+ VMCIHandle resourceHandle, // IN
+ VMCIResourceFreeCB containerFreeCB, // IN
+ void *containerObject) // IN
+{
+ int result;
+
+ ASSERT(resource);
+
+ if (VMCI_HANDLE_EQUAL(resourceHandle, VMCI_INVALID_HANDLE)) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid arguments resource 0x%x:0x%x.\n",
+ resourceHandle.context, resourceHandle.resource));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ VMCIHashTable_InitEntry(&resource->hashEntry, resourceHandle);
+ resource->type = resourceType;
+ resource->containerFreeCB = containerFreeCB;
+ resource->containerObject = containerObject;
+ resource->handle = resourceHandle;
+
+ /* Add resource to hashtable. */
+ result = VMCIHashTable_AddEntry(resourceTable, &resource->hashEntry);
+ if (result != VMCI_SUCCESS) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to add entry to hash table.\n"));
+ return result;
+ }
+
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Remove --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+void
+VMCIResource_Remove(VMCIHandle resourceHandle, // IN:
+ VMCIResourceType resourceType) // IN:
+{
+ VMCIResource *resource = VMCIResource_Get(resourceHandle, resourceType);
+ if (resource == NULL) {
+ return;
+ }
+
+ /* Remove resource from hashtable. */
+ VMCIHashTable_RemoveEntry(resourceTable, &resource->hashEntry);
+
+ VMCIResource_Release(resource);
+ /* resource could be freed by now. */
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Get --
+ *
+ * Results:
+ * Resource is successful. Otherwise NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+VMCIResource *
+VMCIResource_Get(VMCIHandle resourceHandle, // IN
+ VMCIResourceType resourceType) // IN
+{
+ VMCIResource *resource;
+ VMCIHashEntry *entry = VMCIHashTable_GetEntry(resourceTable, resourceHandle);
+ if (entry == NULL) {
+ return NULL;
+ }
+ resource = RESOURCE_CONTAINER(entry, VMCIResource, hashEntry);
+ if ((resourceType == VMCI_RESOURCE_TYPE_ANY) || (resource->type == resourceType)) {
+ return resource;
+ }
+ VMCIHashTable_ReleaseEntry(resourceTable, entry);
+ return NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResourceDoRemove --
+ *
+ * Deallocates data structures associated with the given resource
+ * and invoke any call back registered for the resource.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May deallocate memory and invoke a callback for the removed resource.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+static void INLINE
+VMCIResourceDoRemove(VMCIResource *resource)
+{
+ ASSERT(resource);
+
+ if (resource->containerFreeCB) {
+ resource->containerFreeCB(resource->containerObject);
+ /* Resource has been freed don't dereference it. */
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------------
+ *
+ * VMCIResource_Release --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * resource's containerFreeCB will get called if last reference.
+ *
+ *-----------------------------------------------------------------------------------
+ */
+
+int
+VMCIResource_Release(VMCIResource *resource)
+{
+ int result;
+
+ ASSERT(resource);
+
+ result = VMCIHashTable_ReleaseEntry(resourceTable, &resource->hashEntry);
+ if (result == VMCI_SUCCESS_ENTRY_DEAD) {
+ VMCIResourceDoRemove(resource);
+ }
+
+ /*
+ * We propagate the information back to caller in case it wants to know
+ * whether entry was freed.
+ */
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIResource_Sync --
+ *
+ * Use this as a synchronization point when setting globals, for example,
+ * during device shutdown.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIResource_Sync(void)
+{
+ VMCIHashTable_Sync(resourceTable);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciResource.h --
+ *
+ * VMCI Resource Access Control API.
+ */
+
+#ifndef _VMCI_RESOURCE_H_
+#define _VMCI_RESOURCE_H_
+
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_defs.h"
+#include "vmci_kernel_if.h"
+#include "vmciHashtable.h"
+#include "vmciContext.h"
+
+#define RESOURCE_CONTAINER(ptr, type, member) \
+ ((type *)((char *)(ptr) - offsetof(type, member)))
+
+typedef void(*VMCIResourceFreeCB)(void *resource);
+
+typedef enum {
+ VMCI_RESOURCE_TYPE_ANY,
+ VMCI_RESOURCE_TYPE_API,
+ VMCI_RESOURCE_TYPE_GROUP,
+ VMCI_RESOURCE_TYPE_DATAGRAM,
+ VMCI_RESOURCE_TYPE_DOORBELL,
+} VMCIResourceType;
+
+typedef struct VMCIResource {
+ VMCIHashEntry hashEntry;
+ VMCIResourceType type;
+ VMCIResourceFreeCB containerFreeCB; // Callback to free container
+ // object when refCount is 0.
+ void *containerObject; // Container object reference.
+ VMCIHandle handle;
+} VMCIResource;
+
+
+int VMCIResource_Init(void);
+void VMCIResource_Exit(void);
+void VMCIResource_Sync(void);
+
+VMCIId VMCIResource_GetID(VMCIId contextID);
+
+int VMCIResource_Add(VMCIResource *resource, VMCIResourceType resourceType,
+ VMCIHandle resourceHandle,
+ VMCIResourceFreeCB containerFreeCB, void *containerObject);
+void VMCIResource_Remove(VMCIHandle resourceHandle,
+ VMCIResourceType resourceType);
+VMCIResource *VMCIResource_Get(VMCIHandle resourceHandle,
+ VMCIResourceType resourceType);
+int VMCIResource_Release(VMCIResource *resource);
+
+
+#endif // _VMCI_RESOURCE_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2011 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciRoute.c --
+ *
+ * Implementation of VMCI routing rules.
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+# include "driver-config.h"
+# include "compat_kernel.h"
+# include "compat_module.h"
+#endif // __linux__
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciContext.h"
+#include "vmciKernelAPI.h"
+#include "vmciRoute.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
+
+#define LGPFX "VMCIRoute: "
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCI_Route --
+ *
+ * Make a routing decision for the given source and destination handles.
+ * This will try to determine the route using the handles and the available
+ * devices.
+ *
+ * Result:
+ * A VMCIRoute value.
+ *
+ * Side effects:
+ * Sets the source context if it is invalid.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCI_Route(VMCIHandle *src, // IN/OUT
+ const VMCIHandle *dst, // IN
+ Bool fromGuest, // IN
+ VMCIRoute *route) // OUT
+{
+ Bool hasHostDevice;
+ Bool hasGuestDevice;
+
+ ASSERT(src);
+ ASSERT(dst);
+ ASSERT(route);
+
+ *route = VMCI_ROUTE_NONE;
+
+ /*
+ * "fromGuest" is only ever set to TRUE by IOCTL_VMCI_DATAGRAM_SEND (or by
+ * the vmkernel equivalent), which comes from the VMX, so we know it is
+ * coming from a guest.
+ */
+
+ /*
+ * To avoid inconsistencies, test these once. We will test them again
+ * when we do the actual send to ensure that we do not touch a non-existent
+ * device.
+ */
+
+ hasHostDevice = VMCI_HasHostDevice();
+ hasGuestDevice = VMCI_HasGuestDevice();
+
+ /* Must have a valid destination context. */
+ if (VMCI_INVALID_ID == dst->context) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /*
+ * If we are acting as a host and the datagram is from or for a well-known
+ * context (which also means it must have been passed down from a guest),
+ * then we can assume it is intended for a guest on this host.
+ */
+
+ if (fromGuest && hasHostDevice &&
+ (VMCI_WELL_KNOWN_CONTEXT_ID == src->context ||
+ VMCI_WELL_KNOWN_CONTEXT_ID == dst->context)) {
+ *route = VMCI_ROUTE_HOST_TO_GUEST;
+ return VMCI_SUCCESS;
+ }
+
+ /* Anywhere to hypervisor. */
+ if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) {
+ /*
+ * If this message already came from a guest then we cannot send it
+ * to the hypervisor. It must come from a local client.
+ */
+
+ if (fromGuest) {
+ return VMCI_ERROR_DST_UNREACHABLE;
+ }
+
+ /* We must be acting as a guest in order to send to the hypervisor. */
+ if (!hasGuestDevice) {
+ return VMCI_ERROR_DEVICE_NOT_FOUND;
+ }
+
+ /* And we cannot send if the source is the host context. */
+ if (VMCI_HOST_CONTEXT_ID == src->context) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /* Send from local client down to the hypervisor. */
+ *route = VMCI_ROUTE_GUEST_TO_HYPERVISOR;
+ return VMCI_SUCCESS;
+ }
+
+ /* Anywhere to local client on host. */
+ if (VMCI_HOST_CONTEXT_ID == dst->context) {
+ /*
+ * If it is not from a guest but we are acting as a guest, then we need
+ * to send it down to the host. Note that if we are also acting as a
+ * host then this will prevent us from sending from local client to
+ * local client, but we accept that restriction as a way to remove
+ * any ambiguity from the host context.
+ */
+
+ if (!fromGuest && hasGuestDevice) {
+ /* If no source context then use the current. */
+ if (VMCI_INVALID_ID == src->context) {
+ src->context = VMCI_GetContextID();
+ }
+
+ /* Send it from local client down to the host. */
+ *route = VMCI_ROUTE_GUEST_TO_HOST;
+ return VMCI_SUCCESS;
+ }
+
+ /*
+ * Otherwise we already received it from a guest and it is destined
+ * for a local client on this host, or it is from another local client
+ * on this host. We must be acting as a host to service it.
+ */
+
+ if (!hasHostDevice) {
+ return VMCI_ERROR_DEVICE_NOT_FOUND;
+ }
+
+ if (VMCI_INVALID_ID == src->context) {
+ /*
+ * If it came from a guest then it must have a valid context.
+ * Otherwise we can use the host context.
+ */
+
+ if (fromGuest) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+ src->context = VMCI_HOST_CONTEXT_ID;
+ }
+
+ /* Route to local client. */
+ *route = VMCI_ROUTE_HOST_TO_SELF;
+ return VMCI_SUCCESS;
+ }
+
+ /* If we are acting as a host then this might be destined for a guest. */
+ if (hasHostDevice) {
+ /* It will have a context if it is meant for a guest. */
+ VMCIContext *context = VMCIContext_Get(dst->context);
+ if (NULL != context) {
+ if (VMCI_INVALID_ID == src->context) {
+ /*
+ * If it came from a guest then it must have a valid context.
+ * Otherwise we can use the host context.
+ */
+
+ if (fromGuest) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+ src->context = VMCI_HOST_CONTEXT_ID;
+ }
+
+ /* Pass it up to the guest. */
+ *route = VMCI_ROUTE_HOST_TO_GUEST;
+ return VMCI_SUCCESS;
+ }
+ }
+
+ /*
+ * We must be a guest trying to send to another guest, which means we
+ * need to send it down to the host.
+ */
+
+ if (!hasGuestDevice) {
+ return VMCI_ERROR_DEVICE_NOT_FOUND;
+ }
+
+ /* If no source context then use the current context. */
+ if (VMCI_INVALID_ID == src->context) {
+ src->context = VMCI_GetContextID();
+ }
+
+ /*
+ * Send it from local client down to the host, which will route it to
+ * the other guest for us.
+ */
+
+ *route = VMCI_ROUTE_GUEST_TO_HOST;
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCI_RouteString --
+ *
+ * Get a string for the given route.
+ *
+ * Result:
+ * A string representing the route, if the route is valid, otherwise an
+ * empty string.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+const char *
+VMCI_RouteString(VMCIRoute route) // IN
+{
+ const char *vmciRouteStrings[] = {
+ "none",
+ "host to self",
+ "host to guest",
+ "guest to host/hypervisor",
+ };
+ if (route >= VMCI_ROUTE_NONE && route <= VMCI_ROUTE_GUEST_TO_HYPERVISOR) {
+ return vmciRouteStrings[route];
+ }
+ return "";
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2011 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no later version.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciRoute.h --
+ *
+ * VMCI Routing.
+ */
+
+#ifndef _VMCI_ROUTE_H_
+#define _VMCI_ROUTE_H_
+
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#include "includeCheck.h"
+
+#include "vmci_defs.h"
+
+
+typedef enum {
+ VMCI_ROUTE_NONE,
+ VMCI_ROUTE_HOST_TO_SELF,
+ VMCI_ROUTE_HOST_TO_GUEST,
+ VMCI_ROUTE_GUEST_TO_HOST,
+ VMCI_ROUTE_GUEST_TO_HYPERVISOR = VMCI_ROUTE_GUEST_TO_HOST
+} VMCIRoute;
+
+
+int VMCI_Route(VMCIHandle *src, const VMCIHandle *dst, Bool fromGuest,
+ VMCIRoute *route);
+const char *VMCI_RouteString(VMCIRoute route);
+
+
+#endif // _VMCI_ROUTE_H_
#ifdef __linux__
# include "driver-config.h"
-
# define EXPORT_SYMTAB
-
# include <linux/module.h>
# include "compat_kernel.h"
# include "compat_slab.h"
# include "compat_interrupt.h"
-#elif defined(_WIN32)
-# ifndef WINNT_DDK
-# error This file only works with the NT ddk
-# endif // WINNT_DDK
-# include <ntddk.h>
-#elif defined(SOLARIS)
-# include <sys/ddi.h>
-# include <sys/sunddi.h>
-# include <sys/disp.h>
-#elif defined(__APPLE__)
-# include <IOKit/IOLib.h>
-#else
-#error "platform not supported."
-#endif //linux
+#endif
-#define LGPFX "VMCIUtil: "
-
-#include "vmware.h"
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
#include "vm_atomic.h"
#include "vmci_defs.h"
#include "vmci_kernel_if.h"
#include "vmciEvent.h"
#include "vmciKernelAPI.h"
+#define LGPFX "VMCIUtil: "
+
static void VMCIUtilCidUpdate(VMCIId subID, VMCI_EventData *eventData,
void *clientData);
dg->dst.resource == VMCI_EVENT_HANDLER) {
result = VMCIEvent_Dispatch(dg);
} else {
- result = VMCIDatagram_Dispatch(dg->src.context, dg);
+ result = VMCIDatagram_InvokeGuestHandler(dg);
}
if (result < VMCI_SUCCESS) {
VMCI_DEBUG_LOG(4, (LGPFX"Datagram with resource %d failed with "
}
}
}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCIContext_GetPrivFlags --
- *
- * Provided for compatibility with the host VMCI API.
- *
- * Results:
- * Always returns VMCI_NO_PRIVILEGE_FLAGS.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCIContext_GetPrivFlags)
-VMCIPrivilegeFlags
-VMCIContext_GetPrivFlags(VMCIId contextID) // IN
-{
- return VMCI_NO_PRIVILEGE_FLAGS;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCI_ContextID2HostVmID --
- *
- * Provided for compatibility with the host VMCI API.
- *
- * Results:
- * Returns VMCI_ERROR_UNAVAILABLE.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCI_ContextID2HostVmID)
-int
-VMCI_ContextID2HostVmID(VMCIId contextID, // IN
- void *hostVmID, // OUT
- size_t hostVmIDLen) // IN
-{
- return VMCI_ERROR_UNAVAILABLE;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCI_IsContextOwner --
- *
- * Provided for compatibility with the host VMCI API.
- *
- * Results:
- * Returns VMCI_ERROR_UNAVAILABLE.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCI_IsContextOwner)
-int
-VMCI_IsContextOwner(VMCIId contextID, // IN
- void *hostUser) // IN
-{
- return VMCI_ERROR_UNAVAILABLE;
-}
-
#include "vmci_infrastructure.h"
#include "vmci_iocontrols.h"
#include "vmci_version.h"
+#include "vmciContext.h"
#include "vmciDatagram.h"
#include "vmciEvent.h"
#include "vmciInt.h"
#include "vmciNotifications.h"
#include "vmciQueuePairInt.h"
+#include "vmciResource.h"
#include "vmciUtil.h"
#define LGPFX "VMCI: "
* components, and it may be invoked once request_irq() has
* registered the handler (as the irq line may be shared).
*/
+ VMCIResource_Init();
+ VMCIContext_Init();
VMCIDatagram_Init();
VMCIEvent_Init();
VMCIUtil_Init();
VMCINotifications_Exit();
VMCIUtil_Exit();
VMCIEvent_Exit();
+ VMCIContext_Exit();
+ VMCIResource_Exit();
if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX) {
pci_disable_msix(pdev);
} else if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSI) {
vfree(notification_bitmap);
}
+ VMCIContext_Exit();
+ VMCIResource_Exit();
+
printk(KERN_INFO "Unregistered vmci device.\n");
compat_mutex_unlock(&dev->lock);
#ifndef _VMCI_VERSION_H_
#define _VMCI_VERSION_H_
-#define VMCI_DRIVER_VERSION 9.1.6.0
-#define VMCI_DRIVER_VERSION_COMMAS 9,1,6,0
-#define VMCI_DRIVER_VERSION_STRING "9.1.6.0"
+#define VMCI_DRIVER_VERSION 9.1.7.0
+#define VMCI_DRIVER_VERSION_COMMAS 9,1,7,0
+#define VMCI_DRIVER_VERSION_STRING "9.1.7.0"
#endif /* _VMCI_VERSION_H_ */