From: VMware, Inc <> Date: Tue, 29 Mar 2011 20:11:17 +0000 (-0700) Subject: Backout of previous change. X-Git-Tag: 2011.03.28-387002~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef9965944d670a248374f991974348da98c7aa78;p=thirdparty%2Fopen-vm-tools.git Backout of previous change. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/modules/linux/shared/vmci_kernel_if.h b/open-vm-tools/modules/linux/shared/vmci_kernel_if.h index f726658a7..a5f4b24c1 100644 --- a/open-vm-tools/modules/linux/shared/vmci_kernel_if.h +++ b/open-vm-tools/modules/linux/shared/vmci_kernel_if.h @@ -394,6 +394,8 @@ 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) @@ -403,19 +405,14 @@ Bool VMCI_DeviceShutdown(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) diff --git a/open-vm-tools/modules/linux/vmci/Makefile.kernel b/open-vm-tools/modules/linux/vmci/Makefile.kernel index 805ae488e..a235c07da 100644 --- a/open-vm-tools/modules/linux/vmci/Makefile.kernel +++ b/open-vm-tools/modules/linux/vmci/Makefile.kernel @@ -1,6 +1,6 @@ #!/usr/bin/make -f ########################################################## -# Copyright (C) 2007 VMware, Inc. All rights reserved. +# 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 @@ -17,18 +17,18 @@ # ########################################################## -vm_product_defines = $(if $(findstring tools,$(1)), -DVMX86_TOOLS,) -CC_OPTS += $(call vm_product_defines, $(PRODUCT)) -CC_OPTS += -DVMCI +#### +#### VMware vmci Makefile to be distributed externally +#### -INCLUDE += -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux +INCLUDE += -I. EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE) +EXTRA_CFLAGS += -DVMX86_TOOLS obj-m += $(DRIVER).o -$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, \ - $(wildcard $(SRCROOT)/linux/*.c $(SRCROOT)/common/*.c))) +$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, $(wildcard $(SRCROOT)/*.c))) # # In open-vm-tools, need to compile the common sources from the shared directory. @@ -48,9 +48,8 @@ $(addprefix $(VMCI_PATH)/,$(DRIVERLOG)): $(VMCI_PATH)/%.o: $(DRIVERLOG_PATH)/%.c clean: rm -rf $(wildcard $(DRIVER).mod.c $(DRIVER).ko .tmp_versions \ - Module.symvers Modules.symvers Module.markers modules.order \ - $(foreach dir,linux/ common/ \ - ./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) + Modules.symvers Module.markers modules.order \ + $(foreach dir,./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) ifneq ($(MODULEBUILDDIR),) rm -f $(MODULEBUILDDIR)/VMwareVMCIModule.symvers endif diff --git a/open-vm-tools/modules/linux/vmci/Makefile.normal b/open-vm-tools/modules/linux/vmci/Makefile.normal deleted file mode 100644 index 189aa9174..000000000 --- a/open-vm-tools/modules/linux/vmci/Makefile.normal +++ /dev/null @@ -1,148 +0,0 @@ -#!/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 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/$( $@ - -$(C_TARGETS_LINUX_D): %.d: $(SRCROOT)/linux/%.c - $(ECHO) "Dependencies for $( $@ - -driverLog.d: $(DRIVERLOGPATH)/driverLog.c - $(ECHO) "Dependencies for $( $@ - -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: diff --git a/open-vm-tools/modules/linux/vmci/linux/vmci_drv.c b/open-vm-tools/modules/linux/vmci/linux/vmci_drv.c deleted file mode 100644 index c01bbbf97..000000000 --- a/open-vm-tools/modules/linux/vmci/linux/vmci_drv.c +++ /dev/null @@ -1,2493 +0,0 @@ -/********************************************************* - * 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 - -#include -#include -#if defined(__x86_64__) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) -# include -/* 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 -#include -#include -#include -#include - -#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(¬ifyInfo, (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, ¬ifyInfo, 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(¤t->mm->mmap_sem); - retval = get_user_pages(current, current->mm, addr, - 1, 1, 0, &page, NULL); - up_read(¤t->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"); diff --git a/open-vm-tools/modules/linux/vmci/shared/pgtbl.h b/open-vm-tools/modules/linux/vmci/pgtbl.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/shared/pgtbl.h rename to open-vm-tools/modules/linux/vmci/pgtbl.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciCommonInt.h b/open-vm-tools/modules/linux/vmci/vmciCommonInt.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciCommonInt.h rename to open-vm-tools/modules/linux/vmci/vmciCommonInt.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciContext.c b/open-vm-tools/modules/linux/vmci/vmciContext.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciContext.c rename to open-vm-tools/modules/linux/vmci/vmciContext.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciContext.h b/open-vm-tools/modules/linux/vmci/vmciContext.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciContext.h rename to open-vm-tools/modules/linux/vmci/vmciContext.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDatagram.c b/open-vm-tools/modules/linux/vmci/vmciDatagram.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciDatagram.c rename to open-vm-tools/modules/linux/vmci/vmciDatagram.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDatagram.h b/open-vm-tools/modules/linux/vmci/vmciDatagram.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciDatagram.h rename to open-vm-tools/modules/linux/vmci/vmciDatagram.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDoorbell.c b/open-vm-tools/modules/linux/vmci/vmciDoorbell.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciDoorbell.c rename to open-vm-tools/modules/linux/vmci/vmciDoorbell.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDoorbell.h b/open-vm-tools/modules/linux/vmci/vmciDoorbell.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciDoorbell.h rename to open-vm-tools/modules/linux/vmci/vmciDoorbell.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDriver.c b/open-vm-tools/modules/linux/vmci/vmciDriver.c similarity index 96% rename from open-vm-tools/modules/linux/vmci/common/vmciDriver.c rename to open-vm-tools/modules/linux/vmci/vmciDriver.c index 0b0c17fba..ff074c086 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciDriver.c +++ b/open-vm-tools/modules/linux/vmci/vmciDriver.c @@ -30,7 +30,6 @@ #include "vmciContext.h" #include "vmciDatagram.h" #include "vmciDoorbell.h" -#include "vmciDriver.h" #include "vmciEvent.h" #include "vmciHashtable.h" #include "vmciKernelAPI.h" @@ -40,6 +39,9 @@ # include "vmciVmkInt.h" # include "vm_libc.h" # include "helper_ext.h" +# include "vmciDriver.h" +#else +# include "vmciDriver.h" #endif #define LGPFX "VMCI: " @@ -47,9 +49,6 @@ static Atomic_uint32 vmContextID = { VMCI_INVALID_ID }; static VMCIContext *hostContext; -static Atomic_uint32 vmciClientCount; -static VMCIId ctxUpdateSubID = VMCI_INVALID_ID; - /* *---------------------------------------------------------------------- @@ -92,12 +91,12 @@ VMCI_HostInit(void) goto hostContextExit; } - VMCI_LOG((LGPFX"host components initialized.")); + VMCI_LOG((LGPFX"Driver initialized.")); return VMCI_SUCCESS; -hostContextExit: + hostContextExit: VMCIContext_ReleaseContext(hostContext); -errorExit: + errorExit: return result; } @@ -181,77 +180,33 @@ VMCI_DeviceRelease(void) { } -#else // VMX86_TOOLS /* - *---------------------------------------------------------------------- + *----------------------------------------------------------------------------- * - * VMCI_DeviceGet -- + * VMCI_SendDatagram -- * - * Verifies that a valid VMCI device is present, and indicates - * the callers intention to use the device until it calls - * VMCI_DeviceRelease(). + * Stub for guest VM to hypervisor call mechanism. * * Results: - * TRUE if a valid VMCI device is present, FALSE otherwise. + * Always VMCI_ERROR_GENERIC. * * Side effects: * None. * - *---------------------------------------------------------------------- + *----------------------------------------------------------------------------- */ -VMCI_EXPORT_SYMBOL(VMCI_DeviceGet) -Bool -VMCI_DeviceGet(uint32 *apiVersion) +int +VMCI_SendDatagram(VMCIDatagram *dg) { - 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; + return VMCI_ERROR_UNAVAILABLE; } +#else // VMX86_TOOLS -/* - *---------------------------------------------------------------------- - * - * 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 +static Atomic_uint32 vmciClientCount; +static VMCIId ctxUpdateSubID = VMCI_INVALID_ID; /* @@ -434,6 +389,75 @@ 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); +} + + /* *---------------------------------------------------------------------- * @@ -600,6 +624,7 @@ VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN } } +#endif // VMX86_TOOLS /* *---------------------------------------------------------------------------- @@ -711,14 +736,14 @@ VMCI_SharedInit(void) VMCIEvent_Init(); VMCIDoorbell_Init(); - VMCI_LOG((LGPFX"shared components initialized.")); + VMCI_LOG((LGPFX"Driver initialized.")); return VMCI_SUCCESS; -contextExit: + contextExit: VMCIContext_Exit(); -resourceExit: + resourceExit: VMCIResource_Exit(); -errorExit: + errorExit: return result; } diff --git a/open-vm-tools/modules/linux/vmci/common/vmciDriver.h b/open-vm-tools/modules/linux/vmci/vmciDriver.h similarity index 98% rename from open-vm-tools/modules/linux/vmci/common/vmciDriver.h rename to open-vm-tools/modules/linux/vmci/vmciDriver.h index cfd4a821f..52b010a51 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciDriver.h +++ b/open-vm-tools/modules/linux/vmci/vmciDriver.h @@ -83,6 +83,7 @@ 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); @@ -94,6 +95,7 @@ Bool VMCI_DeviceEnabled(void); int VMCIDoSendDatagram(unsigned int dgSize, ULONG *dataPort, ULONG *resultPort, VMCIDatagram *dg); #endif // _WIN64 +#endif // VMX86_TOOLS #endif // _VMCI_DRIVER_H_ diff --git a/open-vm-tools/modules/linux/vmci/common/vmciEvent.c b/open-vm-tools/modules/linux/vmci/vmciEvent.c similarity index 99% rename from open-vm-tools/modules/linux/vmci/common/vmciEvent.c rename to open-vm-tools/modules/linux/vmci/vmciEvent.c index d009ab6a9..3319d1590 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciEvent.c +++ b/open-vm-tools/modules/linux/vmci/vmciEvent.c @@ -185,6 +185,7 @@ VMCIEvent_Sync(void) } +#ifdef VMX86_TOOLS /* *----------------------------------------------------------------------------- * @@ -209,6 +210,7 @@ VMCIEvent_CheckHostCapabilities(void) /* VMCIEvent does not require any hypercalls. */ return TRUE; } +#endif /* diff --git a/open-vm-tools/modules/linux/vmci/common/vmciEvent.h b/open-vm-tools/modules/linux/vmci/vmciEvent.h similarity index 98% rename from open-vm-tools/modules/linux/vmci/common/vmciEvent.h rename to open-vm-tools/modules/linux/vmci/vmciEvent.h index 9b5f8cfa4..3eeef29df 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciEvent.h +++ b/open-vm-tools/modules/linux/vmci/vmciEvent.h @@ -38,6 +38,8 @@ 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/common/vmciHashtable.c b/open-vm-tools/modules/linux/vmci/vmciHashtable.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciHashtable.c rename to open-vm-tools/modules/linux/vmci/vmciHashtable.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciHashtable.h b/open-vm-tools/modules/linux/vmci/vmciHashtable.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciHashtable.h rename to open-vm-tools/modules/linux/vmci/vmciHashtable.h diff --git a/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c b/open-vm-tools/modules/linux/vmci/vmciKernelIf.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c rename to open-vm-tools/modules/linux/vmci/vmciKernelIf.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQPair.c b/open-vm-tools/modules/linux/vmci/vmciQPair.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciQPair.c rename to open-vm-tools/modules/linux/vmci/vmciQPair.c diff --git a/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h b/open-vm-tools/modules/linux/vmci/vmciQueue.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/shared/vmciQueue.h rename to open-vm-tools/modules/linux/vmci/vmciQueue.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c b/open-vm-tools/modules/linux/vmci/vmciQueuePair.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c rename to open-vm-tools/modules/linux/vmci/vmciQueuePair.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h b/open-vm-tools/modules/linux/vmci/vmciQueuePair.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h rename to open-vm-tools/modules/linux/vmci/vmciQueuePair.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciResource.c b/open-vm-tools/modules/linux/vmci/vmciResource.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciResource.c rename to open-vm-tools/modules/linux/vmci/vmciResource.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciResource.h b/open-vm-tools/modules/linux/vmci/vmciResource.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciResource.h rename to open-vm-tools/modules/linux/vmci/vmciResource.h diff --git a/open-vm-tools/modules/linux/vmci/common/vmciRoute.c b/open-vm-tools/modules/linux/vmci/vmciRoute.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciRoute.c rename to open-vm-tools/modules/linux/vmci/vmciRoute.c diff --git a/open-vm-tools/modules/linux/vmci/common/vmciRoute.h b/open-vm-tools/modules/linux/vmci/vmciRoute.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/common/vmciRoute.h rename to open-vm-tools/modules/linux/vmci/vmciRoute.h diff --git a/open-vm-tools/modules/linux/vmci/vmci_drv.c b/open-vm-tools/modules/linux/vmci/vmci_drv.c new file mode 100644 index 000000000..25cefd945 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmci_drv.c @@ -0,0 +1,1068 @@ +/********************************************************* + * 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 +#include + +#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"); diff --git a/open-vm-tools/modules/linux/vmci/shared/vmci_handle_array.h b/open-vm-tools/modules/linux/vmci/vmci_handle_array.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/shared/vmci_handle_array.h rename to open-vm-tools/modules/linux/vmci/vmci_handle_array.h diff --git a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h b/open-vm-tools/modules/linux/vmci/vmci_version.h similarity index 87% rename from open-vm-tools/modules/linux/vmci/linux/vmci_version.h rename to open-vm-tools/modules/linux/vmci/vmci_version.h index 68fac7e46..dec595865 100644 --- a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h +++ b/open-vm-tools/modules/linux/vmci/vmci_version.h @@ -25,8 +25,8 @@ #ifndef _VMCI_VERSION_H_ #define _VMCI_VERSION_H_ -#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" +#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" #endif /* _VMCI_VERSION_H_ */