]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Merge Linux VMCI guest and host driver code
authorVMware, Inc <>
Tue, 29 Mar 2011 20:10:44 +0000 (13:10 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Tue, 29 Mar 2011 20:10:44 +0000 (13:10 -0700)
This change merges the code for the Linux specific part of the
VMCI driver. The change has two parts: one is the merging of
the actual code of the drivers, and the other is changing the
host and guest makefiles to pick up the VMCI driver files from
the same place.

The actual merging of the Linux driver code is mainly a code
movement from the guest driver to the host driver. Currently,
the driver will still be either guest or host, based on the
flag isGuestDriver which gets controlled by whether
VMX86_TOOLS is defined. All code is now compiled regardless of
whether the driver is guest or host.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
30 files changed:
open-vm-tools/modules/linux/shared/vmci_kernel_if.h
open-vm-tools/modules/linux/vmci/Makefile.kernel
open-vm-tools/modules/linux/vmci/Makefile.normal [new file with mode: 0644]
open-vm-tools/modules/linux/vmci/common/vmciCommonInt.h [moved from open-vm-tools/modules/linux/vmci/vmciCommonInt.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciContext.c [moved from open-vm-tools/modules/linux/vmci/vmciContext.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciContext.h [moved from open-vm-tools/modules/linux/vmci/vmciContext.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDatagram.c [moved from open-vm-tools/modules/linux/vmci/vmciDatagram.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDatagram.h [moved from open-vm-tools/modules/linux/vmci/vmciDatagram.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDoorbell.c [moved from open-vm-tools/modules/linux/vmci/vmciDoorbell.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDoorbell.h [moved from open-vm-tools/modules/linux/vmci/vmciDoorbell.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDriver.c [moved from open-vm-tools/modules/linux/vmci/vmciDriver.c with 96% similarity]
open-vm-tools/modules/linux/vmci/common/vmciDriver.h [moved from open-vm-tools/modules/linux/vmci/vmciDriver.h with 98% similarity]
open-vm-tools/modules/linux/vmci/common/vmciEvent.c [moved from open-vm-tools/modules/linux/vmci/vmciEvent.c with 99% similarity]
open-vm-tools/modules/linux/vmci/common/vmciEvent.h [moved from open-vm-tools/modules/linux/vmci/vmciEvent.h with 98% similarity]
open-vm-tools/modules/linux/vmci/common/vmciHashtable.c [moved from open-vm-tools/modules/linux/vmci/vmciHashtable.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciHashtable.h [moved from open-vm-tools/modules/linux/vmci/vmciHashtable.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciQPair.c [moved from open-vm-tools/modules/linux/vmci/vmciQPair.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c [moved from open-vm-tools/modules/linux/vmci/vmciQueuePair.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h [moved from open-vm-tools/modules/linux/vmci/vmciQueuePair.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciResource.c [moved from open-vm-tools/modules/linux/vmci/vmciResource.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciResource.h [moved from open-vm-tools/modules/linux/vmci/vmciResource.h with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciRoute.c [moved from open-vm-tools/modules/linux/vmci/vmciRoute.c with 100% similarity]
open-vm-tools/modules/linux/vmci/common/vmciRoute.h [moved from open-vm-tools/modules/linux/vmci/vmciRoute.h with 100% similarity]
open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c [moved from open-vm-tools/modules/linux/vmci/vmciKernelIf.c with 100% similarity]
open-vm-tools/modules/linux/vmci/linux/vmci_drv.c [new file with mode: 0644]
open-vm-tools/modules/linux/vmci/linux/vmci_version.h [moved from open-vm-tools/modules/linux/vmci/vmci_version.h with 87% similarity]
open-vm-tools/modules/linux/vmci/shared/pgtbl.h [moved from open-vm-tools/modules/linux/vmci/pgtbl.h with 100% similarity]
open-vm-tools/modules/linux/vmci/shared/vmciQueue.h [moved from open-vm-tools/modules/linux/vmci/vmciQueue.h with 100% similarity]
open-vm-tools/modules/linux/vmci/shared/vmci_handle_array.h [moved from open-vm-tools/modules/linux/vmci/vmci_handle_array.h with 100% similarity]
open-vm-tools/modules/linux/vmci/vmci_drv.c [deleted file]

index a5f4b24c158b1180caa165a88c6d725d3400171c..f726658a7544fc606a0cfcac5b8e5447c2e1039f 100644 (file)
@@ -394,8 +394,6 @@ void VMCI_FreeQueueBuffer(void *queue, uint64 size);
 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)
@@ -405,14 +403,19 @@ Bool VMCI_HasHostDevice(void);
 #  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(_WIN32) || (defined(linux) && !defined(VMKERNEL))
+  Bool VMCI_HasGuestDevice(void);
+  Bool VMCI_HasHostDevice(void);
+#else
+#  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 || (linux && !VMKERNEL))
 
 
 #if defined(VMKERNEL)
index b256fc85811bf69154a09c6e4de0ca1b6eaa4e54..805ae488e1a4e583cab154a19c3c5ce8120703e6 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/make -f
 ##########################################################
-# Copyright (C) 2005 VMware, Inc. All rights reserved.
+# 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
 #
 ##########################################################
 
-####
-####  VMware vmci Makefile to be distributed externally
-####
+vm_product_defines = $(if $(findstring tools,$(1)), -DVMX86_TOOLS,)
+CC_OPTS += $(call vm_product_defines, $(PRODUCT))
+CC_OPTS += -DVMCI
 
-INCLUDE += -I.
+INCLUDE += -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux
 
 EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE)
-EXTRA_CFLAGS += -DVMX86_TOOLS
 
 obj-m += $(DRIVER).o
 
-$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, $(wildcard $(SRCROOT)/*.c)))
+$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, \
+                $(wildcard $(SRCROOT)/linux/*.c $(SRCROOT)/common/*.c)))
 
 #
 # In open-vm-tools, need to compile the common sources from the shared directory.
@@ -48,8 +48,9 @@ $(addprefix $(VMCI_PATH)/,$(DRIVERLOG)): $(VMCI_PATH)/%.o: $(DRIVERLOG_PATH)/%.c
 
 clean:
        rm -rf $(wildcard $(DRIVER).mod.c $(DRIVER).ko .tmp_versions \
-              Modules.symvers Module.markers modules.order \
-              $(foreach dir,./,$(addprefix $(dir),.*.cmd .*.o.flags *.o)))
+               Module.symvers Modules.symvers Module.markers modules.order \
+               $(foreach dir,linux/ common/ \
+               ./,$(addprefix $(dir),.*.cmd .*.o.flags *.o)))
 ifneq ($(MODULEBUILDDIR),)
        rm -f $(MODULEBUILDDIR)/VMwareVMCIModule.symvers
 endif
@@ -64,4 +65,3 @@ ifneq ($(MODULEBUILDDIR),)
        cp -f $(SRCROOT)/Module.symvers $(MODULEBUILDDIR)/VMwareVMCIModule.symvers
 endif
 endif
-
diff --git a/open-vm-tools/modules/linux/vmci/Makefile.normal b/open-vm-tools/modules/linux/vmci/Makefile.normal
new file mode 100644 (file)
index 0000000..189aa91
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/make -f
+##########################################################
+# 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
+#
+##########################################################
+
+vm_check_build = $(shell if $(CC) $(CC_OPTS) $(INCLUDE) -Werror -S -o /dev/null -xc $(1) \
+        > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi)
+vm_product_defines = $(if $(findstring tools,$(1)), -DVMX86_TOOLS,)
+
+####
+####  DESTDIR is where the module, object files, and dependencies are built
+####
+DESTDIR := driver-$(VM_UNAME)
+
+####
+####  DRIVERNAME should be untouched unless you have a good reason to change 
+#### it.  The form below is how the scripts expect it.
+####
+DRIVERNAME := $(DRIVER)-xxx-$(VM_UNAME)
+
+ifneq (,$(filter x86_64%, $(shell $(CC) -dumpmachine)))
+MACHINE := x86_64
+else
+MACHINE := x386
+endif
+
+ifdef QUIET
+ECHO := @true
+else
+ECHO := @echo
+endif
+
+####
+#### You must compile with at least -O level of optimization
+#### or the module won't load.
+#### If desparate, I think that bringing in <linux/bitops.h> might
+#### suffice.
+####
+CC_WARNINGS := -Wall -Wstrict-prototypes 
+# Don't use -pipe or egcs-2.91.66 (shipped with RedHat) will die
+CC_KFLAGS := -D__KERNEL__ -fno-strength-reduce -fno-omit-frame-pointer \
+            -fno-common -DKBUILD_MODNAME=\"$(DRIVER)\"
+CC_KFLAGS += $(call vm_check_gcc,-falign-loops=2 -falign-jumps=2 -falign-functions=2, \
+            -malign-loops=2 -malign-jumps=2 -malign-functions=2)
+CC_KFLAGS += $(call vm_check_gcc,-fno-strict-aliasing,)
+CC_KFLAGS += $(call vm_product_defines, $(PRODUCT))
+ifeq ($(MACHINE),x86_64)
+CC_KFLAGS += -mno-red-zone -mcmodel=kernel
+else
+# Gcc 3.0 deprecates -m486 --hpreg
+CC_KFLAGS += -DCPU=586 $(call check_gcc,-march=i586,-m486)
+endif
+
+CC_OPTS := -O2 -DMODULE -DVMCI $(GLOBAL_DEFS) $(CC_KFLAGS) $(CC_WARNINGS) 
+
+INCLUDE := -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux \
+          -I$(HEADER_DIR)
+
+INCLUDE += $(shell $(CC) $(INCLUDE) -E $(SRCROOT)/shared/autoconf/geninclude.c \
+          | sed -n -e 's!^APATH!-I$(HEADER_DIR)/asm!p')
+
+ifdef OVT_SOURCE_PATH
+DRIVERLOGPATH := $(OVT_SOURCE_DIR)/modules/linux/shared
+else
+DRIVERLOGPATH := $(SRCROOT)/shared
+endif
+
+C_TARGETS_LINUX  := vmci_drv.o vmciKernelIf.o
+C_TARGETS_COMMON := vmciContext.o vmciDatagram.o vmciDriver.o \
+                   vmciHashtable.o vmciResource.o \
+                    vmciQueuePair.o vmciEvent.o vmciRoute.o
+
+C_TARGETS_LINUX_D := ${C_TARGETS_LINUX:.o=.d}
+C_TARGETS_COMMON_D := ${C_TARGETS_COMMON:.o=.d}
+C_TARGETS := $(C_TARGETS_LINUX) $(C_TARGETS_COMMON) driverLog.o
+
+####
+#### Make Targets are beneath here.
+####
+
+driver: setup deps
+       $(MAKE) -C $(DESTDIR) -f ../Makefile SRCROOT=../$(SRCROOT) $(DRIVER).o \
+               INCLUDE_DEPS=1
+
+setup:
+       @if [ -d $(DESTDIR) ] ; then true ; else mkdir $(DESTDIR); chmod 755 $(DESTDIR) ; fi
+
+$(DRIVER) $(DRIVER).o: $(DRIVERNAME)
+       cp -f $< $@
+
+$(DRIVERNAME): $(C_TARGETS)
+       $(ECHO) "Building $(DRIVERNAME)"   
+       ld -r -o $(DRIVERNAME) $(C_TARGETS)
+
+auto-build: 
+       $(MAKE) driver QUIET=1
+       cp -f $(DESTDIR)/$(DRIVERNAME) $(SRCROOT)/../$(DRIVER).o
+
+$(C_TARGETS_LINUX): %.o: $(SRCROOT)/linux/%.c
+       $(ECHO) "Compiling  linux/$(<F)"
+       $(CC) $(CC_OPTS) $(INCLUDE) -c $<
+
+$(C_TARGETS_COMMON): %.o: $(SRCROOT)/common/%.c
+       $(ECHO) "Compiling  common/$(<F)"
+       $(CC) $(CC_OPTS) $(INCLUDE) -c $<
+
+driverLog.o: $(DRIVERLOGPATH)/driverLog.c
+       $(CC) $(CC_OPTS) $(INCLUDE) -c $<
+
+clean:
+       rm -rf $(DESTDIR)/
+
+$(C_TARGETS_COMMON_D): %.d: $(SRCROOT)/common/%.c
+       $(ECHO) "Dependencies for $(<F)"
+       $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@
+
+$(C_TARGETS_LINUX_D): %.d: $(SRCROOT)/linux/%.c
+       $(ECHO) "Dependencies for $(<F)"
+       $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@
+
+driverLog.d: $(DRIVERLOGPATH)/driverLog.c
+       $(ECHO) "Dependencies for $(<F)"
+       $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@
+
+deps: setup 
+       $(MAKE) -C $(DESTDIR) -f ../Makefile SRCROOT=../$(SRCROOT) driver_deps 
+
+driver_deps: ${C_TARGETS:.o=.d}
+
+ifdef INCLUDE_DEPS
+include ${C_TARGETS:.o=.d}
+endif
+
+.SILENT:
similarity index 96%
rename from open-vm-tools/modules/linux/vmci/vmciDriver.c
rename to open-vm-tools/modules/linux/vmci/common/vmciDriver.c
index ff074c08638959dff61669043d24e79b41b1a28e..0b0c17fba1171daa6d7db1e0b28c16a3d4640a42 100644 (file)
@@ -30,6 +30,7 @@
 #include "vmciContext.h"
 #include "vmciDatagram.h"
 #include "vmciDoorbell.h"
+#include "vmciDriver.h"
 #include "vmciEvent.h"
 #include "vmciHashtable.h"
 #include "vmciKernelAPI.h"
@@ -39,9 +40,6 @@
 #  include "vmciVmkInt.h"
 #  include "vm_libc.h"
 #  include "helper_ext.h"
-#  include "vmciDriver.h"
-#else
-#  include "vmciDriver.h"
 #endif
 
 #define LGPFX "VMCI: "
@@ -49,6 +47,9 @@
 static Atomic_uint32 vmContextID = { VMCI_INVALID_ID };
 static VMCIContext *hostContext;
 
+static Atomic_uint32 vmciClientCount;
+static VMCIId ctxUpdateSubID = VMCI_INVALID_ID;
+
 
 /*
  *----------------------------------------------------------------------
@@ -91,12 +92,12 @@ VMCI_HostInit(void)
       goto hostContextExit;
    }
 
-   VMCI_LOG((LGPFX"Driver initialized."));
+   VMCI_LOG((LGPFX"host components initialized."));
    return VMCI_SUCCESS;
 
-  hostContextExit:
+hostContextExit:
    VMCIContext_ReleaseContext(hostContext);
-  errorExit:
+errorExit:
    return result;
 }
 
@@ -180,33 +181,77 @@ VMCI_DeviceRelease(void)
 {
 }
 
+#else // VMX86_TOOLS
 
 /*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------
  *
- * VMCI_SendDatagram --
+ * VMCI_DeviceGet --
  *
- *      Stub for guest VM to hypervisor call mechanism.
+ *      Verifies that a valid VMCI device is present, and indicates
+ *      the callers intention to use the device until it calls
+ *      VMCI_DeviceRelease().
  *
  * Results:
- *      Always VMCI_ERROR_GENERIC.
+ *      TRUE if a valid VMCI device is present, FALSE otherwise.
  *
  * Side effects:
  *      None.
  *
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------
  */
 
-int
-VMCI_SendDatagram(VMCIDatagram *dg)
+VMCI_EXPORT_SYMBOL(VMCI_DeviceGet)
+Bool
+VMCI_DeviceGet(uint32 *apiVersion)
 {
-   return VMCI_ERROR_UNAVAILABLE;
+   Atomic_Inc32(&vmciClientCount);
+
+   if (*apiVersion > VMCI_KERNEL_API_VERSION) {
+      *apiVersion = VMCI_KERNEL_API_VERSION;
+      goto release;
+   }
+
+   if (!VMCI_DeviceEnabled()) {
+      goto release;
+   }
+
+   if (VMCI_DeviceShutdown()) {
+      goto release;
+   }
+
+   return TRUE;
+
+release:
+   Atomic_Dec32(&vmciClientCount);
+   return FALSE;
 }
 
-#else // VMX86_TOOLS
 
-static Atomic_uint32 vmciClientCount;
-static VMCIId ctxUpdateSubID = VMCI_INVALID_ID;
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCI_DeviceRelease --
+ *
+ *      Indicates that the caller is done using the VMCI device.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCI_DeviceRelease)
+void
+VMCI_DeviceRelease(void)
+{
+   Atomic_Dec32(&vmciClientCount);
+}
+
+#endif // VMX86_TOOLS
 
 
 /*
@@ -389,75 +434,6 @@ VMCI_CheckHostCapabilities(void)
 }
 
 
-/*
- *----------------------------------------------------------------------
- *
- * VMCI_DeviceGet --
- *
- *      Verifies that a valid VMCI device is present, and indicates
- *      the callers intention to use the device until it calls
- *      VMCI_DeviceRelease().
- *
- * Results:
- *      TRUE if a valid VMCI device is present, FALSE otherwise.
- *
- * Side effects:
- *      None.
- *
- *----------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCI_DeviceGet)
-Bool
-VMCI_DeviceGet(uint32 *apiVersion)
-{
-   Atomic_Inc32(&vmciClientCount);
-
-   if (*apiVersion > VMCI_KERNEL_API_VERSION) {
-      *apiVersion = VMCI_KERNEL_API_VERSION;
-      goto release;
-   }
-
-   if (!VMCI_DeviceEnabled()) {
-      goto release;
-   }
-
-   if (VMCI_DeviceShutdown()) {
-      goto release;
-   }
-
-   return TRUE;
-
-release:
-   Atomic_Dec32(&vmciClientCount);
-   return FALSE;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCI_DeviceRelease --
- *
- *      Indicates that the caller is done using the VMCI device.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *----------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCI_DeviceRelease)
-void
-VMCI_DeviceRelease(void)
-{
-   Atomic_Dec32(&vmciClientCount);
-}
-
-
 /*
  *----------------------------------------------------------------------
  *
@@ -624,7 +600,6 @@ VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle,  // IN
    }
 }
 
-#endif // VMX86_TOOLS
 
 /*
  *----------------------------------------------------------------------------
@@ -736,14 +711,14 @@ VMCI_SharedInit(void)
    VMCIEvent_Init();
    VMCIDoorbell_Init();
 
-   VMCI_LOG((LGPFX"Driver initialized."));
+   VMCI_LOG((LGPFX"shared components initialized."));
    return VMCI_SUCCESS;
 
-  contextExit:
+contextExit:
    VMCIContext_Exit();
-  resourceExit:
+resourceExit:
    VMCIResource_Exit();
-  errorExit:
+errorExit:
    return result;
 }
 
similarity index 98%
rename from open-vm-tools/modules/linux/vmci/vmciDriver.h
rename to open-vm-tools/modules/linux/vmci/common/vmciDriver.h
index 52b010a51673fdca30799abe3d5bbddc6f39ac9c..cfd4a821faa8a09f41c9bf4ac2a9e165b4347921 100644 (file)
@@ -83,7 +83,6 @@ void VMCI_HostCleanup(void);
 VMCIId VMCI_GetContextID(void);
 int VMCI_SendDatagram(VMCIDatagram *dg);
 
-#if defined(VMX86_TOOLS)
 void VMCIUtil_Init(void);
 void VMCIUtil_Exit(void);
 Bool VMCI_CheckHostCapabilities(void);
@@ -95,7 +94,6 @@ Bool VMCI_DeviceEnabled(void);
 int VMCIDoSendDatagram(unsigned int dgSize, ULONG *dataPort, ULONG *resultPort,
                        VMCIDatagram *dg);
 #endif // _WIN64
-#endif // VMX86_TOOLS
 
 
 #endif // _VMCI_DRIVER_H_
similarity index 99%
rename from open-vm-tools/modules/linux/vmci/vmciEvent.c
rename to open-vm-tools/modules/linux/vmci/common/vmciEvent.c
index 3319d159070ba9e80188c82711f5afbf534a424f..d009ab6a9565d489e251385f2b530cf4a8b63fff 100644 (file)
@@ -185,7 +185,6 @@ VMCIEvent_Sync(void)
 }
 
 
-#ifdef VMX86_TOOLS
 /*
  *-----------------------------------------------------------------------------
  *
@@ -210,7 +209,6 @@ VMCIEvent_CheckHostCapabilities(void)
    /* VMCIEvent does not require any hypercalls. */
    return TRUE;
 }
-#endif
 
 
 /*
similarity index 98%
rename from open-vm-tools/modules/linux/vmci/vmciEvent.h
rename to open-vm-tools/modules/linux/vmci/common/vmciEvent.h
index 3eeef29df9e4f14810f7bef4dc858bddeb6cb615..9b5f8cfa42fc968c0d0b68bbb9b2e8ae6136c33d 100644 (file)
@@ -38,8 +38,6 @@ void VMCIEvent_Init(void);
 void VMCIEvent_Exit(void);
 void VMCIEvent_Sync(void);
 int  VMCIEvent_Dispatch(VMCIDatagram *msg);
-#ifdef VMX86_TOOLS
 Bool VMCIEvent_CheckHostCapabilities(void);
-#endif
 
 #endif //__VMCI_EVENT_H__
diff --git a/open-vm-tools/modules/linux/vmci/linux/vmci_drv.c b/open-vm-tools/modules/linux/vmci/linux/vmci_drv.c
new file mode 100644 (file)
index 0000000..c01bbbf
--- /dev/null
@@ -0,0 +1,2493 @@
+/*********************************************************
+ * 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
+ *
+ *********************************************************/
+
+/* Must come before any kernel header file */
+#include "driver-config.h"
+
+#define EXPORT_SYMTAB
+
+#include <asm/io.h>
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#if defined(__x86_64__) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12)
+#   include <linux/ioctl32.h>
+/* Use weak: not all kernels export sys_ioctl for use by modules */
+asmlinkage __attribute__((weak)) long
+sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
+#endif
+#include <linux/miscdevice.h>
+#include <linux/moduleparam.h>
+#include <linux/poll.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include "compat_highmem.h"
+#include "compat_init.h"
+#include "compat_interrupt.h"
+#include "compat_ioport.h"
+#include "compat_kernel.h"
+#include "compat_mm.h"
+#include "compat_module.h"
+#include "compat_mutex.h"
+#include "compat_page.h"
+#include "compat_pci.h"
+#include "compat_sched.h"
+#include "compat_slab.h"
+#include "compat_uaccess.h"
+#include "compat_version.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9)
+#  error "Linux kernels before 2.6.9 are not supported."
+#endif
+
+#include "vm_basic_types.h"
+#include "vm_device_version.h"
+
+#include "vmware.h"
+#include "driverLog.h"
+#include "pgtbl.h"
+#include "vmci_defs.h"
+#include "vmci_handle_array.h"
+#include "vmci_infrastructure.h"
+#include "vmci_iocontrols.h"
+#include "vmci_version.h"
+#include "vmci_kernel_if.h"
+#include "vmciCommonInt.h"
+#include "vmciContext.h"
+#include "vmciDatagram.h"
+#include "vmciDoorbell.h"
+#include "vmciDriver.h"
+#include "vmciEvent.h"
+#include "vmciKernelAPI.h"
+#include "vmciQueuePair.h"
+#include "vmciResource.h"
+
+#define LGPFX "VMCI: "
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PCI Device interface --
+ *
+ *      Declarations of types and functions related to the VMCI PCI
+ *      device personality.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*
+ * VMCI PCI driver state
+ */
+
+typedef struct vmci_device {
+   compat_mutex_t    lock;
+
+   unsigned int      ioaddr;
+   unsigned int      ioaddr_size;
+   unsigned int      irq;
+   unsigned int      intr_type;
+   Bool              exclusive_vectors;
+   struct msix_entry msix_entries[VMCI_MAX_INTRS];
+
+   Bool              enabled;
+   spinlock_t        dev_spinlock;
+} vmci_device;
+
+static const struct pci_device_id vmci_ids[] = {
+   { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), },
+   { 0 },
+};
+
+static int vmci_probe_device(struct pci_dev *pdev,
+                             const struct pci_device_id *id);
+static void vmci_remove_device(struct pci_dev* pdev);
+
+static struct pci_driver vmci_driver = {
+   .name     = "vmci",
+   .id_table = vmci_ids,
+   .probe = vmci_probe_device,
+   .remove = __devexit_p(vmci_remove_device),
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id,
+                                           struct pt_regs * regs);
+static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id,
+                                            struct pt_regs * regs);
+#else
+static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id);
+static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id);
+#endif
+static void dispatch_datagrams(unsigned long data);
+static void process_bitmap(unsigned long data);
+
+/* MSI-X has performance problems in < 2.6.19 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#  define VMCI_DISABLE_MSIX   0
+#else
+#  define VMCI_DISABLE_MSIX   1
+#endif
+
+static vmci_device vmci_dev;
+static int vmci_disable_msi;
+static int vmci_disable_msix = VMCI_DISABLE_MSIX;
+
+DECLARE_TASKLET(vmci_dg_tasklet, dispatch_datagrams,
+                (unsigned long)&vmci_dev);
+
+DECLARE_TASKLET(vmci_bm_tasklet, process_bitmap,
+                (unsigned long)&vmci_dev);
+
+/*
+ * Allocate a buffer for incoming datagrams globally to avoid repeated
+ * allocation in the interrupt handler's atomic context.
+ */
+
+static uint8 *data_buffer = NULL;
+static uint32 data_buffer_size = VMCI_MAX_DG_SIZE;
+
+/*
+ * If the VMCI hardware supports the notification bitmap, we allocate
+ * and register a page with the device.
+ */
+
+static uint8 *notification_bitmap = NULL;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Host device node interface --
+ *
+ *      Implements VMCI by implementing open/close/ioctl functions
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*
+ * Per-instance host state
+ */
+
+typedef struct VMCILinux {
+   VMCIContext *context;
+   int userVersion;
+   VMCIObjType ctType;
+#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL)
+   compat_mutex_t lock;
+#endif
+} VMCILinux;
+
+/*
+ * Static driver state.
+ */
+
+#define VM_DEVICE_NAME_SIZE 32
+#define LINUXLOG_BUFFER_SIZE  1024
+
+typedef struct VMCILinuxState {
+   int major;
+   int minor;
+   struct miscdevice misc;
+   char deviceName[VM_DEVICE_NAME_SIZE];
+   char buf[LINUXLOG_BUFFER_SIZE];
+} VMCILinuxState;
+
+static struct VMCILinuxState linuxState;
+
+static int VMCISetupNotify(VMCIContext *context, VA notifyUVA);
+static void VMCIUnsetNotifyInt(VMCIContext *context, Bool useLock);
+
+static int LinuxDriver_Open(struct inode *inode, struct file *filp);
+
+static int LinuxDriver_Ioctl(struct inode *inode, struct file *filp,
+                             u_int iocmd, unsigned long ioarg);
+
+#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL)
+static long LinuxDriver_UnlockedIoctl(struct file *filp,
+                                      u_int iocmd, unsigned long ioarg);
+#endif
+
+static int LinuxDriver_Close(struct inode *inode, struct file *filp);
+static unsigned int LinuxDriverPoll(struct file *file, poll_table *wait);
+
+
+#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL)
+#define LinuxDriverLockIoctlPerFD(mutex) compat_mutex_lock(mutex)
+#define LinuxDriverUnlockIoctlPerFD(mutex) compat_mutex_unlock(mutex)
+#else
+#define LinuxDriverLockIoctlPerFD(mutex) do {} while (0)
+#define LinuxDriverUnlockIoctlPerFD(mutex) do {} while (0)
+#endif
+
+static struct file_operations vmuser_fops;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Shared VMCI device definitions --
+ *
+ *      Types and variables shared by both host and guest personality
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool guestDeviceInit;
+static Bool hostDeviceInit;
+
+/*
+ * Until we have a single driver, that uses load or runtime
+ * configuration to enable guest and host personalities directly, we
+ * let the tools flag decide.
+ */
+
+#ifdef VMX86_TOOLS
+static Bool isGuestDriver = TRUE;
+#else
+static Bool isGuestDriver = FALSE;
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Host device support --
+ *
+ *      The following functions implement the support for the VMCI
+ *      host driver.
+ *
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+
+#ifdef VM_X86_64
+#ifndef HAVE_COMPAT_IOCTL
+static int
+LinuxDriver_Ioctl32_Handler(unsigned int fd, unsigned int iocmd,
+                            unsigned long ioarg, struct file * filp)
+{
+   int ret;
+   ret = -ENOTTY;
+   if (filp && filp->f_op && filp->f_op->ioctl == LinuxDriver_Ioctl) {
+      ret = LinuxDriver_Ioctl(filp->f_dentry->d_inode, filp, iocmd, ioarg);
+   }
+   return ret;
+}
+#endif /* !HAVE_COMPAT_IOCTL */
+
+static int
+register_ioctl32_handlers(void)
+{
+#ifndef HAVE_COMPAT_IOCTL
+   {
+      int i;
+
+      for (i = IOCTL_VMCI_FIRST; i < IOCTL_VMCI_LAST; i++) {
+         int retval = register_ioctl32_conversion(i,
+                                                  LinuxDriver_Ioctl32_Handler);
+
+         if (retval) {
+            Warning("Fail to register ioctl32 conversion for cmd %d\n", i);
+            return retval;
+         }
+      }
+
+      for (i = IOCTL_VMCI_FIRST2; i < IOCTL_VMCI_LAST2; i++) {
+         int retval = register_ioctl32_conversion(i,
+                                                  LinuxDriver_Ioctl32_Handler);
+
+         if (retval) {
+            Warning("Fail to register ioctl32 conversion for cmd %d\n", i);
+            return retval;
+         }
+      }
+   }
+#endif /* !HAVE_COMPAT_IOCTL */
+   return 0;
+}
+
+static void
+unregister_ioctl32_handlers(void)
+{
+#ifndef HAVE_COMPAT_IOCTL
+   {
+      int i;
+
+      for (i = IOCTL_VMCI_FIRST; i < IOCTL_VMCI_LAST; i++) {
+         int retval = unregister_ioctl32_conversion(i);
+
+         if (retval) {
+            Warning("Fail to unregister ioctl32 conversion for cmd %d\n", i);
+         }
+      }
+
+      for (i = IOCTL_VMCI_FIRST2; i < IOCTL_VMCI_LAST2; i++) {
+         int retval = unregister_ioctl32_conversion(i);
+
+         if (retval) {
+            Warning("Fail to unregister ioctl32 conversion for cmd %d\n", i);
+         }
+      }
+   }
+#endif /* !HAVE_COMPAT_IOCTL */
+}
+#else /* VM_X86_64 */
+#define register_ioctl32_handlers() (0)
+#define unregister_ioctl32_handlers() do { } while (0)
+#endif /* VM_X86_64 */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_host_init --
+ *
+ *      Initializes the VMCI host device driver.
+ *
+ * Results:
+ *      0 on success, other error codes on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+vmci_host_init(void)
+{
+   int retval;
+
+   if (VMCI_HostInit() < VMCI_SUCCESS) {
+      return -ENOMEM;
+   }
+
+   /*
+    * Initialize the file_operations structure. Because this code is always
+    * compiled as a module, this is fine to do it here and not in a static
+    * initializer.
+    */
+
+   memset(&vmuser_fops, 0, sizeof vmuser_fops);
+   vmuser_fops.owner = THIS_MODULE;
+   vmuser_fops.poll = LinuxDriverPoll;
+#ifdef HAVE_UNLOCKED_IOCTL
+   vmuser_fops.unlocked_ioctl = LinuxDriver_UnlockedIoctl;
+#else
+   vmuser_fops.ioctl = LinuxDriver_Ioctl;
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+   vmuser_fops.compat_ioctl = LinuxDriver_UnlockedIoctl;
+#endif
+   vmuser_fops.open = LinuxDriver_Open;
+   vmuser_fops.release = LinuxDriver_Close;
+
+   sprintf(linuxState.deviceName, "vmci");
+   linuxState.major = 10;
+   linuxState.misc.minor = MISC_DYNAMIC_MINOR;
+   linuxState.misc.name = linuxState.deviceName;
+   linuxState.misc.fops = &vmuser_fops;
+
+   retval = misc_register(&linuxState.misc);
+
+   if (retval) {
+      Warning("Module %s: error %u registering with major=%d minor=%d\n",
+             linuxState.deviceName, -retval, linuxState.major, linuxState.minor);
+      goto exit;
+   }
+   linuxState.minor = linuxState.misc.minor;
+   Log("Module %s: registered with major=%d minor=%d\n",
+       linuxState.deviceName, linuxState.major, linuxState.minor);
+
+   retval = register_ioctl32_handlers();
+   if (retval) {
+      misc_deregister(&linuxState.misc);
+   }
+
+exit:
+   if (retval) {
+      VMCI_HostCleanup();
+   }
+   return retval;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinuxDriver_Open  --
+ *
+ *      called on open of /dev/vmci. Use count used
+ *      to determine eventual deallocation of the module
+ *
+ * Side effects:
+ *     Increment use count used to determine eventual deallocation of
+ *     the module
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+LinuxDriver_Open(struct inode *inode, // IN
+                 struct file *filp)   // IN
+{
+   VMCILinux *vmciLinux;
+
+   vmciLinux = kmalloc(sizeof *vmciLinux, GFP_KERNEL);
+   if (vmciLinux == NULL) {
+      return -ENOMEM;
+   }
+   memset(vmciLinux, 0, sizeof *vmciLinux);
+   vmciLinux->ctType = VMCIOBJ_NOT_SET;
+   vmciLinux->userVersion = 0;
+#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL)
+   compat_mutex_init(&vmciLinux->lock);
+#endif
+
+   filp->private_data = vmciLinux;
+
+   return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinuxDriver_Close  --
+ *
+ *      called on close of /dev/vmci, most often when the
+ *      process exits. Decrement use count, allowing for possible uninstalling
+ *      of the module.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+LinuxDriver_Close(struct inode *inode, // IN
+                  struct file *filp)   // IN
+{
+   VMCILinux *vmciLinux;
+
+   vmciLinux = (VMCILinux *)filp->private_data;
+   ASSERT(vmciLinux);
+
+   if (vmciLinux->ctType == VMCIOBJ_CONTEXT) {
+      VMCIId cid;
+
+      ASSERT(vmciLinux->context);
+      cid = VMCIContext_GetId(vmciLinux->context);
+
+      VMCIContext_ReleaseContext(vmciLinux->context);
+      vmciLinux->context = NULL;
+   }
+   vmciLinux->ctType = VMCIOBJ_NOT_SET;
+
+   kfree(vmciLinux);
+   filp->private_data = NULL;
+   return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinuxDriverPoll  --
+ *
+ *      This is used to wake up the VMX when a VMCI call arrives, or
+ *      to wake up select() or poll() at the next clock tick.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static unsigned int
+LinuxDriverPoll(struct file *filp,
+               poll_table *wait)
+{
+   VMCILockFlags flags;
+   VMCILinux *vmciLinux = (VMCILinux *) filp->private_data;
+   unsigned int mask = 0;
+
+   if (vmciLinux->ctType == VMCIOBJ_CONTEXT) {
+      ASSERT(vmciLinux->context != NULL);
+      /*
+       * Check for VMCI calls to this VM context.
+       */
+
+      if (wait != NULL) {
+         poll_wait(filp, &vmciLinux->context->hostContext.waitQueue, wait);
+      }
+
+      VMCI_GrabLock(&vmciLinux->context->lock, &flags);
+      if (vmciLinux->context->pendingDatagrams > 0 ||
+          VMCIHandleArray_GetSize(vmciLinux->context->pendingDoorbellArray) > 0) {
+         mask = POLLIN;
+      }
+      VMCI_ReleaseLock(&vmciLinux->context->lock, flags);
+   }
+   return mask;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCICopyHandleArrayToUser  --
+ *
+ *      Copies the handles of a handle array into a user buffer, and
+ *      returns the new length in userBufferSize. If the copy to the
+ *      user buffer fails, the functions still returns VMCI_SUCCESS,
+ *      but retval != 0.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+VMCICopyHandleArrayToUser(void *userBufUVA,             // IN
+                          uint64 *userBufSize,          // IN/OUT
+                          VMCIHandleArray *handleArray, // IN
+                          int *retval)                  // IN
+{
+   uint32 arraySize;
+   VMCIHandle *handles;
+
+   if (handleArray) {
+      arraySize = VMCIHandleArray_GetSize(handleArray);
+   } else {
+      arraySize = 0;
+   }
+
+   if (arraySize * sizeof *handles > *userBufSize) {
+      return VMCI_ERROR_MORE_DATA;
+   }
+
+   *userBufSize = arraySize * sizeof *handles;
+   if (*userBufSize) {
+      *retval = copy_to_user(userBufUVA,
+                             VMCIHandleArray_GetHandles(handleArray),
+                             *userBufSize);
+   }
+
+   return VMCI_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * LinuxDriver_Ioctl --
+ *
+ *      Main path for UserRPC
+ *
+ * Results:
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+LinuxDriver_Ioctl(struct inode *inode,
+                  struct file *filp,
+                  u_int iocmd,
+                  unsigned long ioarg)
+{
+   VMCILinux *vmciLinux = (VMCILinux *) filp->private_data;
+   int retval = 0;
+
+   switch (iocmd) {
+   case IOCTL_VMCI_VERSION2: {
+      int verFromUser;
+
+      if (copy_from_user(&verFromUser, (void *)ioarg, sizeof verFromUser)) {
+         retval = -EFAULT;
+         break;
+      }
+
+      vmciLinux->userVersion = verFromUser;
+   }
+      /* Fall through. */
+   case IOCTL_VMCI_VERSION:
+      /*
+       * The basic logic here is:
+       *
+       * If the user sends in a version of 0 tell it our version.
+       * If the user didn't send in a version, tell it our version.
+       * If the user sent in an old version, tell it -its- version.
+       * If the user sent in an newer version, tell it our version.
+       *
+       * The rationale behind telling the caller its version is that
+       * Workstation 6.5 required that VMX and VMCI kernel module were
+       * version sync'd.  All new VMX users will be programmed to
+       * handle the VMCI kernel module version.
+       */
+
+      if (vmciLinux->userVersion > 0 &&
+          vmciLinux->userVersion < VMCI_VERSION_HOSTQP) {
+         retval = vmciLinux->userVersion;
+      } else {
+         retval = VMCI_VERSION;
+      }
+      break;
+
+   case IOCTL_VMCI_INIT_CONTEXT: {
+      VMCIInitBlock initBlock;
+      VMCIHostUser user;
+
+      retval = copy_from_user(&initBlock, (void *)ioarg, sizeof initBlock);
+      if (retval != 0) {
+         Log("VMCI: Error reading init block.\n");
+         retval = -EFAULT;
+         break;
+      }
+
+      LinuxDriverLockIoctlPerFD(&vmciLinux->lock);
+      if (vmciLinux->ctType != VMCIOBJ_NOT_SET) {
+         Log("VMCI: Received VMCI init on initialized handle\n");
+         retval = -EINVAL;
+         goto init_release;
+      }
+
+      if (initBlock.flags & ~VMCI_PRIVILEGE_FLAG_RESTRICTED) {
+         Log("VMCI: Unsupported VMCI restriction flag.\n");
+         retval = -EINVAL;
+         goto init_release;
+      }
+
+      user = current_uid();
+      retval = VMCIContext_InitContext(initBlock.cid, initBlock.flags,
+                                       0 /* Unused */, vmciLinux->userVersion,
+                                       &user, &vmciLinux->context);
+      if (retval < VMCI_SUCCESS) {
+         Log("VMCI: Error initializing context.\n");
+         retval = retval == VMCI_ERROR_DUPLICATE_ENTRY ? -EEXIST : -EINVAL;
+         goto init_release;
+      }
+
+      /*
+       * Copy cid to userlevel, we do this to allow the VMX to enforce its
+       * policy on cid generation.
+       */
+      initBlock.cid = VMCIContext_GetId(vmciLinux->context);
+      retval = copy_to_user((void *)ioarg, &initBlock, sizeof initBlock);
+      if (retval != 0) {
+        VMCIContext_ReleaseContext(vmciLinux->context);
+        vmciLinux->context = NULL;
+        Log("VMCI: Error writing init block.\n");
+        retval = -EFAULT;
+        goto init_release;
+      }
+      ASSERT(initBlock.cid != VMCI_INVALID_ID);
+
+      vmciLinux->ctType = VMCIOBJ_CONTEXT;
+
+     init_release:
+      LinuxDriverUnlockIoctlPerFD(&vmciLinux->lock);
+      break;
+   }
+
+   case IOCTL_VMCI_DATAGRAM_SEND: {
+      VMCIDatagramSendRecvInfo sendInfo;
+      VMCIDatagram *dg = NULL;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Warning("VMCI: Ioctl %d only valid for context handle.\n", iocmd);
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&sendInfo, (void *) ioarg, sizeof sendInfo);
+      if (retval) {
+         Warning("VMCI: copy_from_user failed.\n");
+         retval = -EFAULT;
+         break;
+      }
+
+      if (sendInfo.len > VMCI_MAX_DG_SIZE) {
+         Warning("VMCI: datagram size too big.\n");
+        retval = -EINVAL;
+        break;
+      }
+
+      if (sendInfo.len < sizeof *dg) {
+         Warning("VMCI: datagram size too small.\n");
+        retval = -EINVAL;
+        break;
+      }
+
+      dg = VMCI_AllocKernelMem(sendInfo.len, VMCI_MEMORY_NORMAL);
+      if (dg == NULL) {
+         Log("VMCI: Cannot allocate memory to dispatch datagram.\n");
+         retval = -ENOMEM;
+         break;
+      }
+
+      retval = copy_from_user(dg, (char *)(VA)sendInfo.addr, sendInfo.len);
+      if (retval != 0) {
+         Log("VMCI: Error getting datagram: %d\n", retval);
+         VMCI_FreeKernelMem(dg, sendInfo.len);
+         retval = -EFAULT;
+         break;
+      }
+
+      VMCI_DEBUG_LOG(10, ("VMCI: Datagram dst handle 0x%"FMT64"x, "
+                          "src handle 0x%"FMT64"x, payload size %"FMT64"u.\n",
+                          VMCI_HANDLE_TO_UINT64(dg->dst),
+                          VMCI_HANDLE_TO_UINT64(dg->src), dg->payloadSize));
+
+      /* Get source context id. */
+      ASSERT(vmciLinux->context);
+      cid = VMCIContext_GetId(vmciLinux->context);
+      ASSERT(cid != VMCI_INVALID_ID);
+      sendInfo.result = VMCIDatagram_Dispatch(cid, dg, TRUE);
+      VMCI_FreeKernelMem(dg, sendInfo.len);
+      retval = copy_to_user((void *)ioarg, &sendInfo, sizeof sendInfo);
+      break;
+   }
+
+   case IOCTL_VMCI_DATAGRAM_RECEIVE: {
+      VMCIDatagramSendRecvInfo recvInfo;
+      VMCIDatagram *dg = NULL;
+      size_t size;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Warning("VMCI: Ioctl %d only valid for context handle.\n", iocmd);
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&recvInfo, (void *) ioarg, sizeof recvInfo);
+      if (retval) {
+         Warning("VMCI: copy_from_user failed.\n");
+         retval = -EFAULT;
+         break;
+      }
+
+      ASSERT(vmciLinux->ctType == VMCIOBJ_CONTEXT);
+
+      size = recvInfo.len;
+      ASSERT(vmciLinux->context);
+      recvInfo.result = VMCIContext_DequeueDatagram(vmciLinux->context,
+                                                    &size, &dg);
+
+      if (recvInfo.result >= VMCI_SUCCESS) {
+        ASSERT(dg);
+        retval = copy_to_user((void *) ((uintptr_t) recvInfo.addr), dg,
+                              VMCI_DG_SIZE(dg));
+        VMCI_FreeKernelMem(dg, VMCI_DG_SIZE(dg));
+        if (retval != 0) {
+           break;
+        }
+      }
+      retval = copy_to_user((void *)ioarg, &recvInfo, sizeof recvInfo);
+      break;
+   }
+
+   case IOCTL_VMCI_QUEUEPAIR_ALLOC: {
+      VMCIQueuePairAllocInfo queuePairAllocInfo;
+      VMCIQueuePairAllocInfo *info = (VMCIQueuePairAllocInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_QUEUEPAIR_ALLOC only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&queuePairAllocInfo, (void *)ioarg,
+                              sizeof queuePairAllocInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      VMCIQPBroker_Lock();
+
+      {
+        QueuePairPageStore pageStore = { TRUE,
+                                         queuePairAllocInfo.producePageFile,
+                                         queuePairAllocInfo.consumePageFile,
+                                         queuePairAllocInfo.producePageFileSize,
+                                         queuePairAllocInfo.consumePageFileSize,
+                                         0,
+                                         0 };
+
+        result = VMCIQPBroker_Alloc(queuePairAllocInfo.handle,
+                                     queuePairAllocInfo.peer,
+                                     queuePairAllocInfo.flags,
+                                     VMCI_NO_PRIVILEGE_FLAGS,
+                                     queuePairAllocInfo.produceSize,
+                                     queuePairAllocInfo.consumeSize,
+                                     &pageStore,
+                                     vmciLinux->context);
+      }
+      Log("VMCI: IOCTL_VMCI_QUEUEPAIR_ALLOC cid = %u result = %d.\n", cid,
+          result);
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         retval = -EFAULT;
+         if (result >= VMCI_SUCCESS) {
+            result = VMCIQPBroker_Detach(queuePairAllocInfo.handle,
+                                         vmciLinux->context, TRUE);
+            ASSERT(result >= VMCI_SUCCESS);
+         }
+      }
+
+      VMCIQPBroker_Unlock();
+      break;
+   }
+
+   case IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE: {
+      VMCIQueuePairPageFileInfo pageFileInfo;
+      VMCIQueuePairPageFileInfo *info = (VMCIQueuePairPageFileInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+      Bool useUVA;
+      int size;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE only valid for "
+             "contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      if (VMCIContext_SupportsHostQP(vmciLinux->context)) {
+         useUVA = TRUE;
+         size = sizeof *info;
+      } else {
+         /*
+          * An older VMX version won't supply the UVA of the page
+          * files backing the queue pair contents (and headers)
+          */
+
+         useUVA = FALSE;
+         size = sizeof(VMCIQueuePairPageFileInfo_NoHostQP);
+      }
+
+      retval = copy_from_user(&pageFileInfo, (void *)ioarg, size);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      /*
+       * Communicate success pre-emptively to the caller.  Note that
+       * the basic premise is that it is incumbent upon the caller not
+       * to look at the info.result field until after the ioctl()
+       * returns.  And then, only if the ioctl() result indicates no
+       * error.  We send up the SUCCESS status before calling
+       * SetPageStore() store because failing to copy up the result
+       * code means unwinding the SetPageStore().
+       *
+       * It turns out the logic to unwind a SetPageStore() opens a can
+       * of worms.  For example, if a host had created the QueuePair
+       * and a guest attaches and SetPageStore() is successful but
+       * writing success fails, then ... the host has to be stopped
+       * from writing (anymore) data into the QueuePair.  That means
+       * an additional test in the VMCI_Enqueue() code path.  Ugh.
+       */
+
+      result = VMCI_SUCCESS;
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval == 0) {
+         cid = VMCIContext_GetId(vmciLinux->context);
+         VMCIQPBroker_Lock();
+
+         {
+            QueuePairPageStore pageStore = { TRUE,
+                                             pageFileInfo.producePageFile,
+                                             pageFileInfo.consumePageFile,
+                                             pageFileInfo.producePageFileSize,
+                                             pageFileInfo.consumePageFileSize,
+                                             useUVA ? pageFileInfo.produceVA : 0,
+                                             useUVA ? pageFileInfo.consumeVA : 0 };
+
+            result = VMCIQPBroker_SetPageStore(pageFileInfo.handle,
+                                               &pageStore,
+                                               vmciLinux->context);
+         }
+         VMCIQPBroker_Unlock();
+
+         if (result < VMCI_SUCCESS) {
+            Log("VMCI: IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE cid = %u result = %d.\n",
+                cid, result);
+
+            retval = copy_to_user(&info->result, &result, sizeof result);
+            if (retval != 0) {
+               /*
+                * Note that in this case the SetPageStore() call
+                * failed but we were unable to communicate that to the
+                * caller (because the copy_to_user() call failed).
+                * So, if we simply return an error (in this case
+                * -EFAULT) then the caller will know that the
+                * SetPageStore failed even though we couldn't put the
+                * result code in the result field and indicate exactly
+                * why it failed.
+                *
+                * That says nothing about the issue where we were once
+                * able to write to the caller's info memory and now
+                * can't.  Something more serious is probably going on
+                * than the fact that SetPageStore() didn't work.
+                */
+               retval = -EFAULT;
+            }
+         }
+
+      } else {
+         /*
+          * In this case, we can't write a result field of the
+          * caller's info block.  So, we don't even try to
+          * SetPageStore().
+          */
+         retval = -EFAULT;
+      }
+
+      break;
+   }
+
+   case IOCTL_VMCI_QUEUEPAIR_DETACH: {
+      VMCIQueuePairDetachInfo detachInfo;
+      VMCIQueuePairDetachInfo *info = (VMCIQueuePairDetachInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_QUEUEPAIR_DETACH only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&detachInfo, (void *)ioarg, sizeof detachInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      VMCIQPBroker_Lock();
+      result = VMCIQPBroker_Detach(detachInfo.handle, vmciLinux->context,
+                                FALSE); /* Probe detach operation. */
+      Log("VMCI: IOCTL_VMCI_QUEUEPAIR_DETACH cid = %u result = %d.\n",
+          cid, result);
+
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         /* Could not copy to userland, don't perform the actual detach. */
+         retval = -EFAULT;
+      } else {
+         if (result >= VMCI_SUCCESS) {
+            /* Now perform the actual detach. */
+            int32 result2 = VMCIQPBroker_Detach(detachInfo.handle,
+                                             vmciLinux->context, TRUE);
+            if (UNLIKELY(result != result2)) {
+               /*
+                * This should never happen.  But it's better to log a warning
+                * than to crash the host.
+                */
+               Warning("VMCIQPBroker_Detach returned different results:  "
+                       "previous = %d, current = %d.\n", result, result2);
+            }
+         }
+      }
+
+      VMCIQPBroker_Unlock();
+      break;
+   }
+
+   case IOCTL_VMCI_DATAGRAM_REQUEST_MAP: {
+      VMCIDatagramMapInfo mapInfo;
+      VMCIDatagramMapInfo *info = (VMCIDatagramMapInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_REQUEST_MAP only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&mapInfo, (void *)ioarg, sizeof mapInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      result = VMCIDatagramRequestWellKnownMap(mapInfo.wellKnownID, cid,
+                                              VMCIContext_GetPrivFlags(cid));
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_DATAGRAM_REMOVE_MAP: {
+      VMCIDatagramMapInfo mapInfo;
+      VMCIDatagramMapInfo *info = (VMCIDatagramMapInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_REMOVE_MAP only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&mapInfo, (void *)ioarg, sizeof mapInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      result = VMCIDatagramRemoveWellKnownMap(mapInfo.wellKnownID, cid);
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_CTX_ADD_NOTIFICATION: {
+      VMCINotifyAddRemoveInfo arInfo;
+      VMCINotifyAddRemoveInfo *info = (VMCINotifyAddRemoveInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_CTX_ADD_NOTIFICATION only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&arInfo, (void *)ioarg, sizeof arInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      result = VMCIContext_AddNotification(cid, arInfo.remoteCID);
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_CTX_REMOVE_NOTIFICATION: {
+      VMCINotifyAddRemoveInfo arInfo;
+      VMCINotifyAddRemoveInfo *info = (VMCINotifyAddRemoveInfo *)ioarg;
+      int32 result;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_CTX_REMOVE_NOTIFICATION only valid for "
+            "contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&arInfo, (void *)ioarg, sizeof arInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      result = VMCIContext_RemoveNotification(cid, arInfo.remoteCID);
+      retval = copy_to_user(&info->result, &result, sizeof result);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_CTX_GET_CPT_STATE: {
+      VMCICptBufInfo getInfo;
+      VMCIId cid;
+      char *cptBuf;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_CTX_GET_CPT_STATE only valid for "
+            "contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&getInfo, (void *)ioarg, sizeof getInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      getInfo.result = VMCIContext_GetCheckpointState(cid, getInfo.cptType,
+                                                     &getInfo.bufSize,
+                                                     &cptBuf);
+      if (getInfo.result == VMCI_SUCCESS && getInfo.bufSize) {
+        retval = copy_to_user((void *)(VA)getInfo.cptBuf, cptBuf,
+                              getInfo.bufSize);
+        VMCI_FreeKernelMem(cptBuf, getInfo.bufSize);
+        if (retval) {
+           retval = -EFAULT;
+           break;
+        }
+      }
+      retval = copy_to_user((void *)ioarg, &getInfo, sizeof getInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_CTX_SET_CPT_STATE: {
+      VMCICptBufInfo setInfo;
+      VMCIId cid;
+      char *cptBuf;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_CTX_SET_CPT_STATE only valid for "
+            "contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&setInfo, (void *)ioarg, sizeof setInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cptBuf = VMCI_AllocKernelMem(setInfo.bufSize, VMCI_MEMORY_NORMAL);
+      if (cptBuf == NULL) {
+         Log("VMCI: Cannot allocate memory to set cpt state of type %d.\n",
+            setInfo.cptType);
+         retval = -ENOMEM;
+         break;
+      }
+      retval = copy_from_user(cptBuf, (void *)(VA)setInfo.cptBuf,
+                             setInfo.bufSize);
+      if (retval) {
+        VMCI_FreeKernelMem(cptBuf, setInfo.bufSize);
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      setInfo.result = VMCIContext_SetCheckpointState(cid, setInfo.cptType,
+                                                     setInfo.bufSize, cptBuf);
+      VMCI_FreeKernelMem(cptBuf, setInfo.bufSize);
+      retval = copy_to_user((void *)ioarg, &setInfo, sizeof setInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+      break;
+   }
+
+   case IOCTL_VMCI_GET_CONTEXT_ID: {
+      VMCIId cid = VMCI_HOST_CONTEXT_ID;
+
+      retval = copy_to_user((void *)ioarg, &cid, sizeof cid);
+      break;
+   }
+
+   case IOCTL_VMCI_SET_NOTIFY: {
+      VMCISetNotifyInfo notifyInfo;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_SET_NOTIFY only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&notifyInfo, (void *)ioarg, sizeof notifyInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      if ((VA)notifyInfo.notifyUVA != (VA)NULL) {
+         notifyInfo.result = VMCISetupNotify(vmciLinux->context,
+                                             (VA)notifyInfo.notifyUVA);
+      } else {
+         VMCIUnsetNotifyInt(vmciLinux->context, TRUE);
+         notifyInfo.result = VMCI_SUCCESS;
+      }
+
+      retval = copy_to_user((void *)ioarg, &notifyInfo, sizeof notifyInfo);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      break;
+   }
+
+   case IOCTL_VMCI_NOTIFY_RESOURCE: {
+      VMCINotifyResourceInfo info;
+      VMCIId cid;
+
+      if (vmciLinux->userVersion < VMCI_VERSION_NOTIFY) {
+         Log("VMCI: IOCTL_VMCI_NOTIFY_RESOURCE is invalid for current"
+             " VMX versions.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_NOTIFY_RESOURCE is only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&info, (void *)ioarg, sizeof info);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      switch (info.action) {
+      case VMCI_NOTIFY_RESOURCE_ACTION_NOTIFY:
+         if (info.resource == VMCI_NOTIFY_RESOURCE_DOOR_BELL) {
+            info.result = VMCIContext_NotifyDoorbell(cid, info.handle,
+                                                     VMCI_NO_PRIVILEGE_FLAGS);
+         } else {
+            info.result = VMCI_ERROR_UNAVAILABLE;
+         }
+         break;
+      case VMCI_NOTIFY_RESOURCE_ACTION_CREATE:
+         info.result = VMCIContext_DoorbellCreate(cid, info.handle);
+         break;
+      case VMCI_NOTIFY_RESOURCE_ACTION_DESTROY:
+         info.result = VMCIContext_DoorbellDestroy(cid, info.handle);
+         break;
+      default:
+         Log("VMCI: IOCTL_VMCI_NOTIFY_RESOURCE got unknown action %d.\n",
+             info.action);
+         info.result = VMCI_ERROR_INVALID_ARGS;
+      }
+      retval = copy_to_user((void *)ioarg, &info,
+                            sizeof info);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      break;
+   }
+
+   case IOCTL_VMCI_NOTIFICATIONS_RECEIVE: {
+      VMCINotificationReceiveInfo info;
+      VMCIHandleArray *dbHandleArray;
+      VMCIHandleArray *qpHandleArray;
+      VMCIId cid;
+
+      if (vmciLinux->ctType != VMCIOBJ_CONTEXT) {
+         Log("VMCI: IOCTL_VMCI_NOTIFICATIONS_RECEIVE is only valid for contexts.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      if (vmciLinux->userVersion < VMCI_VERSION_NOTIFY) {
+         Log("VMCI: IOCTL_VMCI_NOTIFICATIONS_RECEIVE is not supported for the"
+             " current vmx version.\n");
+         retval = -EINVAL;
+         break;
+      }
+
+      retval = copy_from_user(&info, (void *)ioarg, sizeof info);
+      if (retval) {
+         retval = -EFAULT;
+         break;
+      }
+
+      if ((info.dbHandleBufSize && !info.dbHandleBufUVA) ||
+          (info.qpHandleBufSize && !info.qpHandleBufUVA)) {
+         retval = -EINVAL;
+         break;
+      }
+
+      cid = VMCIContext_GetId(vmciLinux->context);
+      info.result = VMCIContext_ReceiveNotificationsGet(cid,
+                                                        &dbHandleArray,
+                                                        &qpHandleArray);
+      if (info.result == VMCI_SUCCESS) {
+         info.result =
+            VMCICopyHandleArrayToUser((void *)(VA)info.dbHandleBufUVA,
+                                      &info.dbHandleBufSize,
+                                      dbHandleArray,
+                                      &retval);
+         if (info.result == VMCI_SUCCESS && !retval) {
+            info.result =
+               VMCICopyHandleArrayToUser((void *)(VA)info.qpHandleBufUVA,
+                                         &info.qpHandleBufSize,
+                                         qpHandleArray,
+                                         &retval);
+         }
+         if (!retval) {
+            retval = copy_to_user((void *)ioarg, &info, sizeof info);
+         }
+         VMCIContext_ReceiveNotificationsRelease(cid, dbHandleArray, qpHandleArray,
+                                                 info.result == VMCI_SUCCESS && !retval);
+      } else {
+         retval = copy_to_user((void *)ioarg, &info, sizeof info);
+      }
+      break;
+   }
+
+   default:
+      Warning("Unknown ioctl %d\n", iocmd);
+      retval = -EINVAL;
+   }
+
+   return retval;
+}
+
+
+#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * LinuxDriver_UnlockedIoctl --
+ *
+ *      Wrapper for LinuxDriver_Ioctl supporting the compat_ioctl and
+ *      unlocked_ioctl methods that have signatures different from the
+ *      old ioctl. Used as compat_ioctl method for 32bit apps running
+ *      on 64bit kernel and for unlocked_ioctl on systems supporting
+ *      those.  LinuxDriver_Ioctl may safely be called without holding
+ *      the BKL.
+ *
+ * Results:
+ *      Same as LinuxDriver_Ioctl.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static long
+LinuxDriver_UnlockedIoctl(struct file *filp,
+                          u_int iocmd,
+                          unsigned long ioarg)
+{
+   return LinuxDriver_Ioctl(NULL, filp, iocmd, ioarg);
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIUserVAInvalidPointer --
+ *
+ *      Checks if a given user VA is valid or not.  Copied from
+ *      bora/modules/vmnet/linux/hostif.c:VNetUserIfInvalidPointer().  TODO
+ *      libify the common code.
+ *
+ * Results:
+ *      TRUE iff invalid.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE Bool
+VMCIUserVAInvalidPointer(VA uva,      // IN:
+                         size_t size) // IN:
+{
+   return !access_ok(VERIFY_WRITE, (void *)uva, size);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIUserVALockPage --
+ *
+ *      Lock physical page backing a given user VA.  Copied from
+ *      bora/modules/vmnet/linux/userif.c:UserIfLockPage().  TODO libify the
+ *      common code.
+ *
+ * Results:
+ *      Pointer to struct page on success, NULL otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE struct page *
+VMCIUserVALockPage(VA addr) // IN:
+{
+   struct page *page = NULL;
+   int retval;
+
+   down_read(&current->mm->mmap_sem);
+   retval = get_user_pages(current, current->mm, addr,
+                           1, 1, 0, &page, NULL);
+   up_read(&current->mm->mmap_sem);
+
+   if (retval != 1) {
+      return NULL;
+   }
+
+   return page;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMapBoolPtr --
+ *
+ *      Lock physical page backing a given user VA and maps it to kernel
+ *      address space.  The range of the mapped memory should be within a
+ *      single page otherwise an error is returned.  Copied from
+ *      bora/modules/vmnet/linux/userif.c:VNetUserIfMapUint32Ptr().  TODO
+ *      libify the common code.
+ *
+ * Results:
+ *      0 on success, negative error code otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE int
+VMCIMapBoolPtr(VA notifyUVA,     // IN:
+               struct page **p,  // OUT:
+               Bool **notifyPtr) // OUT:
+{
+   if (VMCIUserVAInvalidPointer(notifyUVA, sizeof **notifyPtr) ||
+       (((notifyUVA + sizeof **notifyPtr - 1) & ~(PAGE_SIZE - 1)) !=
+        (notifyUVA & ~(PAGE_SIZE - 1)))) {
+      return -EINVAL;
+   }
+
+   *p = VMCIUserVALockPage(notifyUVA);
+   if (*p == NULL) {
+      return -EAGAIN;
+   }
+
+   *notifyPtr = (Bool *)((uint8 *)kmap(*p) + (notifyUVA & (PAGE_SIZE - 1)));
+   return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCISetupNotify --
+ *
+ *      Sets up a given context for notify to work.  Calls VMCIMapBoolPtr()
+ *      which maps the notify boolean in user VA in kernel space.
+ *
+ * Results:
+ *      VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+VMCISetupNotify(VMCIContext *context, // IN:
+                VA notifyUVA)         // IN:
+{
+   int retval;
+
+   if (context->notify) {
+      Warning("VMCI:  Notify mechanism is already set up.\n");
+      return VMCI_ERROR_DUPLICATE_ENTRY;
+   }
+
+   retval =
+      VMCIMapBoolPtr(notifyUVA, &context->notifyPage, &context->notify) == 0 ?
+         VMCI_SUCCESS : VMCI_ERROR_GENERIC;
+   if (retval == VMCI_SUCCESS) {
+      VMCIContext_CheckAndSignalNotify(context);
+   }
+
+   return retval;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIUnsetNotifyInt --
+ *
+ *      Internal version of VMCIUnsetNotify, that allows for locking
+ *      the context before unsetting the notify pointer. If useLock is
+ *      TRUE, the context lock is grabbed.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VMCIUnsetNotifyInt(VMCIContext *context, // IN
+                   Bool useLock)         // IN
+{
+   VMCILockFlags flags;
+
+   if (useLock) {
+      VMCI_GrabLock(&context->lock, &flags);
+   }
+
+   if (context->notifyPage) {
+      struct page *notifyPage = context->notifyPage;
+
+      context->notify = NULL;
+      context->notifyPage = NULL;
+
+      if (useLock) {
+         VMCI_ReleaseLock(&context->lock, flags);
+      }
+
+      kunmap(notifyPage);
+      put_page(notifyPage);
+   } else {
+      if (useLock) {
+         VMCI_ReleaseLock(&context->lock, flags);
+      }
+   }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIUnsetNotify --
+ *
+ *      Reverts actions set up by VMCISetupNotify().  Unmaps and unlocks the
+ *      page mapped/locked by VMCISetupNotify().
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIUnsetNotify(VMCIContext *context) // IN:
+{
+   VMCIUnsetNotifyInt(context, FALSE);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * PCI device support --
+ *
+ *      The following functions implement the support for the VMCI
+ *      guest device. This includes initializing the device and
+ *      interrupt handling.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_guest_init --
+ *
+ *      Initializes the VMCI PCI device. The initialization might fail
+ *      if there is no VMCI PCI device.
+ *
+ * Results:
+ *      0 on success, other error codes on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+vmci_guest_init(void)
+{
+   int retval;
+
+   /* Initialize guest device data. */
+   compat_mutex_init(&vmci_dev.lock);
+   vmci_dev.intr_type = VMCI_INTR_TYPE_INTX;
+   vmci_dev.exclusive_vectors = FALSE;
+   spin_lock_init(&vmci_dev.dev_spinlock);
+   vmci_dev.enabled = FALSE;
+
+   data_buffer = vmalloc(data_buffer_size);
+   if (!data_buffer) {
+      return -ENOMEM;
+   }
+
+   /* This should be last to make sure we are done initializing. */
+   retval = pci_register_driver(&vmci_driver);
+   if (retval < 0) {
+      vfree(data_buffer);
+      data_buffer = NULL;
+      return retval;
+   }
+
+   return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_enable_msix --
+ *
+ *      Enable MSI-X.  Try exclusive vectors first, then shared vectors.
+ *
+ * Results:
+ *      0 on success, other error codes on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+vmci_enable_msix(struct pci_dev *pdev) // IN
+{
+   int i;
+   int result;
+
+   for (i = 0; i < VMCI_MAX_INTRS; ++i) {
+      vmci_dev.msix_entries[i].entry = i;
+      vmci_dev.msix_entries[i].vector = i;
+   }
+
+   result = pci_enable_msix(pdev, vmci_dev.msix_entries, VMCI_MAX_INTRS);
+   if (!result) {
+      vmci_dev.exclusive_vectors = TRUE;
+   } else if (result > 0) {
+      result = pci_enable_msix(pdev, vmci_dev.msix_entries, 1);
+   }
+   return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_probe_device --
+ *
+ *      Most of the initialization at module load time is done here.
+ *
+ * Results:
+ *      Returns 0 for success, an error otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int __devinit
+vmci_probe_device(struct pci_dev *pdev,           // IN: vmci PCI device
+                  const struct pci_device_id *id) // IN: matching device ID
+{
+   unsigned int ioaddr;
+   unsigned int ioaddr_size;
+   unsigned int capabilities;
+   int result;
+
+   printk(KERN_INFO "Probing for vmci/PCI.\n");
+
+   result = pci_enable_device(pdev);
+   if (result) {
+      printk(KERN_ERR "Cannot VMCI device %s: error %d\n",
+             pci_name(pdev), result);
+      return result;
+   }
+   pci_set_master(pdev); /* To enable QueuePair functionality. */
+   ioaddr = pci_resource_start(pdev, 0);
+   ioaddr_size = pci_resource_len(pdev, 0);
+
+   /*
+    * Request I/O region with adjusted base address and size. The adjusted
+    * values are needed and used if we release the region in case of failure.
+    */
+
+   if (!compat_request_region(ioaddr, ioaddr_size, "vmci")) {
+      printk(KERN_INFO "vmci: Another driver already loaded "
+                       "for device in slot %s.\n", pci_name(pdev));
+      goto pci_disable;
+   }
+
+   printk(KERN_INFO "Found vmci/PCI at %#x, irq %u.\n", ioaddr, pdev->irq);
+
+   /*
+    * Verify that the VMCI Device supports the capabilities that
+    * we need. If the device is missing capabilities that we would
+    * like to use, check for fallback capabilities and use those
+    * instead (so we can run a new VM on old hosts). Fail the load if
+    * a required capability is missing and there is no fallback.
+    *
+    * Right now, we need datagrams. There are no fallbacks.
+    */
+   capabilities = inl(ioaddr + VMCI_CAPS_ADDR);
+
+   if ((capabilities & VMCI_CAPS_DATAGRAM) == 0) {
+      printk(KERN_ERR "VMCI device does not support datagrams.\n");
+      goto release;
+   }
+
+   /*
+    * If the hardware supports notifications, we will use that as
+    * well.
+    */
+   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
+      capabilities = VMCI_CAPS_DATAGRAM;
+      notification_bitmap = vmalloc(PAGE_SIZE);
+      if (notification_bitmap == NULL) {
+         printk(KERN_ERR "VMCI device unable to allocate notification bitmap.\n");
+      } else {
+         memset(notification_bitmap, 0, PAGE_SIZE);
+         capabilities |= VMCI_CAPS_NOTIFICATIONS;
+      }
+   } else {
+      capabilities = VMCI_CAPS_DATAGRAM;
+   }
+   printk(KERN_INFO "VMCI: using capabilities 0x%x.\n", capabilities);
+
+   /* Let the host know which capabilities we intend to use. */
+   outl(capabilities, ioaddr + VMCI_CAPS_ADDR);
+
+   /* Device struct initialization. */
+   compat_mutex_lock(&vmci_dev.lock);
+   if (vmci_dev.enabled) {
+      printk(KERN_ERR "VMCI device already enabled.\n");
+      goto unlock;
+   }
+
+   vmci_dev.ioaddr = ioaddr;
+   vmci_dev.ioaddr_size = ioaddr_size;
+
+   /*
+    * Register notification bitmap with device if that capability is
+    * used
+    */
+   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
+      unsigned long bitmapPPN;
+      bitmapPPN = page_to_pfn(vmalloc_to_page(notification_bitmap));
+      if (!VMCI_RegisterNotificationBitmap(bitmapPPN)) {
+         printk(KERN_ERR "VMCI device unable to register notification bitmap "
+                "with PPN 0x%x.\n", (uint32)bitmapPPN);
+         goto unlock;
+      }
+   }
+
+   /* Check host capabilities. */
+   if (!VMCI_CheckHostCapabilities()) {
+      goto remove_bitmap;
+   }
+
+   /* Enable device. */
+   vmci_dev.enabled = TRUE;
+   pci_set_drvdata(pdev, &vmci_dev);
+
+   /*
+    * We do global initialization here because we need datagrams
+    * during VMCIUtil_Init, since it registers for VMCI events. If we
+    * ever support more than one VMCI device we will have to create
+    * seperate LateInit/EarlyExit functions that can be used to do
+    * initialization/cleanup that depends on the device being
+    * accessible.  We need to initialize VMCI components before
+    * requesting an irq - the VMCI interrupt handler uses these
+    * components, and it may be invoked once request_irq() has
+    * registered the handler (as the irq line may be shared).
+    */
+   VMCIUtil_Init();
+   VMCIQPGuestEndpoints_Init();
+
+   /*
+    * Enable interrupts.  Try MSI-X first, then MSI, and then fallback on
+    * legacy interrupts.
+    */
+   if (!vmci_disable_msix && !vmci_enable_msix(pdev)) {
+      vmci_dev.intr_type = VMCI_INTR_TYPE_MSIX;
+      vmci_dev.irq = vmci_dev.msix_entries[0].vector;
+   } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) {
+      vmci_dev.intr_type = VMCI_INTR_TYPE_MSI;
+      vmci_dev.irq = pdev->irq;
+   } else {
+      vmci_dev.intr_type = VMCI_INTR_TYPE_INTX;
+      vmci_dev.irq = pdev->irq;
+   }
+
+   /* Request IRQ for legacy or MSI interrupts, or for first MSI-X vector. */
+   result = request_irq(vmci_dev.irq, vmci_interrupt, COMPAT_IRQF_SHARED,
+                        "vmci", &vmci_dev);
+   if (result) {
+      printk(KERN_ERR "vmci: irq %u in use: %d\n", vmci_dev.irq, result);
+      goto components_exit;
+   }
+
+   /*
+    * For MSI-X with exclusive vectors we need to request an interrupt for each
+    * vector so that we get a separate interrupt handler routine.  This allows
+    * us to distinguish between the vectors.
+    */
+
+   if (vmci_dev.exclusive_vectors) {
+      ASSERT(vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX);
+      result = request_irq(vmci_dev.msix_entries[1].vector, vmci_interrupt_bm,
+                           0, "vmci", &vmci_dev);
+      if (result) {
+         printk(KERN_ERR "vmci: irq %u in use: %d\n",
+                vmci_dev.msix_entries[1].vector, result);
+         free_irq(vmci_dev.irq, &vmci_dev);
+         goto components_exit;
+      }
+   }
+
+   printk(KERN_INFO "Registered vmci device.\n");
+
+   compat_mutex_unlock(&vmci_dev.lock);
+
+   /* Enable specific interrupt bits. */
+   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
+      outl(VMCI_IMR_DATAGRAM | VMCI_IMR_NOTIFICATION,
+           vmci_dev.ioaddr + VMCI_IMR_ADDR);
+   } else {
+      outl(VMCI_IMR_DATAGRAM, vmci_dev.ioaddr + VMCI_IMR_ADDR);
+   }
+
+   /* Enable interrupts. */
+   outl(VMCI_CONTROL_INT_ENABLE, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
+
+   return 0;
+
+ components_exit:
+   VMCIQPGuestEndpoints_Exit();
+   VMCIUtil_Exit();
+   if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX) {
+      pci_disable_msix(pdev);
+   } else if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSI) {
+      pci_disable_msi(pdev);
+   }
+ remove_bitmap:
+   if (notification_bitmap) {
+      outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
+   }
+ unlock:
+   compat_mutex_unlock(&vmci_dev.lock);
+ release:
+   if (notification_bitmap) {
+      vfree(notification_bitmap);
+      notification_bitmap = NULL;
+   }
+   release_region(ioaddr, ioaddr_size);
+ pci_disable:
+   pci_disable_device(pdev);
+   return -EBUSY;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_remove_device --
+ *
+ *      Cleanup, called for each device on unload.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void __devexit
+vmci_remove_device(struct pci_dev* pdev)
+{
+   struct vmci_device *dev = pci_get_drvdata(pdev);
+
+   printk(KERN_INFO "Removing vmci device\n");
+
+   VMCIQPGuestEndpoints_Exit();
+   VMCIUtil_Exit();
+
+   compat_mutex_lock(&dev->lock);
+   printk(KERN_INFO "Resetting vmci device\n");
+   outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
+
+   /*
+    * Free IRQ and then disable MSI/MSI-X as appropriate.  For MSI-X, we might
+    * have multiple vectors, each with their own IRQ, which we must free too.
+    */
+
+   free_irq(dev->irq, dev);
+   if (dev->intr_type == VMCI_INTR_TYPE_MSIX) {
+      if (dev->exclusive_vectors) {
+         free_irq(dev->msix_entries[1].vector, dev);
+      }
+      pci_disable_msix(pdev);
+   } else if (dev->intr_type == VMCI_INTR_TYPE_MSI) {
+      pci_disable_msi(pdev);
+   }
+   dev->exclusive_vectors = FALSE;
+   dev->intr_type = VMCI_INTR_TYPE_INTX;
+
+   release_region(dev->ioaddr, dev->ioaddr_size);
+   dev->enabled = FALSE;
+   if (notification_bitmap) {
+      /*
+       * The device reset above cleared the bitmap state of the
+       * device, so we can safely free it here.
+       */
+
+      vfree(notification_bitmap);
+      notification_bitmap = NULL;
+   }
+
+   printk(KERN_INFO "Unregistered vmci device.\n");
+   compat_mutex_unlock(&dev->lock);
+
+   pci_disable_device(pdev);
+}
+
+
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_interrupt --
+ *
+ *      Interrupt handler for legacy or MSI interrupt, or for first MSI-X
+ *      interrupt (vector VMCI_INTR_DATAGRAM).
+ *
+ * Results:
+ *      COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if
+ *      not an interrupt.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static compat_irqreturn_t
+vmci_interrupt(int irq,               // IN
+               void *clientdata,      // IN
+               struct pt_regs *regs)  // IN
+#else
+static compat_irqreturn_t
+vmci_interrupt(int irq,               // IN
+               void *clientdata)      // IN
+#endif
+{
+   vmci_device *dev = clientdata;
+
+   if (dev == NULL) {
+      printk(KERN_DEBUG "vmci_interrupt(): irq %d for unknown device.\n", irq);
+      return COMPAT_IRQ_NONE;
+   }
+
+   /*
+    * If we are using MSI-X with exclusive vectors then we simply schedule
+    * the datagram tasklet, since we know the interrupt was meant for us.
+    * Otherwise we must read the ICR to determine what to do.
+    */
+
+   if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) {
+      tasklet_schedule(&vmci_dg_tasklet);
+   } else {
+      unsigned int icr;
+
+      ASSERT(dev->intr_type == VMCI_INTR_TYPE_INTX ||
+             dev->intr_type == VMCI_INTR_TYPE_MSI);
+
+      /* Acknowledge interrupt and determine what needs doing. */
+      icr = inl(dev->ioaddr + VMCI_ICR_ADDR);
+      if (icr == 0 || icr == 0xffffffff) {
+         return COMPAT_IRQ_NONE;
+      }
+
+      if (icr & VMCI_ICR_DATAGRAM) {
+         tasklet_schedule(&vmci_dg_tasklet);
+         icr &= ~VMCI_ICR_DATAGRAM;
+      }
+      if (icr & VMCI_ICR_NOTIFICATION) {
+         tasklet_schedule(&vmci_bm_tasklet);
+         icr &= ~VMCI_ICR_NOTIFICATION;
+      }
+      if (icr != 0) {
+         printk(KERN_INFO LGPFX"Ignoring unknown interrupt cause (%d).\n", icr);
+      }
+   }
+
+   return COMPAT_IRQ_HANDLED;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * vmci_interrupt_bm --
+ *
+ *      Interrupt handler for MSI-X interrupt vector VMCI_INTR_NOTIFICATION,
+ *      which is for the notification bitmap.  Will only get called if we are
+ *      using MSI-X with exclusive vectors.
+ *
+ * Results:
+ *      COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if
+ *      not an interrupt.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static compat_irqreturn_t
+vmci_interrupt_bm(int irq,               // IN
+                  void *clientdata,      // IN
+                  struct pt_regs *regs)  // IN
+#else
+static compat_irqreturn_t
+vmci_interrupt_bm(int irq,               // IN
+                  void *clientdata)      // IN
+#endif
+{
+   vmci_device *dev = clientdata;
+
+   if (dev == NULL) {
+      printk(KERN_DEBUG "vmci_interrupt_bm(): irq %d for unknown device.\n", irq);
+      return COMPAT_IRQ_NONE;
+   }
+
+   /* For MSI-X we can just assume it was meant for us. */
+   ASSERT(dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors);
+   tasklet_schedule(&vmci_bm_tasklet);
+
+   return COMPAT_IRQ_HANDLED;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCI_DeviceEnabled --
+ *
+ *      Checks whether the VMCI device is enabled.
+ *
+ * Results:
+ *      TRUE if device is enabled, FALSE otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VMCI_DeviceEnabled(void)
+{
+   Bool retval;
+
+   if (!guestDeviceInit) {
+      return FALSE;
+   }
+
+   compat_mutex_lock(&vmci_dev.lock);
+   retval = vmci_dev.enabled;
+   compat_mutex_unlock(&vmci_dev.lock);
+
+   return retval;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCI_SendDatagram --
+ *
+ *      VM to hypervisor call mechanism. We use the standard VMware naming
+ *      convention since shared code is calling this function as well.
+ *
+ * Results:
+ *      The result of the hypercall.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMCI_SendDatagram(VMCIDatagram *dg)
+{
+   unsigned long flags;
+   int result;
+
+   if (!isGuestDriver) {
+      return VMCI_ERROR_UNAVAILABLE;
+   }
+
+   /* Check args. */
+   if (dg == NULL) {
+      return VMCI_ERROR_INVALID_ARGS;
+   }
+
+   /*
+    * Need to acquire spinlock on the device because
+    * the datagram data may be spread over multiple pages and the monitor may
+    * interleave device user rpc calls from multiple VCPUs. Acquiring the
+    * spinlock precludes that possibility. Disabling interrupts to avoid
+    * incoming datagrams during a "rep out" and possibly landing up in this
+    * function.
+    */
+   spin_lock_irqsave(&vmci_dev.dev_spinlock, flags);
+
+   /*
+    * Send the datagram and retrieve the return value from the result register.
+    */
+   __asm__ __volatile__(
+      "cld\n\t"
+      "rep outsb\n\t"
+      : /* No output. */
+      : "d"(vmci_dev.ioaddr + VMCI_DATA_OUT_ADDR),
+        "c"(VMCI_DG_SIZE(dg)), "S"(dg)
+      );
+
+   /*
+    * XXX Should read result high port as well when updating handlers to
+    * return 64bit.
+    */
+   result = inl(vmci_dev.ioaddr + VMCI_RESULT_LOW_ADDR);
+   spin_unlock_irqrestore(&vmci_dev.dev_spinlock, flags);
+
+   return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * dispatch_datagrams --
+ *
+ *      Reads and dispatches incoming datagrams.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      Reads data from the device.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+dispatch_datagrams(unsigned long data)
+{
+   vmci_device *dev = (vmci_device *)data;
+
+   if (dev == NULL) {
+      printk(KERN_DEBUG "vmci: dispatch_datagrams(): no vmci device"
+            "present.\n");
+      return;
+   }
+
+   if (data_buffer == NULL) {
+      printk(KERN_DEBUG "vmci: dispatch_datagrams(): no buffer present.\n");
+      return;
+   }
+
+
+   VMCI_ReadDatagramsFromPort((VMCIIoHandle) 0, dev->ioaddr + VMCI_DATA_IN_ADDR,
+                             data_buffer, data_buffer_size);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * process_bitmap --
+ *
+ *      Scans the notification bitmap for raised flags, clears them
+ *      and handles the notifications.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+process_bitmap(unsigned long data)
+{
+   vmci_device *dev = (vmci_device *)data;
+
+   if (dev == NULL) {
+      printk(KERN_DEBUG "vmci: process_bitmaps(): no vmci device"
+            "present.\n");
+      return;
+   }
+
+   if (notification_bitmap == NULL) {
+      printk(KERN_DEBUG "vmci: process_bitmaps(): no bitmap present.\n");
+      return;
+   }
+
+
+   VMCI_ScanNotificationBitmap(notification_bitmap);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Shared functions --
+ *
+ *      Functions shared between host and guest personality.
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCI_HasGuestDevice --
+ *
+ *      Determines whether the VMCI PCI device has been successfully
+ *      initialized.
+ *
+ * Results:
+ *      TRUE, if VMCI guest device is operational, FALSE otherwise.
+ *
+ * Side effects:
+ *      Reads data from the device.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VMCI_HasGuestDevice(void)
+{
+   return VMCI_DeviceEnabled();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCI_HasHostDevice --
+ *
+ *      Determines whether the VMCI host driver has been successfully
+ *      initialized.
+ *
+ * Results:
+ *      TRUE, if VMCI host driver is operational, FALSE otherwise.
+ *
+ * Side effects:
+ *      Reads data from the device.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VMCI_HasHostDevice(void)
+{
+   return hostDeviceInit;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Module definitions --
+ *
+ *      Implements support for module load/unload.
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * vmci_init --
+ *
+ *      linux module entry point. Called by /sbin/insmod command
+ *
+ * Results:
+ *      registers a device driver for a major # that depends
+ *      on the uid. Add yourself to that list.  List is now in
+ *      private/driver-private.c.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int __init
+vmci_init(void)
+{
+   int retval;
+
+   DriverLog_Init("/dev/vmci");
+
+   retval = VMCI_SharedInit();
+   if (retval != VMCI_SUCCESS) {
+      Warning(LGPFX"Failed to initialize VMCI common components (err=%d).\n",
+              retval);
+      return -ENOMEM;
+   }
+
+   if (isGuestDriver) {
+      retval = vmci_guest_init();
+      if (retval != 0) {
+         Warning(LGPFX"VMCI PCI device not initialized (err=%d).\n", retval);
+      }
+      guestDeviceInit = (retval == 0);
+      if (guestDeviceInit) {
+         Log(LGPFX"Using guest personality\n");
+      }
+   } else {
+      retval = vmci_host_init();
+      if (retval != 0) {
+         Warning(LGPFX"Unable to initialize host personality (err=%d).\n", retval);
+      }
+      hostDeviceInit = (retval == 0);
+      if (hostDeviceInit) {
+         Log(LGPFX"Using host personality\n");
+      }
+   }
+
+   if (!guestDeviceInit && !hostDeviceInit) {
+      VMCI_SharedCleanup();
+      return -ENODEV;
+   }
+
+   Log("Module %s: initialized\n", linuxState.deviceName);
+
+   return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * vmci_exit --
+ *
+ *      Called by /sbin/rmmod
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void __exit
+vmci_exit(void)
+{
+   int retval;
+
+   if (guestDeviceInit) {
+      pci_unregister_driver(&vmci_driver);
+      vfree(data_buffer);
+      guestDeviceInit = FALSE;
+   }
+
+   if (hostDeviceInit) {
+      unregister_ioctl32_handlers();
+
+      VMCI_HostCleanup();
+
+      retval = misc_deregister(&linuxState.misc);
+      if (retval) {
+         Warning("Module %s: error unregistering\n", linuxState.deviceName);
+      } else {
+         Log("Module %s: unloaded\n", linuxState.deviceName);
+      }
+
+      hostDeviceInit = FALSE;
+   }
+
+   VMCI_SharedCleanup();
+}
+
+
+module_init(vmci_init);
+module_exit(vmci_exit);
+MODULE_DEVICE_TABLE(pci, vmci_ids);
+
+module_param_named(disable_msi, vmci_disable_msi, bool, 0);
+MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
+
+module_param_named(disable_msix, vmci_disable_msix, bool, 0);
+MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default="
+                __stringify(VMCI_DISABLE_MSIX) ")");
+
+
+MODULE_AUTHOR("VMware, Inc.");
+MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface (VMCI).");
+MODULE_VERSION(VMCI_DRIVER_VERSION_STRING);
+MODULE_LICENSE("GPL v2");
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
similarity index 87%
rename from open-vm-tools/modules/linux/vmci/vmci_version.h
rename to open-vm-tools/modules/linux/vmci/linux/vmci_version.h
index dec595865156b5fab258e94b8c090f455a0d6644..68fac7e46fbe98eabfc9f3873209f2a96f5c5d60 100644 (file)
@@ -25,8 +25,8 @@
 #ifndef _VMCI_VERSION_H_
 #define _VMCI_VERSION_H_
 
-#define VMCI_DRIVER_VERSION          9.1.12.0
-#define VMCI_DRIVER_VERSION_COMMAS   9,1,12,0
-#define VMCI_DRIVER_VERSION_STRING   "9.1.12.0"
+#define VMCI_DRIVER_VERSION          9.1.10.0
+#define VMCI_DRIVER_VERSION_COMMAS   9,1,10,0
+#define VMCI_DRIVER_VERSION_STRING   "9.1.10.0"
 
 #endif /* _VMCI_VERSION_H_ */
diff --git a/open-vm-tools/modules/linux/vmci/vmci_drv.c b/open-vm-tools/modules/linux/vmci/vmci_drv.c
deleted file mode 100644 (file)
index 25cefd9..0000000
+++ /dev/null
@@ -1,1068 +0,0 @@
-/*********************************************************
- * Copyright (C) 2005 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
- *
- *********************************************************/
-
-/*
- * vmci.c --
- *
- *      Linux guest driver for the VMCI device.
- */
-
-#include "driver-config.h"
-
-#include <linux/moduleparam.h>
-#include <linux/poll.h>
-
-#include "compat_init.h"
-#include "compat_interrupt.h"
-#include "compat_ioport.h"
-#include "compat_kernel.h"
-#include "compat_module.h"
-#include "compat_mutex.h"
-#include "compat_page.h"
-#include "compat_pci.h"
-
-#include "driverLog.h"
-
-#include "vm_basic_types.h"
-#include "vm_device_version.h"
-
-#include "vmci_defs.h"
-#include "vmci_infrastructure.h"
-#include "vmci_iocontrols.h"
-#include "vmci_version.h"
-#include "vmciContext.h"
-#include "vmciDatagram.h"
-#include "vmciDoorbell.h"
-#include "vmciDriver.h"
-#include "vmciEvent.h"
-#include "vmciQueuePair.h"
-#include "vmciResource.h"
-
-#define LGPFX "VMCI: "
-#define VMCI_DEVICE_MINOR_NUM 0
-
-/* MSI-X has performance problems in < 2.6.19 */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
-#  define VMCI_DISABLE_MSIX   0
-#else
-#  define VMCI_DISABLE_MSIX   1
-#endif
-
-typedef struct vmci_device {
-   compat_mutex_t    lock;
-
-   unsigned int      ioaddr;
-   unsigned int      ioaddr_size;
-   unsigned int      irq;
-   unsigned int      intr_type;
-   Bool              exclusive_vectors;
-   struct msix_entry msix_entries[VMCI_MAX_INTRS];
-
-   Bool              enabled;
-   spinlock_t        dev_spinlock;
-} vmci_device;
-
-static int vmci_probe_device(struct pci_dev *pdev,
-                             const struct pci_device_id *id);
-static void vmci_remove_device(struct pci_dev* pdev);
-static int vmci_open(struct inode *inode, struct file *file);
-static int vmci_close(struct inode *inode, struct file *file);
-static long vmci_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-#if !defined(HAVE_UNLOCKED_IOCTL)
-static int vmci_legacy_ioctl(struct inode *dummy, struct file *file,
-                             unsigned int cmd, unsigned long arg);
-#endif
-static unsigned int vmci_poll(struct file *file, poll_table *wait);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id,
-                                           struct pt_regs * regs);
-static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id,
-                                            struct pt_regs * regs);
-#else
-static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id);
-static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id);
-#endif
-static void dispatch_datagrams(unsigned long data);
-static void process_bitmap(unsigned long data);
-
-static const struct pci_device_id vmci_ids[] = {
-   { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), },
-   { 0 },
-};
-
-static struct file_operations vmci_ops = {
-   .owner   = THIS_MODULE,
-   .open    = vmci_open,
-   .release = vmci_close,
-#ifdef HAVE_UNLOCKED_IOCTL
-   .unlocked_ioctl = vmci_ioctl,
-#else
-   .ioctl   = vmci_legacy_ioctl,
-#endif
-#ifdef HAVE_COMPAT_IOCTL
-   .compat_ioctl = vmci_ioctl,
-#endif
-   .poll    = vmci_poll,
-};
-
-static struct pci_driver vmci_driver = {
-   .name     = "vmci",
-   .id_table = vmci_ids,
-   .probe = vmci_probe_device,
-   .remove = __devexit_p(vmci_remove_device),
-};
-
-static vmci_device vmci_dev;
-
-/* We dynamically request the device major number at init time. */
-static int device_major_nr = 0;
-
-static int vmci_disable_msi;
-static int vmci_disable_msix = VMCI_DISABLE_MSIX;
-
-DECLARE_TASKLET(vmci_dg_tasklet, dispatch_datagrams,
-                (unsigned long)&vmci_dev);
-
-DECLARE_TASKLET(vmci_bm_tasklet, process_bitmap,
-                (unsigned long)&vmci_dev);
-
-/*
- * Allocate a buffer for incoming datagrams globally to avoid repeated
- * allocation in the interrupt handler's atomic context.
- */
-
-static uint8 *data_buffer = NULL;
-static uint32 data_buffer_size = VMCI_MAX_DG_SIZE;
-
-/*
- * If the VMCI hardware supports the notification bitmap, we allocate
- * and register a page with the device.
- */
-
-static uint8 *notification_bitmap = NULL;
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_init --
- *
- *      Initialization, called by Linux when the module is loaded.
- *
- * Results:
- *      Returns 0 for success, negative errno value otherwise.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int __init
-vmci_init(void)
-{
-   int err;
-
-   DriverLog_Init("/dev/vmci");
-
-   /* Initialize device data. */
-   compat_mutex_init(&vmci_dev.lock);
-   vmci_dev.intr_type = VMCI_INTR_TYPE_INTX;
-   vmci_dev.exclusive_vectors = FALSE;
-   spin_lock_init(&vmci_dev.dev_spinlock);
-   vmci_dev.enabled = FALSE;
-
-   data_buffer = vmalloc(data_buffer_size);
-   if (!data_buffer) {
-      return -ENOMEM;
-   }
-
-   /* Register device node ops. */
-   err = register_chrdev(0, "vmci", &vmci_ops);
-   if (err < 0) {
-      printk(KERN_ERR "Unable to register vmci device\n");
-      goto err_free_mem;
-   }
-
-   device_major_nr = err;
-
-   printk("VMCI: Major device number is: %d\n", device_major_nr);
-
-   /* This should be last to make sure we are done initializing. */
-   err = pci_register_driver(&vmci_driver);
-   if (err < 0) {
-      goto err_unregister_chrdev;
-   }
-
-   return 0;
-
-err_unregister_chrdev:
-   unregister_chrdev(device_major_nr, "vmci");
-err_free_mem:
-   vfree(data_buffer);
-   return err;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_exit --
- *
- *      Cleanup, called by Linux when the module is unloaded.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static void __exit
-vmci_exit(void)
-{
-   pci_unregister_driver(&vmci_driver);
-
-   unregister_chrdev(device_major_nr, "vmci");
-
-   vfree(data_buffer);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_enable_msix --
- *
- *      Enable MSI-X.  Try exclusive vectors first, then shared vectors.
- *
- * Results:
- *      0 on success, other error codes on failure.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int
-vmci_enable_msix(struct pci_dev *pdev) // IN
-{
-   int i;
-   int result;
-
-   for (i = 0; i < VMCI_MAX_INTRS; ++i) {
-      vmci_dev.msix_entries[i].entry = i;
-      vmci_dev.msix_entries[i].vector = i;
-   }
-
-   result = pci_enable_msix(pdev, vmci_dev.msix_entries, VMCI_MAX_INTRS);
-   if (!result) {
-      vmci_dev.exclusive_vectors = TRUE;
-   } else if (result > 0) {
-      result = pci_enable_msix(pdev, vmci_dev.msix_entries, 1);
-   }
-   return result;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_probe_device --
- *
- *      Most of the initialization at module load time is done here.
- *
- * Results:
- *      Returns 0 for success, an error otherwise.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int __devinit
-vmci_probe_device(struct pci_dev *pdev,           // IN: vmci PCI device
-                  const struct pci_device_id *id) // IN: matching device ID
-{
-   unsigned int ioaddr;
-   unsigned int ioaddr_size;
-   unsigned int capabilities;
-   int result;
-
-   printk(KERN_INFO "Probing for vmci/PCI.\n");
-
-   result = pci_enable_device(pdev);
-   if (result) {
-      printk(KERN_ERR "Cannot VMCI device %s: error %d\n",
-             pci_name(pdev), result);
-      return result;
-   }
-   pci_set_master(pdev); /* To enable QueuePair functionality. */
-   ioaddr = pci_resource_start(pdev, 0);
-   ioaddr_size = pci_resource_len(pdev, 0);
-
-   /*
-    * Request I/O region with adjusted base address and size. The adjusted
-    * values are needed and used if we release the region in case of failure.
-    */
-
-   if (!compat_request_region(ioaddr, ioaddr_size, "vmci")) {
-      printk(KERN_INFO "vmci: Another driver already loaded "
-                       "for device in slot %s.\n", pci_name(pdev));
-      goto pci_disable;
-   }
-
-   printk(KERN_INFO "Found vmci/PCI at %#x, irq %u.\n", ioaddr, pdev->irq);
-
-   /*
-    * Verify that the VMCI Device supports the capabilities that
-    * we need. If the device is missing capabilities that we would
-    * like to use, check for fallback capabilities and use those
-    * instead (so we can run a new VM on old hosts). Fail the load if
-    * a required capability is missing and there is no fallback.
-    *
-    * Right now, we need datagrams. There are no fallbacks.
-    */
-   capabilities = inl(ioaddr + VMCI_CAPS_ADDR);
-
-   if ((capabilities & VMCI_CAPS_DATAGRAM) == 0) {
-      printk(KERN_ERR "VMCI device does not support datagrams.\n");
-      goto release;
-   }
-
-   /*
-    * If the hardware supports notifications, we will use that as
-    * well.
-    */
-   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
-      capabilities = VMCI_CAPS_DATAGRAM;
-      notification_bitmap = vmalloc(PAGE_SIZE);
-      if (notification_bitmap == NULL) {
-         printk(KERN_ERR "VMCI device unable to allocate notification bitmap.\n");
-      } else {
-         memset(notification_bitmap, 0, PAGE_SIZE);
-         capabilities |= VMCI_CAPS_NOTIFICATIONS;
-      }
-   } else {
-      capabilities = VMCI_CAPS_DATAGRAM;
-   }
-   printk(KERN_INFO "VMCI: using capabilities 0x%x.\n", capabilities);
-
-   /* Let the host know which capabilities we intend to use. */
-   outl(capabilities, ioaddr + VMCI_CAPS_ADDR);
-
-   /* Device struct initialization. */
-   compat_mutex_lock(&vmci_dev.lock);
-   if (vmci_dev.enabled) {
-      printk(KERN_ERR "VMCI device already enabled.\n");
-      goto unlock;
-   }
-
-   vmci_dev.ioaddr = ioaddr;
-   vmci_dev.ioaddr_size = ioaddr_size;
-
-   /*
-    * Register notification bitmap with device if that capability is
-    * used
-    */
-   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
-      unsigned long bitmapPPN;
-      bitmapPPN = page_to_pfn(vmalloc_to_page(notification_bitmap));
-      if (!VMCI_RegisterNotificationBitmap(bitmapPPN)) {
-         printk(KERN_ERR "VMCI device unable to register notification bitmap "
-                "with PPN 0x%x.\n", (uint32)bitmapPPN);
-         goto unlock;
-      }
-   }
-
-   /* Check host capabilities. */
-   if (!VMCI_CheckHostCapabilities()) {
-      goto remove_bitmap;
-   }
-
-   /* Enable device. */
-   vmci_dev.enabled = TRUE;
-   pci_set_drvdata(pdev, &vmci_dev);
-
-   /*
-    * We do global initialization here because we need datagrams for
-    * event init. If we ever support more than one VMCI device we will
-    * have to create seperate LateInit/EarlyExit functions that can be
-    * used to do initialization/cleanup that depends on the device
-    * being accessible.  We need to initialize VMCI components before
-    * requesting an irq - the VMCI interrupt handler uses these
-    * 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();
-   VMCIDoorbell_Init();
-   VMCIQPGuestEndpoints_Init();
-
-   /*
-    * Enable interrupts.  Try MSI-X first, then MSI, and then fallback on
-    * legacy interrupts.
-    */
-   if (!vmci_disable_msix && !vmci_enable_msix(pdev)) {
-      vmci_dev.intr_type = VMCI_INTR_TYPE_MSIX;
-      vmci_dev.irq = vmci_dev.msix_entries[0].vector;
-   } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) {
-      vmci_dev.intr_type = VMCI_INTR_TYPE_MSI;
-      vmci_dev.irq = pdev->irq;
-   } else {
-      vmci_dev.intr_type = VMCI_INTR_TYPE_INTX;
-      vmci_dev.irq = pdev->irq;
-   }
-
-   /* Request IRQ for legacy or MSI interrupts, or for first MSI-X vector. */
-   result = request_irq(vmci_dev.irq, vmci_interrupt, COMPAT_IRQF_SHARED,
-                        "vmci", &vmci_dev);
-   if (result) {
-      printk(KERN_ERR "vmci: irq %u in use: %d\n", vmci_dev.irq, result);
-      goto components_exit;
-   }
-
-   /*
-    * For MSI-X with exclusive vectors we need to request an interrupt for each
-    * vector so that we get a separate interrupt handler routine.  This allows
-    * us to distinguish between the vectors.
-    */
-
-   if (vmci_dev.exclusive_vectors) {
-      ASSERT(vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX);
-      result = request_irq(vmci_dev.msix_entries[1].vector, vmci_interrupt_bm,
-                           0, "vmci", &vmci_dev);
-      if (result) {
-         printk(KERN_ERR "vmci: irq %u in use: %d\n",
-                vmci_dev.msix_entries[1].vector, result);
-         free_irq(vmci_dev.irq, &vmci_dev);
-         goto components_exit;
-      }
-   }
-
-   printk(KERN_INFO "Registered vmci device.\n");
-
-   compat_mutex_unlock(&vmci_dev.lock);
-
-   /* Enable specific interrupt bits. */
-   if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
-      outl(VMCI_IMR_DATAGRAM | VMCI_IMR_NOTIFICATION,
-           vmci_dev.ioaddr + VMCI_IMR_ADDR);
-   } else {
-      outl(VMCI_IMR_DATAGRAM, vmci_dev.ioaddr + VMCI_IMR_ADDR);
-   }
-
-   /* Enable interrupts. */
-   outl(VMCI_CONTROL_INT_ENABLE, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
-
-   return 0;
-
- components_exit:
-   VMCIQPGuestEndpoints_Exit();
-   VMCIDoorbell_Exit();
-   VMCIUtil_Exit();
-   VMCIEvent_Exit();
-   VMCIDatagram_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) {
-      pci_disable_msi(pdev);
-   }
- remove_bitmap:
-   if (notification_bitmap) {
-      outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
-   }
- unlock:
-   compat_mutex_unlock(&vmci_dev.lock);
- release:
-   if (notification_bitmap) {
-      vfree(notification_bitmap);
-   }
-   release_region(ioaddr, ioaddr_size);
- pci_disable:
-   pci_disable_device(pdev);
-   return -EBUSY;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_remove_device --
- *
- *      Cleanup, called for each device on unload.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static void __devexit
-vmci_remove_device(struct pci_dev* pdev)
-{
-   struct vmci_device *dev = pci_get_drvdata(pdev);
-
-   printk(KERN_INFO "Removing vmci device\n");
-
-   VMCIQPGuestEndpoints_Exit();
-
-   // XXX Todo add exit/cleanup functions for util, sm, dg, and resource apis.
-   VMCIUtil_Exit();
-   VMCIEvent_Exit();
-
-   compat_mutex_lock(&dev->lock);
-   printk(KERN_INFO "Resetting vmci device\n");
-   outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR);
-
-   /*
-    * Free IRQ and then disable MSI/MSI-X as appropriate.  For MSI-X, we might
-    * have multiple vectors, each with their own IRQ, which we must free too.
-    */
-
-   free_irq(dev->irq, dev);
-   if (dev->intr_type == VMCI_INTR_TYPE_MSIX) {
-      if (dev->exclusive_vectors) {
-         free_irq(dev->msix_entries[1].vector, dev);
-      }
-      pci_disable_msix(pdev);
-   } else if (dev->intr_type == VMCI_INTR_TYPE_MSI) {
-      pci_disable_msi(pdev);
-   }
-   dev->exclusive_vectors = FALSE;
-   dev->intr_type = VMCI_INTR_TYPE_INTX;
-
-   release_region(dev->ioaddr, dev->ioaddr_size);
-   dev->enabled = FALSE;
-   VMCIDoorbell_Exit();
-   if (notification_bitmap) {
-      vfree(notification_bitmap);
-   }
-
-   VMCIDatagram_Exit();
-   VMCIContext_Exit();
-   VMCIResource_Exit();
-
-   printk(KERN_INFO "Unregistered vmci device.\n");
-   compat_mutex_unlock(&dev->lock);
-
-   pci_disable_device(pdev);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_open --
- *
- *      Open device.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int
-vmci_open(struct inode *inode,  // IN
-          struct file *file)    // IN
-{
-   VMCIObj *devHndl;
-   int errcode;
-
-   printk(KERN_INFO "Opening vmci device\n");
-
-   if (MINOR(inode->i_rdev) != VMCI_DEVICE_MINOR_NUM) {
-      return -ENODEV;
-   }
-
-   compat_mutex_lock(&vmci_dev.lock);
-   if (!vmci_dev.enabled) {
-      printk(KERN_INFO "Received open on uninitialized vmci device.\n");
-      errcode = -ENODEV;
-      goto unlock;
-   }
-
-   /* Do open ... */
-   devHndl = VMCI_AllocKernelMem(sizeof *devHndl, VMCI_MEMORY_NORMAL);
-   if (!devHndl) {
-      printk(KERN_INFO "Failed to create device obj when opening device.\n");
-      errcode = -ENOMEM;
-      goto unlock;
-   }
-   devHndl->ptr = NULL;
-   devHndl->type = VMCIOBJ_NOT_SET;
-   file->private_data = devHndl;
-
-   compat_mutex_unlock(&vmci_dev.lock);
-
-   return 0;
-
- unlock:
-   compat_mutex_unlock(&vmci_dev.lock);
-   return errcode;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_close --
- *
- *      Close device.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int
-vmci_close(struct inode *inode,  // IN
-           struct file *file)    // IN
-{
-   VMCIObj *devHndl = (VMCIObj *)file->private_data;
-
-   if (devHndl) {
-      VMCI_FreeKernelMem(devHndl, sizeof *devHndl);
-      file->private_data = NULL;
-   }
-   return 0;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_ioctl --
- *
- *      IOCTL interface to device.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static long
-vmci_ioctl(struct file *file,    // IN
-           unsigned int cmd,     // IN
-           unsigned long arg)    // IN
-{
-   return -ENOTTY;
-}
-
-
-#if !defined(HAVE_UNLOCKED_IOCTL)
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_legacy_ioctl --
- *
- *      IOCTL interface for kernels that do not have unlocked_ioctl.
- *
- * Results:
- *      Negative error code, or per-ioctl result.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static int
-vmci_legacy_ioctl(struct inode *inode,  // IN: unused
-                  struct file *filp,    // IN:
-                  u_int iocmd,          // IN:
-                  unsigned long ioarg)  // IN:
-{
-   return vmci_ioctl(filp, iocmd, ioarg);
-}
-#endif
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_poll --
- *
- *      vmci poll function
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static unsigned int
-vmci_poll(struct file *file, // IN
-          poll_table *wait)  // IN
-{
-   return 0;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_interrupt --
- *
- *      Interrupt handler for legacy or MSI interrupt, or for first MSI-X
- *      interrupt (vector VMCI_INTR_DATAGRAM).
- *
- * Results:
- *      COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if
- *      not an interrupt.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static compat_irqreturn_t
-vmci_interrupt(int irq,               // IN
-               void *clientdata,      // IN
-               struct pt_regs *regs)  // IN
-#else
-static compat_irqreturn_t
-vmci_interrupt(int irq,               // IN
-               void *clientdata)      // IN
-#endif
-{
-   vmci_device *dev = clientdata;
-
-   if (dev == NULL) {
-      printk(KERN_DEBUG "vmci_interrupt(): irq %d for unknown device.\n", irq);
-      return COMPAT_IRQ_NONE;
-   }
-
-   /*
-    * If we are using MSI-X with exclusive vectors then we simply schedule
-    * the datagram tasklet, since we know the interrupt was meant for us.
-    * Otherwise we must read the ICR to determine what to do.
-    */
-
-   if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) {
-      tasklet_schedule(&vmci_dg_tasklet);
-   } else {
-      unsigned int icr;
-
-      ASSERT(dev->intr_type == VMCI_INTR_TYPE_INTX ||
-             dev->intr_type == VMCI_INTR_TYPE_MSI);
-
-      /* Acknowledge interrupt and determine what needs doing. */
-      icr = inl(dev->ioaddr + VMCI_ICR_ADDR);
-      if (icr == 0 || icr == 0xffffffff) {
-         return COMPAT_IRQ_NONE;
-      }
-
-      if (icr & VMCI_ICR_DATAGRAM) {
-         tasklet_schedule(&vmci_dg_tasklet);
-         icr &= ~VMCI_ICR_DATAGRAM;
-      }
-      if (icr & VMCI_ICR_NOTIFICATION) {
-         tasklet_schedule(&vmci_bm_tasklet);
-         icr &= ~VMCI_ICR_NOTIFICATION;
-      }
-      if (icr != 0) {
-         printk(KERN_INFO LGPFX"Ignoring unknown interrupt cause (%d).\n", icr);
-      }
-   }
-
-   return COMPAT_IRQ_HANDLED;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * vmci_interrupt_bm --
- *
- *      Interrupt handler for MSI-X interrupt vector VMCI_INTR_NOTIFICATION,
- *      which is for the notification bitmap.  Will only get called if we are
- *      using MSI-X with exclusive vectors.
- *
- * Results:
- *      COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if
- *      not an interrupt.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static compat_irqreturn_t
-vmci_interrupt_bm(int irq,               // IN
-                  void *clientdata,      // IN
-                  struct pt_regs *regs)  // IN
-#else
-static compat_irqreturn_t
-vmci_interrupt_bm(int irq,               // IN
-                  void *clientdata)      // IN
-#endif
-{
-   vmci_device *dev = clientdata;
-
-   if (dev == NULL) {
-      printk(KERN_DEBUG "vmci_interrupt_bm(): irq %d for unknown device.\n", irq);
-      return COMPAT_IRQ_NONE;
-   }
-
-   /* For MSI-X we can just assume it was meant for us. */
-   ASSERT(dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors);
-   tasklet_schedule(&vmci_bm_tasklet);
-
-   return COMPAT_IRQ_HANDLED;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCI_DeviceEnabled --
- *
- *      Checks whether the VMCI device is enabled.
- *
- * Results:
- *      TRUE if device is enabled, FALSE otherwise.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-Bool
-VMCI_DeviceEnabled(void)
-{
-   Bool retval;
-
-   compat_mutex_lock(&vmci_dev.lock);
-   retval = vmci_dev.enabled;
-   compat_mutex_unlock(&vmci_dev.lock);
-
-   return retval;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCI_SendDatagram --
- *
- *      VM to hypervisor call mechanism. We use the standard VMware naming
- *      convention since shared code is calling this function as well.
- *
- * Results:
- *      The result of the hypercall.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-int
-VMCI_SendDatagram(VMCIDatagram *dg)
-{
-   unsigned long flags;
-   int result;
-
-   /* Check args. */
-   if (dg == NULL) {
-      return VMCI_ERROR_INVALID_ARGS;
-   }
-
-   /*
-    * Need to acquire spinlock on the device because
-    * the datagram data may be spread over multiple pages and the monitor may
-    * interleave device user rpc calls from multiple VCPUs. Acquiring the
-    * spinlock precludes that possibility. Disabling interrupts to avoid
-    * incoming datagrams during a "rep out" and possibly landing up in this
-    * function.
-    */
-   spin_lock_irqsave(&vmci_dev.dev_spinlock, flags);
-
-   /*
-    * Send the datagram and retrieve the return value from the result register.
-    */
-   __asm__ __volatile__(
-      "cld\n\t"
-      "rep outsb\n\t"
-      : /* No output. */
-      : "d"(vmci_dev.ioaddr + VMCI_DATA_OUT_ADDR),
-        "c"(VMCI_DG_SIZE(dg)), "S"(dg)
-      );
-
-   /*
-    * XXX Should read result high port as well when updating handlers to
-    * return 64bit.
-    */
-   result = inl(vmci_dev.ioaddr + VMCI_RESULT_LOW_ADDR);
-   spin_unlock_irqrestore(&vmci_dev.dev_spinlock, flags);
-
-   return result;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * dispatch_datagrams --
- *
- *      Reads and dispatches incoming datagrams.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      Reads data from the device.
- *
- *-----------------------------------------------------------------------------
- */
-
-void
-dispatch_datagrams(unsigned long data)
-{
-   vmci_device *dev = (vmci_device *)data;
-
-   if (dev == NULL) {
-      printk(KERN_DEBUG "vmci: dispatch_datagrams(): no vmci device"
-            "present.\n");
-      return;
-   }
-
-   if (data_buffer == NULL) {
-      printk(KERN_DEBUG "vmci: dispatch_datagrams(): no buffer present.\n");
-      return;
-   }
-
-
-   VMCI_ReadDatagramsFromPort((VMCIIoHandle) 0, dev->ioaddr + VMCI_DATA_IN_ADDR,
-                             data_buffer, data_buffer_size);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * process_bitmap --
- *
- *      Scans the notification bitmap for raised flags, clears them
- *      and handles the notifications.
- *
- * Results:
- *      None.
- *
- * Side effects:
- *      None.
- *
- *-----------------------------------------------------------------------------
- */
-
-void
-process_bitmap(unsigned long data)
-{
-   vmci_device *dev = (vmci_device *)data;
-
-   if (dev == NULL) {
-      printk(KERN_DEBUG "vmci: process_bitmaps(): no vmci device"
-            "present.\n");
-      return;
-   }
-
-   if (notification_bitmap == NULL) {
-      printk(KERN_DEBUG "vmci: process_bitmaps(): no bitmap present.\n");
-      return;
-   }
-
-
-   VMCI_ScanNotificationBitmap(notification_bitmap);
-}
-
-
-/*
- *------------------------------------------------------------------------------
- *
- *  VMCIUnsetNotify --
- *
- *     Stub.  Not called in the guest driver (yet).
- *
- *  Result:
- *     Always VMCI_ERROR_GENERIC.
- *
- *------------------------------------------------------------------------------
- */
-
-void
-VMCIUnsetNotify(VMCIContext *context)
-{
-}
-
-
-module_init(vmci_init);
-module_exit(vmci_exit);
-MODULE_DEVICE_TABLE(pci, vmci_ids);
-
-module_param_named(disable_msi, vmci_disable_msi, bool, 0);
-MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
-
-module_param_named(disable_msix, vmci_disable_msix, bool, 0);
-MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default="
-                __stringify(VMCI_DISABLE_MSIX) ")");
-
-/* Module information. */
-MODULE_AUTHOR("VMware, Inc.");
-MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface");
-MODULE_VERSION(VMCI_DRIVER_VERSION_STRING);
-MODULE_LICENSE("GPL v2");
-/*
- * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
- * with them and mark their kernel modules as externally supported via a
- * change to the module header. If this isn't done, the module will not load
- * by default (i.e., neither mkinitrd nor modprobe will accept it).
- */
-MODULE_INFO(supported, "external");