From: VMware, Inc <> Date: Tue, 29 Mar 2011 20:12:01 +0000 (-0700) Subject: Re-commit of VMCI host/guest driver merge. X-Git-Tag: 2011.03.28-387002~39 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9449140afa9db0848148ebe55e91d801ce571987;p=thirdparty%2Fopen-vm-tools.git Re-commit of VMCI host/guest driver merge. 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 a5f4b24c1..f726658a7 100644 --- a/open-vm-tools/modules/linux/shared/vmci_kernel_if.h +++ b/open-vm-tools/modules/linux/shared/vmci_kernel_if.h @@ -394,8 +394,6 @@ void VMCI_FreeQueueBuffer(void *queue, uint64 size); void VMCI_DeviceShutdownBegin(void); void VMCI_DeviceShutdownEnd(void); Bool VMCI_DeviceShutdown(void); -Bool VMCI_HasGuestDevice(void); -Bool VMCI_HasHostDevice(void); #else // _WIN32 # define VMCI_InitQueueMutex(_pq, _cq) # define VMCI_AcquireQueueMutex(_q) @@ -405,14 +403,19 @@ Bool VMCI_HasHostDevice(void); # define VMCI_RevertToNonLocalQueue(_q, _nlq, _s) # define VMCI_FreeQueueBuffer(_q, _s) # define VMCI_DeviceShutdown() FALSE -#if defined(VMX86_TOOLS) -# define VMCI_HasGuestDevice() TRUE -# define VMCI_HasHostDevice() FALSE -#else // VMX86_TOOLS -# define VMCI_HasGuestDevice() FALSE -# define VMCI_HasHostDevice() TRUE -#endif // VMX86_TOOLS #endif // !_WIN32 +#if defined(_WIN32) || (defined(linux) && !defined(VMKERNEL)) + Bool VMCI_HasGuestDevice(void); + Bool VMCI_HasHostDevice(void); +#else +# if defined(VMX86_TOOLS) +# define VMCI_HasGuestDevice() TRUE +# define VMCI_HasHostDevice() FALSE +# else // VMX86_TOOLS +# define VMCI_HasGuestDevice() FALSE +# define VMCI_HasHostDevice() TRUE +# endif // !VMX86_TOOLS +#endif // !(_WIN32 || (linux && !VMKERNEL)) #if defined(VMKERNEL) diff --git a/open-vm-tools/modules/linux/vmci/Makefile.kernel b/open-vm-tools/modules/linux/vmci/Makefile.kernel index a235c07da..805ae488e 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) 2005 VMware, Inc. All rights reserved. +# Copyright (C) 2007 VMware, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -17,18 +17,18 @@ # ########################################################## -#### -#### VMware vmci Makefile to be distributed externally -#### +vm_product_defines = $(if $(findstring tools,$(1)), -DVMX86_TOOLS,) +CC_OPTS += $(call vm_product_defines, $(PRODUCT)) +CC_OPTS += -DVMCI -INCLUDE += -I. +INCLUDE += -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE) -EXTRA_CFLAGS += -DVMX86_TOOLS obj-m += $(DRIVER).o -$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, $(wildcard $(SRCROOT)/*.c))) +$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, \ + $(wildcard $(SRCROOT)/linux/*.c $(SRCROOT)/common/*.c))) # # In open-vm-tools, need to compile the common sources from the shared directory. @@ -48,8 +48,9 @@ $(addprefix $(VMCI_PATH)/,$(DRIVERLOG)): $(VMCI_PATH)/%.o: $(DRIVERLOG_PATH)/%.c clean: rm -rf $(wildcard $(DRIVER).mod.c $(DRIVER).ko .tmp_versions \ - Modules.symvers Module.markers modules.order \ - $(foreach dir,./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) + Module.symvers Modules.symvers Module.markers modules.order \ + $(foreach dir,linux/ common/ \ + ./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) ifneq ($(MODULEBUILDDIR),) rm -f $(MODULEBUILDDIR)/VMwareVMCIModule.symvers endif diff --git a/open-vm-tools/modules/linux/vmci/Makefile.normal b/open-vm-tools/modules/linux/vmci/Makefile.normal new file mode 100644 index 000000000..3b579a7c4 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/Makefile.normal @@ -0,0 +1,148 @@ +#!/usr/bin/make -f +########################################################## +# Copyright (C) 2007 VMware, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation version 2 and no later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +########################################################## + +vm_check_build = $(shell if $(CC) $(CC_OPTS) $(INCLUDE) -Werror -S -o /dev/null -xc $(1) \ + > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi) +vm_product_defines = $(if $(findstring tools,$(1)), -DVMX86_TOOLS,) + +#### +#### DESTDIR is where the module, object files, and dependencies are built +#### +DESTDIR := driver-$(VM_UNAME) + +#### +#### DRIVERNAME should be untouched unless you have a good reason to change +#### it. The form below is how the scripts expect it. +#### +DRIVERNAME := $(DRIVER)-xxx-$(VM_UNAME) + +ifneq (,$(filter x86_64%, $(shell $(CC) -dumpmachine))) +MACHINE := x86_64 +else +MACHINE := x386 +endif + +ifdef QUIET +ECHO := @true +else +ECHO := @echo +endif + +#### +#### You must compile with at least -O level of optimization +#### or the module won't load. +#### If desparate, I think that bringing in 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 := driver.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/vmciCommonInt.h b/open-vm-tools/modules/linux/vmci/common/vmciCommonInt.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciCommonInt.h rename to open-vm-tools/modules/linux/vmci/common/vmciCommonInt.h diff --git a/open-vm-tools/modules/linux/vmci/vmciContext.c b/open-vm-tools/modules/linux/vmci/common/vmciContext.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciContext.c rename to open-vm-tools/modules/linux/vmci/common/vmciContext.c diff --git a/open-vm-tools/modules/linux/vmci/vmciContext.h b/open-vm-tools/modules/linux/vmci/common/vmciContext.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciContext.h rename to open-vm-tools/modules/linux/vmci/common/vmciContext.h diff --git a/open-vm-tools/modules/linux/vmci/vmciDatagram.c b/open-vm-tools/modules/linux/vmci/common/vmciDatagram.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciDatagram.c rename to open-vm-tools/modules/linux/vmci/common/vmciDatagram.c diff --git a/open-vm-tools/modules/linux/vmci/vmciDatagram.h b/open-vm-tools/modules/linux/vmci/common/vmciDatagram.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciDatagram.h rename to open-vm-tools/modules/linux/vmci/common/vmciDatagram.h diff --git a/open-vm-tools/modules/linux/vmci/vmciDoorbell.c b/open-vm-tools/modules/linux/vmci/common/vmciDoorbell.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciDoorbell.c rename to open-vm-tools/modules/linux/vmci/common/vmciDoorbell.c diff --git a/open-vm-tools/modules/linux/vmci/vmciDoorbell.h b/open-vm-tools/modules/linux/vmci/common/vmciDoorbell.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciDoorbell.h rename to open-vm-tools/modules/linux/vmci/common/vmciDoorbell.h diff --git a/open-vm-tools/modules/linux/vmci/vmciDriver.c b/open-vm-tools/modules/linux/vmci/common/vmciDriver.c similarity index 96% rename from open-vm-tools/modules/linux/vmci/vmciDriver.c rename to open-vm-tools/modules/linux/vmci/common/vmciDriver.c index ff074c086..0b0c17fba 100644 --- a/open-vm-tools/modules/linux/vmci/vmciDriver.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciDriver.c @@ -30,6 +30,7 @@ #include "vmciContext.h" #include "vmciDatagram.h" #include "vmciDoorbell.h" +#include "vmciDriver.h" #include "vmciEvent.h" #include "vmciHashtable.h" #include "vmciKernelAPI.h" @@ -39,9 +40,6 @@ # include "vmciVmkInt.h" # include "vm_libc.h" # include "helper_ext.h" -# include "vmciDriver.h" -#else -# include "vmciDriver.h" #endif #define LGPFX "VMCI: " @@ -49,6 +47,9 @@ static Atomic_uint32 vmContextID = { VMCI_INVALID_ID }; static VMCIContext *hostContext; +static Atomic_uint32 vmciClientCount; +static VMCIId ctxUpdateSubID = VMCI_INVALID_ID; + /* *---------------------------------------------------------------------- @@ -91,12 +92,12 @@ VMCI_HostInit(void) goto hostContextExit; } - VMCI_LOG((LGPFX"Driver initialized.")); + VMCI_LOG((LGPFX"host components initialized.")); return VMCI_SUCCESS; - hostContextExit: +hostContextExit: VMCIContext_ReleaseContext(hostContext); - errorExit: +errorExit: return result; } @@ -180,33 +181,77 @@ VMCI_DeviceRelease(void) { } +#else // VMX86_TOOLS /* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * - * VMCI_SendDatagram -- + * VMCI_DeviceGet -- * - * Stub for guest VM to hypervisor call mechanism. + * Verifies that a valid VMCI device is present, and indicates + * the callers intention to use the device until it calls + * VMCI_DeviceRelease(). * * Results: - * Always VMCI_ERROR_GENERIC. + * TRUE if a valid VMCI device is present, FALSE otherwise. * * Side effects: * None. * - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ -int -VMCI_SendDatagram(VMCIDatagram *dg) +VMCI_EXPORT_SYMBOL(VMCI_DeviceGet) +Bool +VMCI_DeviceGet(uint32 *apiVersion) { - return VMCI_ERROR_UNAVAILABLE; + Atomic_Inc32(&vmciClientCount); + + if (*apiVersion > VMCI_KERNEL_API_VERSION) { + *apiVersion = VMCI_KERNEL_API_VERSION; + goto release; + } + + if (!VMCI_DeviceEnabled()) { + goto release; + } + + if (VMCI_DeviceShutdown()) { + goto release; + } + + return TRUE; + +release: + Atomic_Dec32(&vmciClientCount); + return FALSE; } -#else // VMX86_TOOLS -static Atomic_uint32 vmciClientCount; -static VMCIId ctxUpdateSubID = VMCI_INVALID_ID; +/* + *---------------------------------------------------------------------- + * + * VMCI_DeviceRelease -- + * + * Indicates that the caller is done using the VMCI device. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCI_EXPORT_SYMBOL(VMCI_DeviceRelease) +void +VMCI_DeviceRelease(void) +{ + Atomic_Dec32(&vmciClientCount); +} + +#endif // VMX86_TOOLS /* @@ -389,75 +434,6 @@ VMCI_CheckHostCapabilities(void) } -/* - *---------------------------------------------------------------------- - * - * VMCI_DeviceGet -- - * - * Verifies that a valid VMCI device is present, and indicates - * the callers intention to use the device until it calls - * VMCI_DeviceRelease(). - * - * Results: - * TRUE if a valid VMCI device is present, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -VMCI_EXPORT_SYMBOL(VMCI_DeviceGet) -Bool -VMCI_DeviceGet(uint32 *apiVersion) -{ - Atomic_Inc32(&vmciClientCount); - - if (*apiVersion > VMCI_KERNEL_API_VERSION) { - *apiVersion = VMCI_KERNEL_API_VERSION; - goto release; - } - - if (!VMCI_DeviceEnabled()) { - goto release; - } - - if (VMCI_DeviceShutdown()) { - goto release; - } - - return TRUE; - -release: - Atomic_Dec32(&vmciClientCount); - return FALSE; -} - - -/* - *---------------------------------------------------------------------- - * - * VMCI_DeviceRelease -- - * - * Indicates that the caller is done using the VMCI device. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -VMCI_EXPORT_SYMBOL(VMCI_DeviceRelease) -void -VMCI_DeviceRelease(void) -{ - Atomic_Dec32(&vmciClientCount); -} - - /* *---------------------------------------------------------------------- * @@ -624,7 +600,6 @@ VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN } } -#endif // VMX86_TOOLS /* *---------------------------------------------------------------------------- @@ -736,14 +711,14 @@ VMCI_SharedInit(void) VMCIEvent_Init(); VMCIDoorbell_Init(); - VMCI_LOG((LGPFX"Driver initialized.")); + VMCI_LOG((LGPFX"shared components initialized.")); return VMCI_SUCCESS; - contextExit: +contextExit: VMCIContext_Exit(); - resourceExit: +resourceExit: VMCIResource_Exit(); - errorExit: +errorExit: return result; } diff --git a/open-vm-tools/modules/linux/vmci/vmciDriver.h b/open-vm-tools/modules/linux/vmci/common/vmciDriver.h similarity index 98% rename from open-vm-tools/modules/linux/vmci/vmciDriver.h rename to open-vm-tools/modules/linux/vmci/common/vmciDriver.h index 52b010a51..cfd4a821f 100644 --- a/open-vm-tools/modules/linux/vmci/vmciDriver.h +++ b/open-vm-tools/modules/linux/vmci/common/vmciDriver.h @@ -83,7 +83,6 @@ void VMCI_HostCleanup(void); VMCIId VMCI_GetContextID(void); int VMCI_SendDatagram(VMCIDatagram *dg); -#if defined(VMX86_TOOLS) void VMCIUtil_Init(void); void VMCIUtil_Exit(void); Bool VMCI_CheckHostCapabilities(void); @@ -95,7 +94,6 @@ Bool VMCI_DeviceEnabled(void); int VMCIDoSendDatagram(unsigned int dgSize, ULONG *dataPort, ULONG *resultPort, VMCIDatagram *dg); #endif // _WIN64 -#endif // VMX86_TOOLS #endif // _VMCI_DRIVER_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciEvent.c b/open-vm-tools/modules/linux/vmci/common/vmciEvent.c similarity index 99% rename from open-vm-tools/modules/linux/vmci/vmciEvent.c rename to open-vm-tools/modules/linux/vmci/common/vmciEvent.c index 3319d1590..d009ab6a9 100644 --- a/open-vm-tools/modules/linux/vmci/vmciEvent.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciEvent.c @@ -185,7 +185,6 @@ VMCIEvent_Sync(void) } -#ifdef VMX86_TOOLS /* *----------------------------------------------------------------------------- * @@ -210,7 +209,6 @@ VMCIEvent_CheckHostCapabilities(void) /* VMCIEvent does not require any hypercalls. */ return TRUE; } -#endif /* diff --git a/open-vm-tools/modules/linux/vmci/vmciEvent.h b/open-vm-tools/modules/linux/vmci/common/vmciEvent.h similarity index 98% rename from open-vm-tools/modules/linux/vmci/vmciEvent.h rename to open-vm-tools/modules/linux/vmci/common/vmciEvent.h index 3eeef29df..9b5f8cfa4 100644 --- a/open-vm-tools/modules/linux/vmci/vmciEvent.h +++ b/open-vm-tools/modules/linux/vmci/common/vmciEvent.h @@ -38,8 +38,6 @@ void VMCIEvent_Init(void); void VMCIEvent_Exit(void); void VMCIEvent_Sync(void); int VMCIEvent_Dispatch(VMCIDatagram *msg); -#ifdef VMX86_TOOLS Bool VMCIEvent_CheckHostCapabilities(void); -#endif #endif //__VMCI_EVENT_H__ diff --git a/open-vm-tools/modules/linux/vmci/vmciHashtable.c b/open-vm-tools/modules/linux/vmci/common/vmciHashtable.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciHashtable.c rename to open-vm-tools/modules/linux/vmci/common/vmciHashtable.c diff --git a/open-vm-tools/modules/linux/vmci/vmciHashtable.h b/open-vm-tools/modules/linux/vmci/common/vmciHashtable.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciHashtable.h rename to open-vm-tools/modules/linux/vmci/common/vmciHashtable.h diff --git a/open-vm-tools/modules/linux/vmci/vmciQPair.c b/open-vm-tools/modules/linux/vmci/common/vmciQPair.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciQPair.c rename to open-vm-tools/modules/linux/vmci/common/vmciQPair.c diff --git a/open-vm-tools/modules/linux/vmci/vmciQueuePair.c b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciQueuePair.c rename to open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c diff --git a/open-vm-tools/modules/linux/vmci/vmciQueuePair.h b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciQueuePair.h rename to open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h diff --git a/open-vm-tools/modules/linux/vmci/vmciResource.c b/open-vm-tools/modules/linux/vmci/common/vmciResource.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciResource.c rename to open-vm-tools/modules/linux/vmci/common/vmciResource.c diff --git a/open-vm-tools/modules/linux/vmci/vmciResource.h b/open-vm-tools/modules/linux/vmci/common/vmciResource.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciResource.h rename to open-vm-tools/modules/linux/vmci/common/vmciResource.h diff --git a/open-vm-tools/modules/linux/vmci/vmciRoute.c b/open-vm-tools/modules/linux/vmci/common/vmciRoute.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciRoute.c rename to open-vm-tools/modules/linux/vmci/common/vmciRoute.c diff --git a/open-vm-tools/modules/linux/vmci/vmciRoute.h b/open-vm-tools/modules/linux/vmci/common/vmciRoute.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciRoute.h rename to open-vm-tools/modules/linux/vmci/common/vmciRoute.h diff --git a/open-vm-tools/modules/linux/vmci/linux/driver.c b/open-vm-tools/modules/linux/vmci/linux/driver.c new file mode 100644 index 000000000..c01bbbf97 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/linux/driver.c @@ -0,0 +1,2493 @@ +/********************************************************* + * Copyright (C) 2011 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *********************************************************/ + +/* Must come before any kernel header file */ +#include "driver-config.h" + +#define EXPORT_SYMTAB + +#include + +#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/vmciKernelIf.c b/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciKernelIf.c rename to open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c diff --git a/open-vm-tools/modules/linux/vmci/vmci_version.h b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h similarity index 87% rename from open-vm-tools/modules/linux/vmci/vmci_version.h rename to open-vm-tools/modules/linux/vmci/linux/vmci_version.h index dec595865..68fac7e46 100644 --- a/open-vm-tools/modules/linux/vmci/vmci_version.h +++ b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h @@ -25,8 +25,8 @@ #ifndef _VMCI_VERSION_H_ #define _VMCI_VERSION_H_ -#define VMCI_DRIVER_VERSION 9.1.12.0 -#define VMCI_DRIVER_VERSION_COMMAS 9,1,12,0 -#define VMCI_DRIVER_VERSION_STRING "9.1.12.0" +#define VMCI_DRIVER_VERSION 9.1.10.0 +#define VMCI_DRIVER_VERSION_COMMAS 9,1,10,0 +#define VMCI_DRIVER_VERSION_STRING "9.1.10.0" #endif /* _VMCI_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/vmci/pgtbl.h b/open-vm-tools/modules/linux/vmci/shared/pgtbl.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/pgtbl.h rename to open-vm-tools/modules/linux/vmci/shared/pgtbl.h diff --git a/open-vm-tools/modules/linux/vmci/vmciQueue.h b/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmciQueue.h rename to open-vm-tools/modules/linux/vmci/shared/vmciQueue.h diff --git a/open-vm-tools/modules/linux/vmci/vmci_handle_array.h b/open-vm-tools/modules/linux/vmci/shared/vmci_handle_array.h similarity index 100% rename from open-vm-tools/modules/linux/vmci/vmci_handle_array.h rename to open-vm-tools/modules/linux/vmci/shared/vmci_handle_array.h diff --git a/open-vm-tools/modules/linux/vmci/vmci_drv.c b/open-vm-tools/modules/linux/vmci/vmci_drv.c deleted file mode 100644 index 25cefd945..000000000 --- a/open-vm-tools/modules/linux/vmci/vmci_drv.c +++ /dev/null @@ -1,1068 +0,0 @@ -/********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2 and no later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *********************************************************/ - -/* - * vmci.c -- - * - * Linux guest driver for the VMCI device. - */ - -#include "driver-config.h" - -#include -#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");