diff options
| author | Michael Meskes <meskes@debian.org> | 2009-02-09 17:42:08 +0100 |
|---|---|---|
| committer | Michael Meskes <meskes@debian.org> | 2009-02-09 17:42:08 +0100 |
| commit | 5b633c860b9ccb98910812f91c2474fda316b50b (patch) | |
| tree | db6656e828509048e53ad8aea69d2edb9f64f442 /src/VBox/HostDrivers | |
| parent | ce414e6eec1583def0dc7be0926f1a07364cb5e3 (diff) | |
| download | virtualbox-5b633c860b9ccb98910812f91c2474fda316b50b.tar.gz | |
Imported 2.1.2-dfsgupstream/2.1.2-dfsg
Diffstat (limited to 'src/VBox/HostDrivers')
107 files changed, 39801 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Makefile.kmk b/src/VBox/HostDrivers/Makefile.kmk new file mode 100644 index 000000000..1dae601fa --- /dev/null +++ b/src/VBox/HostDrivers/Makefile.kmk @@ -0,0 +1,55 @@ +# $Id: Makefile.kmk 14545 2008-11-24 21:15:01Z vboxsync $ +## @file +# Top-level makefile for the VBox Host drivers. +# + +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SUB_DEPTH = ../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefiles. +include $(PATH_SUB_CURRENT)/Support/Makefile.kmk + +ifndef VBOX_ONLY_DOCS + ifeq ($(KBUILD_TARGET),win) + include $(PATH_SUB_CURRENT)/VBoxTAP/Makefile.kmk + endif + ifdef VBOX_WITH_USB + include $(PATH_SUB_CURRENT)/VBoxUSB/Makefile.kmk + endif + if1of ($(KBUILD_TARGET), darwin solaris win linux) + ifdef VBOX_WITH_NETFLT + include $(PATH_SUB_CURRENT)/VBoxNetFlt/Makefile.kmk + endif + endif + + ifeq ($(KBUILD_TARGET),linux) + # + # Install the Makefile for module compliation on Linux hosts + # + INSTALLS += HostDrivers-src + HostDrivers-src_INST = bin/src/ + HostDrivers-src_MODE = a+r,u+w + HostDrivers-src_SOURCES = linux/Makefile + endif + +endif # !VBOX_ONLY_DOCS + +# Let kBuild generate the rules. +include $(KBUILD_PATH)/subfooter.kmk + diff --git a/src/VBox/HostDrivers/Support/Makefile.kmk b/src/VBox/HostDrivers/Support/Makefile.kmk new file mode 100644 index 000000000..7333bf284 --- /dev/null +++ b/src/VBox/HostDrivers/Support/Makefile.kmk @@ -0,0 +1,434 @@ +# $Id: Makefile.kmk 15589 2008-12-16 14:24:08Z vboxsync $ +## @file +# Sub-Makefile for the support library and the drivers/modules/kexts it uses. +# + +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# Targets +# +LIBRARIES += SUPR3 SUPR3HardenedStatic +ifndef VBOX_ONLY_DOCS + ifeq ($(filter-out pe lx,$(VBOX_LDR_FMT)),) + LIBRARIES += SUPR0 + endif + ifdef VBOX_WITH_SUPSVC + PROGRAMS += VBoxSupSvc + endif + ifdef VBOX_WITH_VBOXDRV + LIBRARIES += SUPR0IdcClient + SYSMODS.freebsd += vboxdrv + SYSMODS.linux += vboxdrv + SYSMODS.win += VBoxDrv + SYSMODS.os2 += VBoxDrv + SYSMODS.solaris += vboxdrv + endif + INSTALLS.linux += vboxdrv-mod vboxdrv-sh + + # + # Include sub-makefile(s). + # + include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + + # + # Populate FILES_VBOXDRV_NOBIN and FILES_VBOXDRV_BIN + # + include $(PATH_SUB_CURRENT)/linux/files_vboxdrv +endif # !VBOX_ONLY_DOCS + + +# +# The Ring-3 Support Library (this is linked into the IPRT dll, VBoxRT). +# +ifneq ($(filter l4%,$(KBUILD_TARGET) $(BUILD_TARGET_SUB)),) + # L4 has trouble with -pedantic. It also make trouble when inlining is not enabled. + SUPR3_TEMPLATE = VBOXR3NP +else + SUPR3_TEMPLATE = VBOXR3 +endif +SUPR3_DEFS = IN_SUP_R3 IN_RT_R3 +ifdef VBOX_WITH_SUPSVC + SUPR3_DEFS += VBOX_WITH_SUPSVC +endif +SUPR3_INCS := $(PATH_SUB_CURRENT) +SUPR3_INCS.l4 = $(L4_INCDIR) +SUPR3_SOURCES = \ + SUPLib.cpp \ + SUPR3HardenedIPRT.cpp \ + SUPR3HardenedVerify.cpp \ + $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp + + +# +# The static part of the hardened support library (ring-3). +# +SUPR3HardenedStatic_TEMPLATE = VBOXR3HARDENEDLIB +SUPR3HardenedStatic_DEFS = IN_SUP_HARDENED_R3 +ifdef VBOX_WITH_SUPSVC + SUPR3HardenedStatic_DEFS += VBOX_WITH_SUPSVC +endif +SUPR3HardenedStatic_INCS = . +SUPR3HardenedStatic_SOURCES = \ + SUPR3HardenedMain.cpp \ + SUPR3HardenedVerify.cpp \ + $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp + +ifndef VBOX_ONLY_DOCS + +# +# VBoxSupSvc - The system wide service/daemon. +# +VBoxSupSvc_TEMPLATE = VBOXR3EXE +VBoxSupSvc_SOURCES = \ + SUPSvc.cpp \ + SUPSvcGlobal.cpp \ + $(KBUILD_TARGET)/SUPSvc-$(KBUILD_TARGET).cpp +if1of ($(KBUILD_TARGET), win) + VBoxSupSvc_SOURCES += \ + SUPSvcGrant.cpp +endif +ifn1of ($(KBUILD_TARGET), win) + VBoxSupSvc_SOURCES += \ + SUPSvcMain-posix.cpp +endif +VBoxSupSvc_LIBS = \ + $(LIB_RUNTIME) + + +# +# SUPR0 - The Ring-0 Import / Thunk library. +# +SUPR0_TEMPLATE = VBOXR0 +ifeq ($(VBOX_LDR_FMT),pe) + SUPR0_SOURCES += SUPR0.def +endif +ifeq ($(VBOX_LDR_FMT),lx) +SUPR0_SOURCES += $$(PATH_SUPR0)/SUPR0.def +$$(PATH_SUPR0)/SUPR0.def: $(PATH_SUB_CURRENT)/SUPR0.def | $$(dir $$@) + $(SED) -e 's/^[ \t][ \t]*\([gA-Z]\)/ _\1/' -e 's/[ \t]DATA[ \t]*/ /' $< > $@.tmp + $(MV) -f $@.tmp $@ +endif + + +# +# SUPR0IdcClient - The Ring-0 IDC client driver library. +# +SUPR0IdcClient_TEMPLATE = VBOXR0DRV +SUPR0IdcClient_DEFS = IN_RT_R0 IN_SUP_R0 IN_SUP_STATIC +SUPR0IdcClient_SDKS.win = W2K3DDK WINPSDKINCS +SUPR0IdcClient_SOURCES.$(KBUILD_TARGET) = \ + $(KBUILD_TARGET)/SUPR0IdcClient-$(KBUILD_TARGET).c +SUPR0IdcClient_SOURCES = \ + SUPR0IdcClient.c \ + SUPR0IdcClientComponent.c \ + SUPR0IdcClientStubs.c + + +# +# VBoxDrv.sys - The Windows driver. +# +## @todo consoliate all the targets into a single mess. +ifeq ($(KBUILD_TARGET),win) +VBoxDrv_TEMPLATE = VBOXR0DRV +ifdef VBOX_SIGNING_MODE + VBoxDrv_NOINST = true +endif +VBoxDrv_DEFS = IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER +VBoxDrv_DEFS.amd64 = RT_WITH_W64_UNWIND_HACK +VBoxDrv_SDKS = W2K3DDK WINPSDKINCS +VBoxDrv_INCS := $(PATH_SUB_CURRENT) +VBoxDrv_SOURCES = \ + win/SUPDrv-win.cpp \ + win/SUPDrvA-win.asm \ + SUPDrv.c +VBoxDrv_LDFLAGS.x86 = -Entry:DriverEntry@8 +VBoxDrv_LDFLAGS.amd64 = -Entry:DriverEntry +VBoxDrv_LIBS = \ + $(PATH_SDK_W2K3DDK_LIB)/ntoskrnl.lib \ + $(PATH_SDK_W2K3DDK_LIB)/hal.lib \ + $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) + + +INSTALLS += VBoxDrv-inf +VBoxDrv-inf_INST = $(INST_BIN) +VBoxDrv-inf_MODE = a+r,u+w +VBoxDrv-inf_SOURCES = \ + $(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.inf +VBoxDrv-inf_CLEAN = $(VBoxDrv-inf_SOURCES) +VBoxDrv-inf_BLDDIRS = $(PATH_TARGET)/VBoxDrvCat.dir + +$(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.inf: $(PATH_SUB_CURRENT)/win/VBoxDrv.inf $(MAKEFILE_CURRENT) | $$(call DIRDEP,$$(@D)) + $(call MSG_GENERATE,VBoxDrv-inf,$@,$<) + $(call VBOX_EDIT_INF_FN,$<,$@) + + ifdef VBOX_SIGNING_MODE +VBoxDrv-inf_SOURCES += \ + $(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.sys \ + $(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.cat + +$(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.sys: $$(TARGET_VBoxDrv) | $$(call DIRDEP,$$(@D)) + $(INSTALL) -m 644 $< $(@D) + +$(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.cat: \ + $(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.inf \ + $(PATH_TARGET)/VBoxDrvCat.dir/VBoxDrv.sys + $(call MSG_TOOL,Inf2Cat,VBoxDrv-inf,$@,$<) + $(call VBOX_MAKE_CAT_FN, $(@D),$@) + endif # signing +endif # win + + +# +# vboxdrv.ko - The Linux Kernel Module (syntax check only). +# +ifeq ($(KBUILD_TARGET),linux) +vboxdrv_TEMPLATE = VBOXR0DRV +vboxdrv_NOINST = true +vboxdrv_DEFS = KBUILD_MODNAME=KBUILD_STR\(vboxdrv\) KBUILD_BASENAME=KBUILD_STR\(vboxdrv\) MODULE IN_RT_R0 IN_SUP_R0 CONFIG_VBOXDRV_AS_MISC +ifdef VBOX_LINUX_VERSION_2_4 +vboxdrv_DEFS += EXPORT_SYMTAB +endif +vboxdrv_INCS := \ + $(PATH_SUB_CURRENT) \ + $(PATH_ROOT)/src/VBox/Runtime/r0drv/linux +vboxdrv_LIBS = $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +vboxdrv_LIBS.debug = $(vboxdrv_LIBS) $(VBOX_GCC_LIBGCC) +vboxdrv_SOURCES = \ + $(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).c \ + SUPDrv.c +ifndef VBOX_LINUX_VERSION_2_4 +vboxdrv_SOURCES += \ + $(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).mod.c +endif + + +# +# Targets for installing the linux sources. +# +vboxdrv-mod_INST = bin/src/vboxdrv/ +vboxdrv-mod_MODE = a+r,u+w +vboxdrv-mod_SOURCES = $(subst ",,$(FILES_VBOXDRV_NOBIN)) #" +vboxdrv-mod_SOURCES += \ + $(if $(VBOX_OSE),,\ + $(PATH_vboxdrv-mod)/dkms.conf) \ + $(PATH_vboxdrv-mod)/Makefile +vboxdrv-mod_CLEAN = \ + $(PATH_vboxdrv-mod)/dkms.conf \ + $(PATH_vboxdrv-mod)/Makefile + +vboxdrv-sh_INST = bin/src/vboxdrv +vboxdrv-sh_MODE = a+rx,u+w +vboxdrv-sh_SOURCES = $(subst ",,$(FILES_VBOXDRV_BIN)) #" +vboxdrv-sh_SOURCES += \ + $(PATH_vboxdrv-sh)/build_in_tmp \ + $(if $(VBOX_OSE),,\ + $(PATH_ROOT)/src/VBox/HostDrivers/linux/do_Module.symvers) +vboxdrv-sh_CLEAN = \ + $(PATH_TARGET)/vboxdrv-sh-1.dep \ + $(PATH_vboxdrv-sh)/build_in_tmp + + +# Scripts needed for building the kernel modules + +$$(PATH_vboxdrv-sh)/build_in_tmp: \ + $(PATH_ROOT)/src/VBox/HostDrivers/linux/build_in_tmp \ + $(VBOX_VERSION_STAMP) \ + | $$(dir $$@) + $(call MSG_TOOL,Creating,,$@) + $(QUIET)$(SED) -e "s;_VERSION_;${VBOX_VERSION_STRING};g; s;_MODULE_;vboxdrv;g" --output $@ $< + $(QUIET)chmod 0755 $@ + +$$(PATH_vboxdrv-mod)/dkms.conf: \ + $(PATH_SUB_CURRENT)/linux/dkms.conf \ + $(VBOX_VERSION_STAMP) \ + | $$(dir $$@) + $(call MSG_TOOL,Creating,,$@) + $(QUIET)$(SED) -e "s;_VERSION_;${VBOX_VERSION_STRING};g" --output $@ $< + +includedep $(PATH_TARGET)/vboxdrv-sh-1.dep +$$(PATH_vboxdrv-mod)/Makefile: \ + $(PATH_SUB_CURRENT)/linux/Makefile \ + $$(if $$(eq $$(Support/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \ + | $$(dir $$@) + $(call MSG_TOOL,Creating,,$@) + ifndef VBOX_WITH_HARDENING + $(QUIET)$(SED) -e "s;-DVBOX_WITH_HARDENING;;g" --output $@ $< + else + $(QUIET)$(CP) -f $< $@ + endif + %$(QUIET2)$(RM) -f -- $(PATH_TARGET)/vboxdrv-sh-1.dep + %$(QUIET2)$(APPEND) '$(PATH_TARGET)/vboxdrv-sh-1.dep' 'Support/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)' + +endif # real linux + + + + +# +# VBoxDrv.kext - The Darwin Kernel Extension. +# +ifeq ($(KBUILD_TARGET),darwin) +# The Tiger variant. +SYSMODS += VBoxDrvTiger +VBoxDrvTiger_TEMPLATE = VBOXR0DRV +VBoxDrvTiger_INST = $(INST_VBOXDRV_TIGER)Contents/MacOS/ +VBoxDrvTiger_DEFS := IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER VBOX_SVN_REV=$(VBOX_SVN_REV) +VBoxDrvTiger_DEFS.debug += DEBUG_DARWIN_GIP +VBoxDrvTiger_INCS = . +VBoxDrvTiger_LIBS = $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +VBoxDrvTiger_LDFLAGS = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded +VBoxDrvTiger_SOURCES = \ + SUPDrv.c \ + $(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).cpp + +INSTALLS += VBoxDrvTiger.kext +VBoxDrvTiger.kext_INST = $(INST_VBOXDRV_TIGER)Contents/ +VBoxDrvTiger.kext_SOURCES = $(PATH_VBoxDrvTiger.kext)/Info.plist +VBoxDrvTiger.kext_CLEAN = $(PATH_VBoxDrvTiger.kext)/Info.plist + +$$(PATH_VBoxDrvTiger.kext)/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info-Tiger.plist $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxDrvTiger,$@,$<) + $(xQUIET)$(RM) -f $@ + $(xQUIET)$(SED) \ + -e 's/@VBOX_VERSION_STRING@/$(VBOX_VERSION_STRING)/g' \ + -e 's/@VBOX_VERSION_MAJOR@/$(VBOX_VERSION_MAJOR)/g' \ + -e 's/@VBOX_VERSION_MINOR@/$(VBOX_VERSION_MINOR)/g' \ + -e 's/@VBOX_VERSION_BUILD@/$(VBOX_VERSION_BUILD)/g' \ + --output $@ \ + $< + +# The Leopard variant. +SYSMODS += VBoxDrv +VBoxDrv_EXTENDS = VBoxDrvTiger +VBoxDrv_EXTENDS_BY = appending +VBoxDrv_TEMPLATE = VBOXR0DRVOSX105 +VBoxDrv_INST = $(INST_VBOXDRV)Contents/MacOS/ +VBoxDrv_DEFS = VBOX_WITH_HOST_VMX + +INSTALLS += VBoxDrv.kext +VBoxDrv.kext_INST = $(INST_VBOXDRV)Contents/ +VBoxDrv.kext_SOURCES = $(PATH_VBoxDrv.kext)/Info.plist +VBoxDrv.kext_CLEAN = $(PATH_VBoxDrv.kext)/Info.plist + +$$(PATH_VBoxDrv.kext)/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxDrv,$@,$<) + $(xQUIET)$(RM) -f $@ + $(xQUIET)$(SED) \ + -e 's/@VBOX_VERSION_STRING@/$(VBOX_VERSION_STRING)/g' \ + -e 's/@VBOX_VERSION_MAJOR@/$(VBOX_VERSION_MAJOR)/g' \ + -e 's/@VBOX_VERSION_MINOR@/$(VBOX_VERSION_MINOR)/g' \ + -e 's/@VBOX_VERSION_BUILD@/$(VBOX_VERSION_BUILD)/g' \ + --output $@ \ + $< + +# Common manual loader script. +INSTALLS += Scripts +Scripts_INST = $(INST_DIST) +Scripts_SOURCES = \ + darwin/load.sh + +endif # darwin + + +# +# VBoxDrv.sys - The OS/2 driver. +# +ifeq ($(KBUILD_TARGET),os2) +VBoxDrv_TEMPLATE = VBOXR0DRV +VBoxDrv_DEFS = IN_RT_R0 IN_SUP_R0 +VBoxDrv_INCS := $(PATH_SUB_CURRENT) +#VBoxDrv_LDFLAGS = -s -t -v +VBoxDrv_SOURCES = \ + os2/SUPDrvA-os2.asm \ + os2/SUPDrv-os2.def +VBoxDrv_LIBS = \ + $(TARGET_VBoxDrvLib) \ + $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \ + $(VBOX_GCC_LIBGCC) \ + end + +# temp hack to ensure that SUPDrvA-os2.asm is first in the link. +LIBRARIES += VBoxDrvLib +VBoxDrvLib_TEMPLATE = VBOXR0DRV +VBoxDrvLib_NOINST = 1 +VBoxDrvLib_DEFS = IN_RT_R0 IN_SUP_R0 +VBoxDrvLib_INCS := \ + . \ + $(PATH_ROOT)/src/VBox/Runtime/include +VBoxDrvLib_SOURCES = \ + os2/SUPDrv-os2.cpp \ + SUPDrv.c +endif + + +# +# vboxdrv.ko - The FreeBSD Kernel Module. +# +ifeq ($(KBUILD_TARGET),freebsd) +vboxdrv_TEMPLATE = VBOXR0DRV +vboxdrv_DEFS = IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER VBOX_SVN_REV=$(VBOX_SVN_REV) +vboxdrv_INCS := $(PATH_SUB_CURRENT) +vboxdrv_LIBS = $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +vboxdrv_SOURCES := \ + $(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).c \ + $(PATH_SUB_CURRENT)/$(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).def \ + SUPDrv.c +## @todo the SUPDrv-freebsd.def is most probably gonna break it and require build system hacking... +endif # freebsd + + +# +# vboxdrv.o - The Solaris Kernel Module. +# +ifeq ($(KBUILD_TARGET),solaris) +vboxdrv_TEMPLATE = VBOXR0DRV +vboxdrv_DEFS = IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER +ifdef VBOX_WITH_NETFLT + vboxdrv_DEFS += VBOX_WITH_NETFLT +endif +vboxdrv_DEFS += VBOX_SVN_REV=$(VBOX_SVN_REV) +vboxdrv_DEPS += $(VBOX_SVN_REV_KMK) +vboxdrv_INCS := $(PATH_SUB_CURRENT) +vboxdrv_LIBS = $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +vboxdrv_SOURCES = \ + $(KBUILD_TARGET)/SUPDrv-$(KBUILD_TARGET).c \ + SUPDrv.c +endif # solaris + + +# +# SUPDrv.c needs the VBOX_SVN_REV. +# +SUPDrv.c_DEFS += VBOX_SVN_REV=$(VBOX_SVN_REV) + + +endif # !VBOX_ONLY_DOCS +include $(KBUILD_PATH)/subfooter.kmk + diff --git a/src/VBox/HostDrivers/Support/SUPDrv.c b/src/VBox/HostDrivers/Support/SUPDrv.c new file mode 100644 index 000000000..4e7fbe908 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPDrv.c @@ -0,0 +1,4810 @@ +/* $Revision: 15843 $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - Common code. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include "SUPDrvInternal.h" +#ifndef PAGE_SHIFT +# include <iprt/param.h> +#endif +#include <iprt/alloc.h> +#include <iprt/semaphore.h> +#include <iprt/spinlock.h> +#include <iprt/thread.h> +#include <iprt/process.h> +#include <iprt/mp.h> +#include <iprt/power.h> +#include <iprt/cpuset.h> +#include <iprt/uuid.h> +#include <VBox/param.h> +#include <VBox/log.h> +#include <VBox/err.h> +#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) +# include <iprt/crc32.h> +# include <iprt/net.h> +#endif +/* VBox/x86.h not compatible with the Linux kernel sources */ +#ifdef RT_OS_LINUX +# define X86_CPUID_VENDOR_AMD_EBX 0x68747541 +# define X86_CPUID_VENDOR_AMD_ECX 0x444d4163 +# define X86_CPUID_VENDOR_AMD_EDX 0x69746e65 +#else +# include <VBox/x86.h> +#endif + +/* + * Logging assignments: + * Log - useful stuff, like failures. + * LogFlow - program flow, except the really noisy bits. + * Log2 - Cleanup. + * Log3 - Loader flow noise. + * Log4 - Call VMMR0 flow noise. + * Log5 - Native yet-to-be-defined noise. + * Log6 - Native ioctl flow noise. + * + * Logging requires BUILD_TYPE=debug and possibly changes to the logger + * instanciation in log-vbox.c(pp). + */ + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/* from x86.h - clashes with linux thus this duplication */ +#undef X86_CR0_PG +#define X86_CR0_PG RT_BIT(31) +#undef X86_CR0_PE +#define X86_CR0_PE RT_BIT(0) +#undef X86_CPUID_AMD_FEATURE_EDX_NX +#define X86_CPUID_AMD_FEATURE_EDX_NX RT_BIT(20) +#undef MSR_K6_EFER +#define MSR_K6_EFER 0xc0000080 +#undef MSR_K6_EFER_NXE +#define MSR_K6_EFER_NXE RT_BIT(11) +#undef MSR_K6_EFER_LMA +#define MSR_K6_EFER_LMA RT_BIT(10) +#undef X86_CR4_PGE +#define X86_CR4_PGE RT_BIT(7) +#undef X86_CR4_PAE +#define X86_CR4_PAE RT_BIT(5) +#undef X86_CPUID_AMD_FEATURE_EDX_LONG_MODE +#define X86_CPUID_AMD_FEATURE_EDX_LONG_MODE RT_BIT(29) + + +/** The frequency by which we recalculate the u32UpdateHz and + * u32UpdateIntervalNS GIP members. The value must be a power of 2. */ +#define GIP_UPDATEHZ_RECALC_FREQ 0x800 + +/** + * Validates a session pointer. + * + * @returns true/false accordingly. + * @param pSession The session. + */ +#define SUP_IS_SESSION_VALID(pSession) \ + ( VALID_PTR(pSession) \ + && pSession->u32Cookie == BIRD_INV) + +/** @def VBOX_SVN_REV + * The makefile should define this if it can. */ +#ifndef VBOX_SVN_REV +# define VBOX_SVN_REV 0 +#endif + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession); +static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType); +static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq); +static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq); +static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq); +static int supdrvIOCtl_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq); +static int supdrvIDC_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQGETSYM pReq); +static int supdrvLdrSetVMMR0EPs(PSUPDRVDEVEXT pDevExt, void *pvVMMR0, void *pvVMMR0EntryInt, void *pvVMMR0EntryFast, void *pvVMMR0EntryEx); +static void supdrvLdrUnsetVMMR0EPs(PSUPDRVDEVEXT pDevExt); +static int supdrvLdrAddUsage(PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage); +static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage); +static int supdrvIOCtl_CallServiceModule(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPCALLSERVICE pReq); +static SUPGIPMODE supdrvGipDeterminTscMode(PSUPDRVDEVEXT pDevExt); +#ifdef RT_OS_WINDOWS +static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages); +static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3); +#endif /* RT_OS_WINDOWS */ +static int supdrvGipCreate(PSUPDRVDEVEXT pDevExt); +static void supdrvGipDestroy(PSUPDRVDEVEXT pDevExt); +static DECLCALLBACK(void) supdrvGipSyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick); +static DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick); +static DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser); + +#ifdef RT_WITH_W64_UNWIND_HACK +DECLASM(int) supdrvNtWrapVMMR0EntryEx(PFNRT pfnVMMR0EntryEx, PVM pVM, unsigned uOperation, PSUPVMMR0REQHDR pReq, uint64_t u64Arg, PSUPDRVSESSION pSession); +DECLASM(int) supdrvNtWrapVMMR0EntryFast(PFNRT pfnVMMR0EntryFast, PVM pVM, unsigned idCpu, unsigned uOperation); +DECLASM(void) supdrvNtWrapObjDestructor(PFNRT pfnDestruction, void *pvObj, void *pvUser1, void *pvUser2); +DECLASM(void *) supdrvNtWrapQueryFactoryInterface(PFNRT pfnQueryFactoryInterface, struct SUPDRVFACTORY const *pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid); +DECLASM(int) supdrvNtWrapModuleInit(PFNRT pfnModuleInit); +DECLASM(void) supdrvNtWrapModuleTerm(PFNRT pfnModuleTerm); +DECLASM(int) supdrvNtWrapServiceReqHandler(PFNRT pfnServiceReqHandler, PSUPDRVSESSION pSession, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr); + +DECLASM(int) UNWIND_WRAP(SUPR0ComponentRegisterFactory)(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory); +DECLASM(int) UNWIND_WRAP(SUPR0ComponentDeregisterFactory)(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory); +DECLASM(int) UNWIND_WRAP(SUPR0ComponentQueryFactory)(PSUPDRVSESSION pSession, const char *pszName, const char *pszInterfaceUuid, void **ppvFactoryIf); +DECLASM(void *) UNWIND_WRAP(SUPR0ObjRegister)(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2); +DECLASM(int) UNWIND_WRAP(SUPR0ObjAddRef)(void *pvObj, PSUPDRVSESSION pSession); +DECLASM(int) UNWIND_WRAP(SUPR0ObjAddRefEx)(void *pvObj, PSUPDRVSESSION pSession, bool fNoPreempt); +DECLASM(int) UNWIND_WRAP(SUPR0ObjRelease)(void *pvObj, PSUPDRVSESSION pSession); +DECLASM(int) UNWIND_WRAP(SUPR0ObjVerifyAccess)(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName); +DECLASM(int) UNWIND_WRAP(SUPR0LockMem)(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages); +DECLASM(int) UNWIND_WRAP(SUPR0UnlockMem)(PSUPDRVSESSION pSession, RTR3PTR pvR3); +DECLASM(int) UNWIND_WRAP(SUPR0ContAlloc)(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys); +DECLASM(int) UNWIND_WRAP(SUPR0ContFree)(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr); +DECLASM(int) UNWIND_WRAP(SUPR0LowAlloc)(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS paPages); +DECLASM(int) UNWIND_WRAP(SUPR0LowFree)(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr); +DECLASM(int) UNWIND_WRAP(SUPR0MemAlloc)(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3); +DECLASM(int) UNWIND_WRAP(SUPR0MemGetPhys)(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, PSUPPAGE paPages); +DECLASM(int) UNWIND_WRAP(SUPR0MemFree)(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr); +DECLASM(int) UNWIND_WRAP(SUPR0PageAlloc)(PSUPDRVSESSION pSession, uint32_t cPages, PRTR3PTR ppvR3, PRTHCPHYS paPages); +DECLASM(int) UNWIND_WRAP(SUPR0PageFree)(PSUPDRVSESSION pSession, RTR3PTR pvR3); +//DECLASM(int) UNWIND_WRAP(SUPR0Printf)(const char *pszFormat, ...); +DECLASM(SUPPAGINGMODE) UNWIND_WRAP(SUPR0GetPagingMode)(void); +DECLASM(void *) UNWIND_WRAP(RTMemAlloc)(size_t cb) RT_NO_THROW; +DECLASM(void *) UNWIND_WRAP(RTMemAllocZ)(size_t cb) RT_NO_THROW; +DECLASM(void) UNWIND_WRAP(RTMemFree)(void *pv) RT_NO_THROW; +DECLASM(void *) UNWIND_WRAP(RTMemDup)(const void *pvSrc, size_t cb) RT_NO_THROW; +DECLASM(void *) UNWIND_WRAP(RTMemDupEx)(const void *pvSrc, size_t cbSrc, size_t cbExtra) RT_NO_THROW; +DECLASM(void *) UNWIND_WRAP(RTMemRealloc)(void *pvOld, size_t cbNew) RT_NO_THROW; +DECLASM(int) UNWIND_WRAP(RTR0MemObjAllocLow)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable); +DECLASM(int) UNWIND_WRAP(RTR0MemObjAllocPage)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable); +DECLASM(int) UNWIND_WRAP(RTR0MemObjAllocPhys)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest); +DECLASM(int) UNWIND_WRAP(RTR0MemObjAllocPhysNC)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest); +DECLASM(int) UNWIND_WRAP(RTR0MemObjAllocCont)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable); +DECLASM(int) UNWIND_WRAP(RTR0MemObjEnterPhys)(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb); +DECLASM(int) UNWIND_WRAP(RTR0MemObjLockUser)(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb, RTR0PROCESS R0Process); +DECLASM(int) UNWIND_WRAP(RTR0MemObjMapKernel)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt); +DECLASM(int) UNWIND_WRAP(RTR0MemObjMapKernelEx)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt, size_t offSub, size_t cbSub); +DECLASM(int) UNWIND_WRAP(RTR0MemObjMapUser)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process); +/*DECLASM(void *) UNWIND_WRAP(RTR0MemObjAddress)(RTR0MEMOBJ MemObj); - not necessary */ +/*DECLASM(RTR3PTR) UNWIND_WRAP(RTR0MemObjAddressR3)(RTR0MEMOBJ MemObj); - not necessary */ +/*DECLASM(size_t) UNWIND_WRAP(RTR0MemObjSize)(RTR0MEMOBJ MemObj); - not necessary */ +/*DECLASM(bool) UNWIND_WRAP(RTR0MemObjIsMapping)(RTR0MEMOBJ MemObj); - not necessary */ +/*DECLASM(RTHCPHYS) UNWIND_WRAP(RTR0MemObjGetPagePhysAddr)(RTR0MEMOBJ MemObj, size_t iPage); - not necessary */ +DECLASM(int) UNWIND_WRAP(RTR0MemObjFree)(RTR0MEMOBJ MemObj, bool fFreeMappings); +/* RTProcSelf - not necessary */ +/* RTR0ProcHandleSelf - not necessary */ +DECLASM(int) UNWIND_WRAP(RTSemFastMutexCreate)(PRTSEMFASTMUTEX pMutexSem); +DECLASM(int) UNWIND_WRAP(RTSemFastMutexDestroy)(RTSEMFASTMUTEX MutexSem); +DECLASM(int) UNWIND_WRAP(RTSemFastMutexRequest)(RTSEMFASTMUTEX MutexSem); +DECLASM(int) UNWIND_WRAP(RTSemFastMutexRelease)(RTSEMFASTMUTEX MutexSem); +DECLASM(int) UNWIND_WRAP(RTSemEventCreate)(PRTSEMEVENT pEventSem); +DECLASM(int) UNWIND_WRAP(RTSemEventSignal)(RTSEMEVENT EventSem); +DECLASM(int) UNWIND_WRAP(RTSemEventWait)(RTSEMEVENT EventSem, unsigned cMillies); +DECLASM(int) UNWIND_WRAP(RTSemEventWaitNoResume)(RTSEMEVENT EventSem, unsigned cMillies); +DECLASM(int) UNWIND_WRAP(RTSemEventDestroy)(RTSEMEVENT EventSem); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiCreate)(PRTSEMEVENTMULTI pEventMultiSem); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiSignal)(RTSEMEVENTMULTI EventMultiSem); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiReset)(RTSEMEVENTMULTI EventMultiSem); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiWait)(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiWaitNoResume)(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies); +DECLASM(int) UNWIND_WRAP(RTSemEventMultiDestroy)(RTSEMEVENTMULTI EventMultiSem); +DECLASM(int) UNWIND_WRAP(RTSpinlockCreate)(PRTSPINLOCK pSpinlock); +DECLASM(int) UNWIND_WRAP(RTSpinlockDestroy)(RTSPINLOCK Spinlock); +DECLASM(void) UNWIND_WRAP(RTSpinlockAcquire)(RTSPINLOCK Spinlock, PRTSPINLOCKTMP pTmp); +DECLASM(void) UNWIND_WRAP(RTSpinlockRelease)(RTSPINLOCK Spinlock, PRTSPINLOCKTMP pTmp); +DECLASM(void) UNWIND_WRAP(RTSpinlockAcquireNoInts)(RTSPINLOCK Spinlock, PRTSPINLOCKTMP pTmp); +DECLASM(void) UNWIND_WRAP(RTSpinlockReleaseNoInts)(RTSPINLOCK Spinlock, PRTSPINLOCKTMP pTmp); +/* RTTimeNanoTS - not necessary */ +/* RTTimeMilliTS - not necessary */ +/* RTTimeSystemNanoTS - not necessary */ +/* RTTimeSystemMilliTS - not necessary */ +/* RTThreadNativeSelf - not necessary */ +DECLASM(int) UNWIND_WRAP(RTThreadSleep)(unsigned cMillies); +DECLASM(bool) UNWIND_WRAP(RTThreadYield)(void); +#if 0 +/* RTThreadSelf - not necessary */ +DECLASM(int) UNWIND_WRAP(RTThreadCreate)(PRTTHREAD pThread, PFNRTTHREAD pfnThread, void *pvUser, size_t cbStack, + RTTHREADTYPE enmType, unsigned fFlags, const char *pszName); +DECLASM(RTNATIVETHREAD) UNWIND_WRAP(RTThreadGetNative)(RTTHREAD Thread); +DECLASM(int) UNWIND_WRAP(RTThreadWait)(RTTHREAD Thread, unsigned cMillies, int *prc); +DECLASM(int) UNWIND_WRAP(RTThreadWaitNoResume)(RTTHREAD Thread, unsigned cMillies, int *prc); +DECLASM(const char *) UNWIND_WRAP(RTThreadGetName)(RTTHREAD Thread); +DECLASM(const char *) UNWIND_WRAP(RTThreadSelfName)(void); +DECLASM(RTTHREADTYPE) UNWIND_WRAP(RTThreadGetType)(RTTHREAD Thread); +DECLASM(int) UNWIND_WRAP(RTThreadUserSignal)(RTTHREAD Thread); +DECLASM(int) UNWIND_WRAP(RTThreadUserReset)(RTTHREAD Thread); +DECLASM(int) UNWIND_WRAP(RTThreadUserWait)(RTTHREAD Thread, unsigned cMillies); +DECLASM(int) UNWIND_WRAP(RTThreadUserWaitNoResume)(RTTHREAD Thread, unsigned cMillies); +#endif +/* RTLogDefaultInstance - a bit of a gamble, but we do not want the overhead! */ +/* RTMpCpuId - not necessary */ +/* RTMpCpuIdFromSetIndex - not necessary */ +/* RTMpCpuIdToSetIndex - not necessary */ +/* RTMpIsCpuPossible - not necessary */ +/* RTMpGetCount - not necessary */ +/* RTMpGetMaxCpuId - not necessary */ +/* RTMpGetOnlineCount - not necessary */ +/* RTMpGetOnlineSet - not necessary */ +/* RTMpGetSet - not necessary */ +/* RTMpIsCpuOnline - not necessary */ +DECLASM(int) UNWIND_WRAP(RTMpOnAll)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); +DECLASM(int) UNWIND_WRAP(RTMpOnOthers)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); +DECLASM(int) UNWIND_WRAP(RTMpOnSpecific)(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); +DECLASM(int) UNWIND_WRAP(RTMpIsCpuWorkPending)(void); +/* RTLogRelDefaultInstance - not necessary. */ +DECLASM(int) UNWIND_WRAP(RTLogSetDefaultInstanceThread)(PRTLOGGER pLogger, uintptr_t uKey); +/* RTLogLogger - can't wrap this buster. */ +/* RTLogLoggerEx - can't wrap this buster. */ +DECLASM(void) UNWIND_WRAP(RTLogLoggerExV)(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args); +/* RTLogPrintf - can't wrap this buster. */ /** @todo provide va_list log wrappers in RuntimeR0. */ +DECLASM(void) UNWIND_WRAP(RTLogPrintfV)(const char *pszFormat, va_list args); +DECLASM(void) UNWIND_WRAP(AssertMsg1)(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction); +/* AssertMsg2 - can't wrap this buster. */ +#endif /* RT_WITH_W64_UNWIND_HACK */ + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Array of the R0 SUP API. + */ +static SUPFUNC g_aFunctions[] = +{ + /* name function */ + /* Entries with absolute addresses determined at runtime, fixup + code makes ugly ASSUMPTIONS about the order here: */ + { "SUPR0AbsIs64bit", (void *)0 }, + { "SUPR0Abs64bitKernelCS", (void *)0 }, + { "SUPR0Abs64bitKernelSS", (void *)0 }, + { "SUPR0Abs64bitKernelDS", (void *)0 }, + { "SUPR0AbsKernelCS", (void *)0 }, + { "SUPR0AbsKernelSS", (void *)0 }, + { "SUPR0AbsKernelDS", (void *)0 }, + { "SUPR0AbsKernelES", (void *)0 }, + { "SUPR0AbsKernelFS", (void *)0 }, + { "SUPR0AbsKernelGS", (void *)0 }, + /* Normal function pointers: */ + { "SUPR0ComponentRegisterFactory", (void *)UNWIND_WRAP(SUPR0ComponentRegisterFactory) }, + { "SUPR0ComponentDeregisterFactory", (void *)UNWIND_WRAP(SUPR0ComponentDeregisterFactory) }, + { "SUPR0ComponentQueryFactory", (void *)UNWIND_WRAP(SUPR0ComponentQueryFactory) }, + { "SUPR0ObjRegister", (void *)UNWIND_WRAP(SUPR0ObjRegister) }, + { "SUPR0ObjAddRef", (void *)UNWIND_WRAP(SUPR0ObjAddRef) }, + { "SUPR0ObjAddRefEx", (void *)UNWIND_WRAP(SUPR0ObjAddRefEx) }, + { "SUPR0ObjRelease", (void *)UNWIND_WRAP(SUPR0ObjRelease) }, + { "SUPR0ObjVerifyAccess", (void *)UNWIND_WRAP(SUPR0ObjVerifyAccess) }, + { "SUPR0LockMem", (void *)UNWIND_WRAP(SUPR0LockMem) }, + { "SUPR0UnlockMem", (void *)UNWIND_WRAP(SUPR0UnlockMem) }, + { "SUPR0ContAlloc", (void *)UNWIND_WRAP(SUPR0ContAlloc) }, + { "SUPR0ContFree", (void *)UNWIND_WRAP(SUPR0ContFree) }, + { "SUPR0LowAlloc", (void *)UNWIND_WRAP(SUPR0LowAlloc) }, + { "SUPR0LowFree", (void *)UNWIND_WRAP(SUPR0LowFree) }, + { "SUPR0MemAlloc", (void *)UNWIND_WRAP(SUPR0MemAlloc) }, + { "SUPR0MemGetPhys", (void *)UNWIND_WRAP(SUPR0MemGetPhys) }, + { "SUPR0MemFree", (void *)UNWIND_WRAP(SUPR0MemFree) }, + { "SUPR0PageAlloc", (void *)UNWIND_WRAP(SUPR0PageAlloc) }, + { "SUPR0PageFree", (void *)UNWIND_WRAP(SUPR0PageFree) }, + { "SUPR0Printf", (void *)SUPR0Printf }, /** @todo needs wrapping? */ + { "SUPR0GetPagingMode", (void *)UNWIND_WRAP(SUPR0GetPagingMode) }, + { "SUPR0EnableVTx", (void *)SUPR0EnableVTx }, + { "RTMemAlloc", (void *)UNWIND_WRAP(RTMemAlloc) }, + { "RTMemAllocZ", (void *)UNWIND_WRAP(RTMemAllocZ) }, + { "RTMemFree", (void *)UNWIND_WRAP(RTMemFree) }, + /*{ "RTMemDup", (void *)UNWIND_WRAP(RTMemDup) }, + { "RTMemDupEx", (void *)UNWIND_WRAP(RTMemDupEx) },*/ + { "RTMemRealloc", (void *)UNWIND_WRAP(RTMemRealloc) }, + { "RTR0MemObjAllocLow", (void *)UNWIND_WRAP(RTR0MemObjAllocLow) }, + { "RTR0MemObjAllocPage", (void *)UNWIND_WRAP(RTR0MemObjAllocPage) }, + { "RTR0MemObjAllocPhys", (void *)UNWIND_WRAP(RTR0MemObjAllocPhys) }, + { "RTR0MemObjAllocPhysNC", (void *)UNWIND_WRAP(RTR0MemObjAllocPhysNC) }, + { "RTR0MemObjAllocCont", (void *)UNWIND_WRAP(RTR0MemObjAllocCont) }, + { "RTR0MemObjEnterPhys", (void *)UNWIND_WRAP(RTR0MemObjEnterPhys) }, + { "RTR0MemObjLockUser", (void *)UNWIND_WRAP(RTR0MemObjLockUser) }, + { "RTR0MemObjMapKernel", (void *)UNWIND_WRAP(RTR0MemObjMapKernel) }, + { "RTR0MemObjMapKernelEx", (void *)UNWIND_WRAP(RTR0MemObjMapKernelEx) }, + { "RTR0MemObjMapUser", (void *)UNWIND_WRAP(RTR0MemObjMapUser) }, + { "RTR0MemObjAddress", (void *)RTR0MemObjAddress }, + { "RTR0MemObjAddressR3", (void *)RTR0MemObjAddressR3 }, + { "RTR0MemObjSize", (void *)RTR0MemObjSize }, + { "RTR0MemObjIsMapping", (void *)RTR0MemObjIsMapping }, + { "RTR0MemObjGetPagePhysAddr", (void *)RTR0MemObjGetPagePhysAddr }, + { "RTR0MemObjFree", (void *)UNWIND_WRAP(RTR0MemObjFree) }, +/* These don't work yet on linux - use fast mutexes! + { "RTSemMutexCreate", (void *)RTSemMutexCreate }, + { "RTSemMutexRequest", (void *)RTSemMutexRequest }, + { "RTSemMutexRelease", (void *)RTSemMutexRelease }, + { "RTSemMutexDestroy", (void *)RTSemMutexDestroy }, +*/ + { "RTProcSelf", (void *)RTProcSelf }, + { "RTR0ProcHandleSelf", (void *)RTR0ProcHandleSelf }, + { "RTSemFastMutexCreate", (void *)UNWIND_WRAP(RTSemFastMutexCreate) }, + { "RTSemFastMutexDestroy", (void *)UNWIND_WRAP(RTSemFastMutexDestroy) }, + { "RTSemFastMutexRequest", (void *)UNWIND_WRAP(RTSemFastMutexRequest) }, + { "RTSemFastMutexRelease", (void *)UNWIND_WRAP(RTSemFastMutexRelease) }, + { "RTSemEventCreate", (void *)UNWIND_WRAP(RTSemEventCreate) }, + { "RTSemEventSignal", (void *)UNWIND_WRAP(RTSemEventSignal) }, + { "RTSemEventWait", (void *)UNWIND_WRAP(RTSemEventWait) }, + { "RTSemEventWaitNoResume", (void *)UNWIND_WRAP(RTSemEventWaitNoResume) }, + { "RTSemEventDestroy", (void *)UNWIND_WRAP(RTSemEventDestroy) }, + { "RTSemEventMultiCreate", (void *)UNWIND_WRAP(RTSemEventMultiCreate) }, + { "RTSemEventMultiSignal", (void *)UNWIND_WRAP(RTSemEventMultiSignal) }, + { "RTSemEventMultiReset", (void *)UNWIND_WRAP(RTSemEventMultiReset) }, + { "RTSemEventMultiWait", (void *)UNWIND_WRAP(RTSemEventMultiWait) }, + { "RTSemEventMultiWaitNoResume", (void *)UNWIND_WRAP(RTSemEventMultiWaitNoResume) }, + { "RTSemEventMultiDestroy", (void *)UNWIND_WRAP(RTSemEventMultiDestroy) }, + { "RTSpinlockCreate", (void *)UNWIND_WRAP(RTSpinlockCreate) }, + { "RTSpinlockDestroy", (void *)UNWIND_WRAP(RTSpinlockDestroy) }, + { "RTSpinlockAcquire", (void *)UNWIND_WRAP(RTSpinlockAcquire) }, + { "RTSpinlockRelease", (void *)UNWIND_WRAP(RTSpinlockRelease) }, + { "RTSpinlockAcquireNoInts", (void *)UNWIND_WRAP(RTSpinlockAcquireNoInts) }, + { "RTSpinlockReleaseNoInts", (void *)UNWIND_WRAP(RTSpinlockReleaseNoInts) }, + { "RTTimeNanoTS", (void *)RTTimeNanoTS }, + { "RTTimeMillieTS", (void *)RTTimeMilliTS }, + { "RTTimeSystemNanoTS", (void *)RTTimeSystemNanoTS }, + { "RTTimeSystemMillieTS", (void *)RTTimeSystemMilliTS }, + { "RTThreadNativeSelf", (void *)RTThreadNativeSelf }, + { "RTThreadSleep", (void *)UNWIND_WRAP(RTThreadSleep) }, + { "RTThreadYield", (void *)UNWIND_WRAP(RTThreadYield) }, +#if 0 /* Thread APIs, Part 2. */ + { "RTThreadSelf", (void *)UNWIND_WRAP(RTThreadSelf) }, + { "RTThreadCreate", (void *)UNWIND_WRAP(RTThreadCreate) }, /** @todo need to wrap the callback */ + { "RTThreadGetNative", (void *)UNWIND_WRAP(RTThreadGetNative) }, + { "RTThreadWait", (void *)UNWIND_WRAP(RTThreadWait) }, + { "RTThreadWaitNoResume", (void *)UNWIND_WRAP(RTThreadWaitNoResume) }, + { "RTThreadGetName", (void *)UNWIND_WRAP(RTThreadGetName) }, + { "RTThreadSelfName", (void *)UNWIND_WRAP(RTThreadSelfName) }, + { "RTThreadGetType", (void *)UNWIND_WRAP(RTThreadGetType) }, + { "RTThreadUserSignal", (void *)UNWIND_WRAP(RTThreadUserSignal) }, + { "RTThreadUserReset", (void *)UNWIND_WRAP(RTThreadUserReset) }, + { "RTThreadUserWait", (void *)UNWIND_WRAP(RTThreadUserWait) }, + { "RTThreadUserWaitNoResume", (void *)UNWIND_WRAP(RTThreadUserWaitNoResume) }, +#endif + { "RTLogDefaultInstance", (void *)RTLogDefaultInstance }, + { "RTMpCpuId", (void *)RTMpCpuId }, + { "RTMpCpuIdFromSetIndex", (void *)RTMpCpuIdFromSetIndex }, + { "RTMpCpuIdToSetIndex", (void *)RTMpCpuIdToSetIndex }, + { "RTMpIsCpuPossible", (void *)RTMpIsCpuPossible }, + { "RTMpGetCount", (void *)RTMpGetCount }, + { "RTMpGetMaxCpuId", (void *)RTMpGetMaxCpuId }, + { "RTMpGetOnlineCount", (void *)RTMpGetOnlineCount }, + { "RTMpGetOnlineSet", (void *)RTMpGetOnlineSet }, + { "RTMpGetSet", (void *)RTMpGetSet }, + { "RTMpIsCpuOnline", (void *)RTMpIsCpuOnline }, + { "RTMpIsCpuWorkPending", (void *)UNWIND_WRAP(RTMpIsCpuWorkPending) }, + { "RTMpOnAll", (void *)UNWIND_WRAP(RTMpOnAll) }, + { "RTMpOnOthers", (void *)UNWIND_WRAP(RTMpOnOthers) }, + { "RTMpOnSpecific", (void *)UNWIND_WRAP(RTMpOnSpecific) }, + { "RTPowerNotificationRegister", (void *)RTPowerNotificationRegister }, + { "RTPowerNotificationDeregister", (void *)RTPowerNotificationDeregister }, + { "RTLogRelDefaultInstance", (void *)RTLogRelDefaultInstance }, + { "RTLogSetDefaultInstanceThread", (void *)UNWIND_WRAP(RTLogSetDefaultInstanceThread) }, + { "RTLogLogger", (void *)RTLogLogger }, /** @todo remove this */ + { "RTLogLoggerEx", (void *)RTLogLoggerEx }, /** @todo remove this */ + { "RTLogLoggerExV", (void *)UNWIND_WRAP(RTLogLoggerExV) }, + { "RTLogPrintf", (void *)RTLogPrintf }, /** @todo remove this */ + { "RTLogPrintfV", (void *)UNWIND_WRAP(RTLogPrintfV) }, + { "AssertMsg1", (void *)UNWIND_WRAP(AssertMsg1) }, + { "AssertMsg2", (void *)AssertMsg2 }, /** @todo replace this by RTAssertMsg2V */ +#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) + { "RTR0AssertPanicSystem", (void *)RTR0AssertPanicSystem }, +#endif +#if defined(RT_OS_DARWIN) + { "RTAssertMsg1", (void *)RTAssertMsg1 }, + { "RTAssertMsg2", (void *)RTAssertMsg2 }, + { "RTAssertMsg2V", (void *)RTAssertMsg2V }, +#endif +}; + +#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) +/** + * Drag in the rest of IRPT since we share it with the + * rest of the kernel modules on darwin. + */ +PFNRT g_apfnVBoxDrvIPRTDeps[] = +{ + (PFNRT)RTCrc32, + (PFNRT)RTErrConvertFromErrno, + (PFNRT)RTNetIPv4IsHdrValid, + (PFNRT)RTNetIPv4TCPChecksum, + (PFNRT)RTNetIPv4UDPChecksum, + (PFNRT)RTUuidCompare, + (PFNRT)RTUuidCompareStr, + (PFNRT)RTUuidFromStr, + NULL +}; +#endif /* RT_OS_DARWIN || RT_OS_SOLARIS */ + + +/** + * Initializes the device extentsion structure. + * + * @returns IPRT status code. + * @param pDevExt The device extension to initialize. + */ +int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt) +{ + int rc; + +#ifdef SUPDRV_WITH_RELEASE_LOGGER + /* + * Create the release log. + */ + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, + RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL); + if (RT_SUCCESS(rc)) + RTLogRelSetDefaultInstance(pRelLogger); +#endif + + /* + * Initialize it. + */ + memset(pDevExt, 0, sizeof(*pDevExt)); + rc = RTSpinlockCreate(&pDevExt->Spinlock); + if (!rc) + { + rc = RTSemFastMutexCreate(&pDevExt->mtxLdr); + if (!rc) + { + rc = RTSemFastMutexCreate(&pDevExt->mtxComponentFactory); + if (!rc) + { + rc = RTSemFastMutexCreate(&pDevExt->mtxGip); + if (!rc) + { + rc = supdrvGipCreate(pDevExt); + if (RT_SUCCESS(rc)) + { + pDevExt->u32Cookie = BIRD; /** @todo make this random? */ + + /* + * Fixup the absolute symbols. + * + * Because of the table indexing assumptions we'll do #ifdef orgy here rather + * than distributing this to OS specific files. At least for now. + */ +#ifdef RT_OS_DARWIN + if (SUPR0GetPagingMode() >= SUPPAGINGMODE_AMD64) + { + g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */ + g_aFunctions[1].pfn = (void *)0x80; /* SUPR0Abs64bitKernelCS - KERNEL64_CS, seg.h */ + g_aFunctions[2].pfn = (void *)0x88; /* SUPR0Abs64bitKernelSS - KERNEL64_SS, seg.h */ + g_aFunctions[3].pfn = (void *)0x88; /* SUPR0Abs64bitKernelDS - KERNEL64_SS, seg.h */ + } + else + g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0; + g_aFunctions[4].pfn = (void *)0x08; /* SUPR0AbsKernelCS - KERNEL_CS, seg.h */ + g_aFunctions[5].pfn = (void *)0x10; /* SUPR0AbsKernelSS - KERNEL_DS, seg.h */ + g_aFunctions[6].pfn = (void *)0x10; /* SUPR0AbsKernelDS - KERNEL_DS, seg.h */ + g_aFunctions[7].pfn = (void *)0x10; /* SUPR0AbsKernelES - KERNEL_DS, seg.h */ +#else +# if ARCH_BITS == 64 + g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */ + g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */ + g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */ + g_aFunctions[3].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0Abs64bitKernelDS */ +# elif ARCH_BITS == 32 + g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0; +# endif + g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */ + g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */ + g_aFunctions[6].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0AbsKernelDS */ + g_aFunctions[7].pfn = (void *)(uintptr_t)ASMGetES(); /* SUPR0AbsKernelES */ +#endif + g_aFunctions[8].pfn = (void *)(uintptr_t)ASMGetFS(); /* SUPR0AbsKernelFS */ + g_aFunctions[9].pfn = (void *)(uintptr_t)ASMGetGS(); /* SUPR0AbsKernelGS */ + return VINF_SUCCESS; + } + + RTSemFastMutexDestroy(pDevExt->mtxGip); + pDevExt->mtxGip = NIL_RTSEMFASTMUTEX; + } + RTSemFastMutexDestroy(pDevExt->mtxComponentFactory); + pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX; + } + RTSemFastMutexDestroy(pDevExt->mtxLdr); + pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX; + } + RTSpinlockDestroy(pDevExt->Spinlock); + pDevExt->Spinlock = NIL_RTSPINLOCK; + } +#ifdef SUPDRV_WITH_RELEASE_LOGGER + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif + + return rc; +} + + +/** + * Delete the device extension (e.g. cleanup members). + * + * @param pDevExt The device extension to delete. + */ +void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt) +{ + PSUPDRVOBJ pObj; + PSUPDRVUSAGE pUsage; + + /* + * Kill mutexes and spinlocks. + */ + RTSemFastMutexDestroy(pDevExt->mtxGip); + pDevExt->mtxGip = NIL_RTSEMFASTMUTEX; + RTSemFastMutexDestroy(pDevExt->mtxLdr); + pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX; + RTSpinlockDestroy(pDevExt->Spinlock); + pDevExt->Spinlock = NIL_RTSPINLOCK; + RTSemFastMutexDestroy(pDevExt->mtxComponentFactory); + pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX; + + /* + * Free lists. + */ + /* objects. */ + pObj = pDevExt->pObjs; +#if !defined(DEBUG_bird) || !defined(RT_OS_LINUX) /* breaks unloading, temporary, remove me! */ + Assert(!pObj); /* (can trigger on forced unloads) */ +#endif + pDevExt->pObjs = NULL; + while (pObj) + { + void *pvFree = pObj; + pObj = pObj->pNext; + RTMemFree(pvFree); + } + + /* usage records. */ + pUsage = pDevExt->pUsageFree; + pDevExt->pUsageFree = NULL; + while (pUsage) + { + void *pvFree = pUsage; + pUsage = pUsage->pNext; + RTMemFree(pvFree); + } + + /* kill the GIP. */ + supdrvGipDestroy(pDevExt); + +#ifdef SUPDRV_WITH_RELEASE_LOGGER + /* destroy the loggers. */ + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif +} + + +/** + * Create session. + * + * @returns IPRT status code. + * @param pDevExt Device extension. + * @param fUser Flag indicating whether this is a user or kernel session. + * @param ppSession Where to store the pointer to the session data. + */ +int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, PSUPDRVSESSION *ppSession) +{ + /* + * Allocate memory for the session data. + */ + int rc = VERR_NO_MEMORY; + PSUPDRVSESSION pSession = *ppSession = (PSUPDRVSESSION)RTMemAllocZ(sizeof(*pSession)); + if (pSession) + { + /* Initialize session data. */ + rc = RTSpinlockCreate(&pSession->Spinlock); + if (!rc) + { + Assert(pSession->Spinlock != NIL_RTSPINLOCK); + pSession->pDevExt = pDevExt; + pSession->u32Cookie = BIRD_INV; + /*pSession->pLdrUsage = NULL; + pSession->pVM = NULL; + pSession->pUsage = NULL; + pSession->pGip = NULL; + pSession->fGipReferenced = false; + pSession->Bundle.cUsed = 0; */ + pSession->Uid = NIL_RTUID; + pSession->Gid = NIL_RTGID; + if (fUser) + { + pSession->Process = RTProcSelf(); + pSession->R0Process = RTR0ProcHandleSelf(); + } + else + { + pSession->Process = NIL_RTPROCESS; + pSession->R0Process = NIL_RTR0PROCESS; + } + + LogFlow(("Created session %p initial cookie=%#x\n", pSession, pSession->u32Cookie)); + return VINF_SUCCESS; + } + + RTMemFree(pSession); + *ppSession = NULL; + Log(("Failed to create spinlock, rc=%d!\n", rc)); + } + + return rc; +} + + +/** + * Shared code for cleaning up a session. + * + * @param pDevExt Device extension. + * @param pSession Session data. + * This data will be freed by this routine. + */ +void VBOXCALL supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession) +{ + /* + * Cleanup the session first. + */ + supdrvCleanupSession(pDevExt, pSession); + + /* + * Free the rest of the session stuff. + */ + RTSpinlockDestroy(pSession->Spinlock); + pSession->Spinlock = NIL_RTSPINLOCK; + pSession->pDevExt = NULL; + RTMemFree(pSession); + LogFlow(("supdrvCloseSession: returns\n")); +} + + +/** + * Shared code for cleaning up a session (but not quite freeing it). + * + * This is primarily intended for MAC OS X where we have to clean up the memory + * stuff before the file handle is closed. + * + * @param pDevExt Device extension. + * @param pSession Session data. + * This data will be freed by this routine. + */ +void VBOXCALL supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession) +{ + PSUPDRVBUNDLE pBundle; + LogFlow(("supdrvCleanupSession: pSession=%p\n", pSession)); + + /* + * Remove logger instances related to this session. + */ + RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pSession); + + /* + * Release object references made in this session. + * In theory there should be noone racing us in this session. + */ + Log2(("release objects - start\n")); + if (pSession->pUsage) + { + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + PSUPDRVUSAGE pUsage; + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + + while ((pUsage = pSession->pUsage) != NULL) + { + PSUPDRVOBJ pObj = pUsage->pObj; + pSession->pUsage = pUsage->pNext; + + AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage)); + if (pUsage->cUsage < pObj->cUsage) + { + pObj->cUsage -= pUsage->cUsage; + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + } + else + { + /* Destroy the object and free the record. */ + if (pDevExt->pObjs == pObj) + pDevExt->pObjs = pObj->pNext; + else + { + PSUPDRVOBJ pObjPrev; + for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext) + if (pObjPrev->pNext == pObj) + { + pObjPrev->pNext = pObj->pNext; + break; + } + Assert(pObjPrev); + } + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + Log(("supdrvCleanupSession: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n", + pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor)); + if (pObj->pfnDestructor) +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapObjDestructor((PFNRT)pObj->pfnDestructor, pObj, pObj->pvUser1, pObj->pvUser2); +#else + pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2); +#endif + RTMemFree(pObj); + } + + /* free it and continue. */ + RTMemFree(pUsage); + + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + } + + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + AssertMsg(!pSession->pUsage, ("Some buster reregistered an object during desturction!\n")); + } + Log2(("release objects - done\n")); + + /* + * Release memory allocated in the session. + * + * We do not serialize this as we assume that the application will + * not allocated memory while closing the file handle object. + */ + Log2(("freeing memory:\n")); + pBundle = &pSession->Bundle; + while (pBundle) + { + PSUPDRVBUNDLE pToFree; + unsigned i; + + /* + * Check and unlock all entries in the bundle. + */ + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if (pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ) + { + int rc; + Log2(("eType=%d pvR0=%p pvR3=%p cb=%ld\n", pBundle->aMem[i].eType, RTR0MemObjAddress(pBundle->aMem[i].MemObj), + (void *)RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3), (long)RTR0MemObjSize(pBundle->aMem[i].MemObj))); + if (pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ) + { + rc = RTR0MemObjFree(pBundle->aMem[i].MapObjR3, false); + AssertRC(rc); /** @todo figure out how to handle this. */ + pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ; + } + rc = RTR0MemObjFree(pBundle->aMem[i].MemObj, true /* fFreeMappings */); + AssertRC(rc); /** @todo figure out how to handle this. */ + pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ; + pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED; + } + } + + /* + * Advance and free previous bundle. + */ + pToFree = pBundle; + pBundle = pBundle->pNext; + + pToFree->pNext = NULL; + pToFree->cUsed = 0; + if (pToFree != &pSession->Bundle) + RTMemFree(pToFree); + } + Log2(("freeing memory - done\n")); + + /* + * Deregister component factories. + */ + RTSemFastMutexRequest(pDevExt->mtxComponentFactory); + Log2(("deregistering component factories:\n")); + if (pDevExt->pComponentFactoryHead) + { + PSUPDRVFACTORYREG pPrev = NULL; + PSUPDRVFACTORYREG pCur = pDevExt->pComponentFactoryHead; + while (pCur) + { + if (pCur->pSession == pSession) + { + /* unlink it */ + PSUPDRVFACTORYREG pNext = pCur->pNext; + if (pPrev) + pPrev->pNext = pNext; + else + pDevExt->pComponentFactoryHead = pNext; + + /* free it */ + pCur->pNext = NULL; + pCur->pSession = NULL; + pCur->pFactory = NULL; + RTMemFree(pCur); + + /* next */ + pCur = pNext; + } + else + { + /* next */ + pPrev = pCur; + pCur = pCur->pNext; + } + } + } + RTSemFastMutexRelease(pDevExt->mtxComponentFactory); + Log2(("deregistering component factories - done\n")); + + /* + * Loaded images needs to be dereferenced and possibly freed up. + */ + RTSemFastMutexRequest(pDevExt->mtxLdr); + Log2(("freeing images:\n")); + if (pSession->pLdrUsage) + { + PSUPDRVLDRUSAGE pUsage = pSession->pLdrUsage; + pSession->pLdrUsage = NULL; + while (pUsage) + { + void *pvFree = pUsage; + PSUPDRVLDRIMAGE pImage = pUsage->pImage; + if (pImage->cUsage > pUsage->cUsage) + pImage->cUsage -= pUsage->cUsage; + else + supdrvLdrFree(pDevExt, pImage); + pUsage->pImage = NULL; + pUsage = pUsage->pNext; + RTMemFree(pvFree); + } + } + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log2(("freeing images - done\n")); + + /* + * Unmap the GIP. + */ + Log2(("umapping GIP:\n")); + if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ) + { + SUPR0GipUnmap(pSession); + pSession->fGipReferenced = 0; + } + Log2(("umapping GIP - done\n")); +} + + +/** + * Fast path I/O Control worker. + * + * @returns VBox status code that should be passed down to ring-3 unchanged. + * @param uIOCtl Function number. + * @param idCpu VMCPU id. + * @param pDevExt Device extention. + * @param pSession Session data. + */ +int VBOXCALL supdrvIOCtlFast(uintptr_t uIOCtl, unsigned idCpu, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession) +{ + /* + * We check the two prereqs after doing this only to allow the compiler to optimize things better. + */ + if (RT_LIKELY(pSession->pVM && pDevExt->pfnVMMR0EntryFast)) + { + switch (uIOCtl) + { + case SUP_IOCTL_FAST_DO_RAW_RUN: +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapVMMR0EntryFast((PFNRT)pDevExt->pfnVMMR0EntryFast, pSession->pVM, idCpu, SUP_VMMR0_DO_RAW_RUN); +#else + pDevExt->pfnVMMR0EntryFast(pSession->pVM, idCpu, SUP_VMMR0_DO_RAW_RUN); +#endif + break; + case SUP_IOCTL_FAST_DO_HWACC_RUN: +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapVMMR0EntryFast((PFNRT)pDevExt->pfnVMMR0EntryFast, pSession->pVM, idCpu, SUP_VMMR0_DO_HWACC_RUN); +#else + pDevExt->pfnVMMR0EntryFast(pSession->pVM, idCpu, SUP_VMMR0_DO_HWACC_RUN); +#endif + break; + case SUP_IOCTL_FAST_DO_NOP: +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapVMMR0EntryFast((PFNRT)pDevExt->pfnVMMR0EntryFast, pSession->pVM, idCpu, SUP_VMMR0_DO_NOP); +#else + pDevExt->pfnVMMR0EntryFast(pSession->pVM, idCpu, SUP_VMMR0_DO_NOP); +#endif + break; + default: + return VERR_INTERNAL_ERROR; + } + return VINF_SUCCESS; + } + return VERR_INTERNAL_ERROR; +} + + +/** + * Helper for supdrvIOCtl. Check if pszStr contains any character of pszChars. + * We would use strpbrk here if this function would be contained in the RedHat kABI white + * list, see http://www.kerneldrivers.org/RHEL5. + * + * @return 1 if pszStr does contain any character of pszChars, 0 otherwise. + * @param pszStr String to check + * @param pszChars Character set + */ +static int supdrvCheckInvalidChar(const char *pszStr, const char *pszChars) +{ + int chCur; + while ((chCur = *pszStr++) != '\0') + { + int ch; + const char *psz = pszChars; + while ((ch = *psz++) != '\0') + if (ch == chCur) + return 1; + + } + return 0; +} + + +/** + * I/O Control worker. + * + * @returns 0 on success. + * @returns VERR_INVALID_PARAMETER if the request is invalid. + * + * @param uIOCtl Function number. + * @param pDevExt Device extention. + * @param pSession Session data. + * @param pReqHdr The request header. + */ +int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr) +{ + /* + * Validate the request. + */ + /* this first check could probably be omitted as its also done by the OS specific code... */ + if (RT_UNLIKELY( (pReqHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC + || pReqHdr->cbIn < sizeof(*pReqHdr) + || pReqHdr->cbOut < sizeof(*pReqHdr))) + { + OSDBGPRINT(("vboxdrv: Bad ioctl request header; cbIn=%#lx cbOut=%#lx fFlags=%#lx\n", + (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->fFlags)); + return VERR_INVALID_PARAMETER; + } + if (RT_UNLIKELY(uIOCtl == SUP_IOCTL_COOKIE)) + { + if (pReqHdr->u32Cookie != SUPCOOKIE_INITIAL_COOKIE) + { + OSDBGPRINT(("SUP_IOCTL_COOKIE: bad cookie %#lx\n", (long)pReqHdr->u32Cookie)); + return VERR_INVALID_PARAMETER; + } + } + else if (RT_UNLIKELY( pReqHdr->u32Cookie != pDevExt->u32Cookie + || pReqHdr->u32SessionCookie != pSession->u32Cookie)) + { + OSDBGPRINT(("vboxdrv: bad cookie %#lx / %#lx.\n", (long)pReqHdr->u32Cookie, (long)pReqHdr->u32SessionCookie)); + return VERR_INVALID_PARAMETER; + } + +/* + * Validation macros + */ +#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \ + do { \ + if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect) || pReqHdr->cbOut != (cbOutExpect))) \ + { \ + OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \ + (long)pReq->Hdr.cbIn, (long)(cbInExpect), (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \ + return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_SIZES(Name) REQ_CHECK_SIZES_EX(Name, Name ## _SIZE_IN, Name ## _SIZE_OUT) + +#define REQ_CHECK_SIZE_IN(Name, cbInExpect) \ + do { \ + if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect))) \ + { \ + OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld.\n", \ + (long)pReq->Hdr.cbIn, (long)(cbInExpect))); \ + return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_SIZE_OUT(Name, cbOutExpect) \ + do { \ + if (RT_UNLIKELY(pReqHdr->cbOut != (cbOutExpect))) \ + { \ + OSDBGPRINT(( #Name ": Invalid input/output sizes. cbOut=%ld expected %ld.\n", \ + (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \ + return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_EXPR(Name, expr) \ + do { \ + if (RT_UNLIKELY(!(expr))) \ + { \ + OSDBGPRINT(( #Name ": %s\n", #expr)); \ + return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_EXPR_FMT(expr, fmt) \ + do { \ + if (RT_UNLIKELY(!(expr))) \ + { \ + OSDBGPRINT( fmt ); \ + return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + + + /* + * The switch. + */ + switch (SUP_CTL_CODE_NO_SIZE(uIOCtl)) + { + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_COOKIE): + { + PSUPCOOKIE pReq = (PSUPCOOKIE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_COOKIE); + if (strncmp(pReq->u.In.szMagic, SUPCOOKIE_MAGIC, sizeof(pReq->u.In.szMagic))) + { + OSDBGPRINT(("SUP_IOCTL_COOKIE: invalid magic %.16s\n", pReq->u.In.szMagic)); + pReq->Hdr.rc = VERR_INVALID_MAGIC; + return 0; + } + +#if 0 + /* + * Call out to the OS specific code and let it do permission checks on the + * client process. + */ + if (!supdrvOSValidateClientProcess(pDevExt, pSession)) + { + pReq->u.Out.u32Cookie = 0xffffffff; + pReq->u.Out.u32SessionCookie = 0xffffffff; + pReq->u.Out.u32SessionVersion = 0xffffffff; + pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION; + pReq->u.Out.pSession = NULL; + pReq->u.Out.cFunctions = 0; + pReq->Hdr.rc = VERR_PERMISSION_DENIED; + return 0; + } +#endif + + /* + * Match the version. + * The current logic is very simple, match the major interface version. + */ + if ( pReq->u.In.u32MinVersion > SUPDRV_IOC_VERSION + || (pReq->u.In.u32MinVersion & 0xffff0000) != (SUPDRV_IOC_VERSION & 0xffff0000)) + { + OSDBGPRINT(("SUP_IOCTL_COOKIE: Version mismatch. Requested: %#x Min: %#x Current: %#x\n", + pReq->u.In.u32ReqVersion, pReq->u.In.u32MinVersion, SUPDRV_IOC_VERSION)); + pReq->u.Out.u32Cookie = 0xffffffff; + pReq->u.Out.u32SessionCookie = 0xffffffff; + pReq->u.Out.u32SessionVersion = 0xffffffff; + pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION; + pReq->u.Out.pSession = NULL; + pReq->u.Out.cFunctions = 0; + pReq->Hdr.rc = VERR_VERSION_MISMATCH; + return 0; + } + + /* + * Fill in return data and be gone. + * N.B. The first one to change SUPDRV_IOC_VERSION shall makes sure that + * u32SessionVersion <= u32ReqVersion! + */ + /** @todo Somehow validate the client and negotiate a secure cookie... */ + pReq->u.Out.u32Cookie = pDevExt->u32Cookie; + pReq->u.Out.u32SessionCookie = pSession->u32Cookie; + pReq->u.Out.u32SessionVersion = SUPDRV_IOC_VERSION; + pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION; + pReq->u.Out.pSession = pSession; + pReq->u.Out.cFunctions = sizeof(g_aFunctions) / sizeof(g_aFunctions[0]); + pReq->Hdr.rc = VINF_SUCCESS; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_QUERY_FUNCS(0)): + { + /* validate */ + PSUPQUERYFUNCS pReq = (PSUPQUERYFUNCS)pReqHdr; + REQ_CHECK_SIZES_EX(SUP_IOCTL_QUERY_FUNCS, SUP_IOCTL_QUERY_FUNCS_SIZE_IN, SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(RT_ELEMENTS(g_aFunctions))); + + /* execute */ + pReq->u.Out.cFunctions = RT_ELEMENTS(g_aFunctions); + memcpy(&pReq->u.Out.aFunctions[0], g_aFunctions, sizeof(g_aFunctions)); + pReq->Hdr.rc = VINF_SUCCESS; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_INSTALL): + { + /* validate */ + PSUPIDTINSTALL pReq = (PSUPIDTINSTALL)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_IDT_INSTALL); + + /* execute */ + pReq->u.Out.u8Idt = 3; + pReq->Hdr.rc = VERR_NOT_SUPPORTED; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_REMOVE): + { + /* validate */ + PSUPIDTREMOVE pReq = (PSUPIDTREMOVE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_IDT_REMOVE); + + /* execute */ + pReq->Hdr.rc = VERR_NOT_SUPPORTED; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_LOCK): + { + /* validate */ + PSUPPAGELOCK pReq = (PSUPPAGELOCK)pReqHdr; + REQ_CHECK_SIZE_IN(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_IN); + REQ_CHECK_SIZE_OUT(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_OUT(pReq->u.In.cPages)); + REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.cPages > 0); + REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.pvR3 >= PAGE_SIZE); + + /* execute */ + pReq->Hdr.rc = SUPR0LockMem(pSession, pReq->u.In.pvR3, pReq->u.In.cPages, &pReq->u.Out.aPages[0]); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_UNLOCK): + { + /* validate */ + PSUPPAGEUNLOCK pReq = (PSUPPAGEUNLOCK)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_PAGE_UNLOCK); + + /* execute */ + pReq->Hdr.rc = SUPR0UnlockMem(pSession, pReq->u.In.pvR3); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_ALLOC): + { + /* validate */ + PSUPCONTALLOC pReq = (PSUPCONTALLOC)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_CONT_ALLOC); + + /* execute */ + pReq->Hdr.rc = SUPR0ContAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.HCPhys); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_FREE): + { + /* validate */ + PSUPCONTFREE pReq = (PSUPCONTFREE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_CONT_FREE); + + /* execute */ + pReq->Hdr.rc = SUPR0ContFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_OPEN): + { + /* validate */ + PSUPLDROPEN pReq = (PSUPLDROPEN)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_LDR_OPEN); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage > 0); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage < _1M*16); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.szName[0]); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, memchr(pReq->u.In.szName, '\0', sizeof(pReq->u.In.szName))); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, !supdrvCheckInvalidChar(pReq->u.In.szName, ";:()[]{}/\\|&*%#@!~`\"'")); + + /* execute */ + pReq->Hdr.rc = supdrvIOCtl_LdrOpen(pDevExt, pSession, pReq); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_LOAD): + { + /* validate */ + PSUPLDRLOAD pReq = (PSUPLDRLOAD)pReqHdr; + REQ_CHECK_EXPR(Name, pReq->Hdr.cbIn >= sizeof(*pReq)); + REQ_CHECK_SIZES_EX(SUP_IOCTL_LDR_LOAD, SUP_IOCTL_LDR_LOAD_SIZE_IN(pReq->u.In.cbImage), SUP_IOCTL_LDR_LOAD_SIZE_OUT); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_LOAD, pReq->u.In.cSymbols <= 16384); + REQ_CHECK_EXPR_FMT( !pReq->u.In.cSymbols + || ( pReq->u.In.offSymbols < pReq->u.In.cbImage + && pReq->u.In.offSymbols + pReq->u.In.cSymbols * sizeof(SUPLDRSYM) <= pReq->u.In.cbImage), + ("SUP_IOCTL_LDR_LOAD: offSymbols=%#lx cSymbols=%#lx cbImage=%#lx\n", (long)pReq->u.In.offSymbols, + (long)pReq->u.In.cSymbols, (long)pReq->u.In.cbImage)); + REQ_CHECK_EXPR_FMT( !pReq->u.In.cbStrTab + || ( pReq->u.In.offStrTab < pReq->u.In.cbImage + && pReq->u.In.offStrTab + pReq->u.In.cbStrTab <= pReq->u.In.cbImage + && pReq->u.In.cbStrTab <= pReq->u.In.cbImage), + ("SUP_IOCTL_LDR_LOAD: offStrTab=%#lx cbStrTab=%#lx cbImage=%#lx\n", (long)pReq->u.In.offStrTab, + (long)pReq->u.In.cbStrTab, (long)pReq->u.In.cbImage)); + + if (pReq->u.In.cSymbols) + { + uint32_t i; + PSUPLDRSYM paSyms = (PSUPLDRSYM)&pReq->u.In.achImage[pReq->u.In.offSymbols]; + for (i = 0; i < pReq->u.In.cSymbols; i++) + { + REQ_CHECK_EXPR_FMT(paSyms[i].offSymbol < pReq->u.In.cbImage, + ("SUP_IOCTL_LDR_LOAD: sym #%ld: symb off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offSymbol, (long)pReq->u.In.cbImage)); + REQ_CHECK_EXPR_FMT(paSyms[i].offName < pReq->u.In.cbStrTab, + ("SUP_IOCTL_LDR_LOAD: sym #%ld: name off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage)); + REQ_CHECK_EXPR_FMT(memchr(&pReq->u.In.achImage[pReq->u.In.offStrTab + paSyms[i].offName], '\0', pReq->u.In.cbStrTab - paSyms[i].offName), + ("SUP_IOCTL_LDR_LOAD: sym #%ld: unterminated name! (%#lx / %#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage)); + } + } + + /* execute */ + pReq->Hdr.rc = supdrvIOCtl_LdrLoad(pDevExt, pSession, pReq); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_FREE): + { + /* validate */ + PSUPLDRFREE pReq = (PSUPLDRFREE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_LDR_FREE); + + /* execute */ + pReq->Hdr.rc = supdrvIOCtl_LdrFree(pDevExt, pSession, pReq); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_GET_SYMBOL): + { + /* validate */ + PSUPLDRGETSYMBOL pReq = (PSUPLDRGETSYMBOL)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_LDR_GET_SYMBOL); + REQ_CHECK_EXPR(SUP_IOCTL_LDR_GET_SYMBOL, memchr(pReq->u.In.szSymbol, '\0', sizeof(pReq->u.In.szSymbol))); + + /* execute */ + pReq->Hdr.rc = supdrvIOCtl_LdrGetSymbol(pDevExt, pSession, pReq); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_VMMR0(0)): + { + /* validate */ + PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)pReqHdr; + Log4(("SUP_IOCTL_CALL_VMMR0: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + + if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_VMMR0_SIZE(0)) + { + REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(0), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0)); + + /* execute */ + if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx)) +#ifdef RT_WITH_W64_UNWIND_HACK + pReq->Hdr.rc = supdrvNtWrapVMMR0EntryEx((PFNRT)pDevExt->pfnVMMR0EntryEx, pReq->u.In.pVMR0, pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg, pSession); +#else + pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg, pSession); +#endif + else + pReq->Hdr.rc = VERR_WRONG_ORDER; + } + else + { + PSUPVMMR0REQHDR pVMMReq = (PSUPVMMR0REQHDR)&pReq->abReqPkt[0]; + REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR)), + ("SUP_IOCTL_CALL_VMMR0: cbIn=%#x < %#lx\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR)))); + REQ_CHECK_EXPR(SUP_IOCTL_CALL_VMMR0, pVMMReq->u32Magic == SUPVMMR0REQHDR_MAGIC); + REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(pVMMReq->cbReq), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(pVMMReq->cbReq)); + + /* execute */ + if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx)) +#ifdef RT_WITH_W64_UNWIND_HACK + pReq->Hdr.rc = supdrvNtWrapVMMR0EntryEx((PFNRT)pDevExt->pfnVMMR0EntryEx, pReq->u.In.pVMR0, pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession); +#else + pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession); +#endif + else + pReq->Hdr.rc = VERR_WRONG_ORDER; + } + + if ( RT_FAILURE(pReq->Hdr.rc) + && pReq->Hdr.rc != VERR_INTERRUPTED + && pReq->Hdr.rc != VERR_TIMEOUT) + Log(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + else + Log4(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GET_PAGING_MODE): + { + /* validate */ + PSUPGETPAGINGMODE pReq = (PSUPGETPAGINGMODE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_GET_PAGING_MODE); + + /* execute */ + pReq->Hdr.rc = VINF_SUCCESS; + pReq->u.Out.enmMode = SUPR0GetPagingMode(); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_ALLOC): + { + /* validate */ + PSUPLOWALLOC pReq = (PSUPLOWALLOC)pReqHdr; + REQ_CHECK_EXPR(SUP_IOCTL_LOW_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_LOW_ALLOC_SIZE_IN); + REQ_CHECK_SIZES_EX(SUP_IOCTL_LOW_ALLOC, SUP_IOCTL_LOW_ALLOC_SIZE_IN, SUP_IOCTL_LOW_ALLOC_SIZE_OUT(pReq->u.In.cPages)); + + /* execute */ + pReq->Hdr.rc = SUPR0LowAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_FREE): + { + /* validate */ + PSUPLOWFREE pReq = (PSUPLOWFREE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_LOW_FREE); + + /* execute */ + pReq->Hdr.rc = SUPR0LowFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_MAP): + { + /* validate */ + PSUPGIPMAP pReq = (PSUPGIPMAP)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_GIP_MAP); + + /* execute */ + pReq->Hdr.rc = SUPR0GipMap(pSession, &pReq->u.Out.pGipR3, &pReq->u.Out.HCPhysGip); + if (RT_SUCCESS(pReq->Hdr.rc)) + pReq->u.Out.pGipR0 = pDevExt->pGip; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_UNMAP): + { + /* validate */ + PSUPGIPUNMAP pReq = (PSUPGIPUNMAP)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_GIP_UNMAP); + + /* execute */ + pReq->Hdr.rc = SUPR0GipUnmap(pSession); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SET_VM_FOR_FAST): + { + /* validate */ + PSUPSETVMFORFAST pReq = (PSUPSETVMFORFAST)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_SET_VM_FOR_FAST); + REQ_CHECK_EXPR_FMT( !pReq->u.In.pVMR0 + || ( VALID_PTR(pReq->u.In.pVMR0) + && !((uintptr_t)pReq->u.In.pVMR0 & (PAGE_SIZE - 1))), + ("SUP_IOCTL_SET_VM_FOR_FAST: pVMR0=%p!\n", pReq->u.In.pVMR0)); + /* execute */ + pSession->pVM = pReq->u.In.pVMR0; + pReq->Hdr.rc = VINF_SUCCESS; + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_ALLOC): + { + /* validate */ + PSUPPAGEALLOC pReq = (PSUPPAGEALLOC)pReqHdr; + REQ_CHECK_EXPR(SUP_IOCTL_PAGE_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_PAGE_ALLOC_SIZE_IN); + REQ_CHECK_SIZES_EX(SUP_IOCTL_PAGE_ALLOC, SUP_IOCTL_PAGE_ALLOC_SIZE_IN, SUP_IOCTL_PAGE_ALLOC_SIZE_OUT(pReq->u.In.cPages)); + + /* execute */ + pReq->Hdr.rc = SUPR0PageAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_ALLOC_EX): + { + /* validate */ + PSUPPAGEALLOCEX pReq = (PSUPPAGEALLOCEX)pReqHdr; + REQ_CHECK_EXPR(SUP_IOCTL_PAGE_ALLOC_EX, pReq->Hdr.cbIn <= SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN); + REQ_CHECK_SIZES_EX(SUP_IOCTL_PAGE_ALLOC_EX, SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN, SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(pReq->u.In.cPages)); + REQ_CHECK_EXPR_FMT(pReq->u.In.fKernelMapping || pReq->u.In.fUserMapping, + ("SUP_IOCTL_PAGE_ALLOC_EX: No mapping requested!\n")); + REQ_CHECK_EXPR_FMT(pReq->u.In.fUserMapping, + ("SUP_IOCTL_PAGE_ALLOC_EX: Must have user mapping!\n")); + REQ_CHECK_EXPR_FMT(!pReq->u.In.fReserved0 && !pReq->u.In.fReserved1, + ("SUP_IOCTL_PAGE_ALLOC_EX: fReserved0=%d fReserved1=%d\n", pReq->u.In.fReserved0, pReq->u.In.fReserved1)); + + /* execute */ + pReq->Hdr.rc = SUPR0PageAllocEx(pSession, pReq->u.In.cPages, 0 /* fFlags */, + pReq->u.In.fUserMapping ? &pReq->u.Out.pvR3 : NULL, + pReq->u.In.fKernelMapping ? &pReq->u.Out.pvR0 : NULL, + &pReq->u.Out.aPages[0]); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_MAP_KERNEL): + { + /* validate */ + PSUPPAGEMAPKERNEL pReq = (PSUPPAGEMAPKERNEL)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_PAGE_MAP_KERNEL); + REQ_CHECK_EXPR_FMT(!pReq->u.In.fFlags, ("SUP_IOCTL_PAGE_MAP_KERNEL: fFlags=%#x! MBZ\n", pReq->u.In.fFlags)); + REQ_CHECK_EXPR_FMT(!(pReq->u.In.offSub & PAGE_OFFSET_MASK), ("SUP_IOCTL_PAGE_MAP_KERNEL: offSub=%#x\n", pReq->u.In.offSub)); + REQ_CHECK_EXPR_FMT(pReq->u.In.cbSub && !(pReq->u.In.cbSub & PAGE_OFFSET_MASK), + ("SUP_IOCTL_PAGE_MAP_KERNEL: cbSub=%#x\n", pReq->u.In.cbSub)); + + /* execute */ + pReq->Hdr.rc = SUPR0PageMapKernel(pSession, pReq->u.In.pvR3, pReq->u.In.offSub, pReq->u.In.cbSub, + pReq->u.In.fFlags, &pReq->u.Out.pvR0); + if (RT_FAILURE(pReq->Hdr.rc)) + pReq->Hdr.cbOut = sizeof(pReq->Hdr); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_FREE): + { + /* validate */ + PSUPPAGEFREE pReq = (PSUPPAGEFREE)pReqHdr; + REQ_CHECK_SIZES(SUP_IOCTL_PAGE_FREE); + + /* execute */ + pReq->Hdr.rc = SUPR0PageFree(pSession, pReq->u.In.pvR3); + return 0; + } + + case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_SERVICE(0)): + { + /* validate */ + PSUPCALLSERVICE pReq = (PSUPCALLSERVICE)pReqHdr; + Log4(("SUP_IOCTL_CALL_SERVICE: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + + if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_SERVICE_SIZE(0)) + REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_SERVICE, SUP_IOCTL_CALL_SERVICE_SIZE_IN(0), SUP_IOCTL_CALL_SERVICE_SIZE_OUT(0)); + else + { + PSUPR0SERVICEREQHDR pSrvReq = (PSUPR0SERVICEREQHDR)&pReq->abReqPkt[0]; + REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_SERVICE_SIZE(sizeof(SUPR0SERVICEREQHDR)), + ("SUP_IOCTL_CALL_SERVICE: cbIn=%#x < %#lx\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_SERVICE_SIZE(sizeof(SUPR0SERVICEREQHDR)))); + REQ_CHECK_EXPR(SUP_IOCTL_CALL_SERVICE, pSrvReq->u32Magic == SUPR0SERVICEREQHDR_MAGIC); + REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_SERVICE, SUP_IOCTL_CALL_SERVICE_SIZE_IN(pSrvReq->cbReq), SUP_IOCTL_CALL_SERVICE_SIZE_OUT(pSrvReq->cbReq)); + } + REQ_CHECK_EXPR(SUP_IOCTL_CALL_SERVICE, memchr(pReq->u.In.szName, '\0', sizeof(pReq->u.In.szName))); + + /* execute */ + pReq->Hdr.rc = supdrvIOCtl_CallServiceModule(pDevExt, pSession, pReq); + return 0; + } + + default: + Log(("Unknown IOCTL %#lx\n", (long)uIOCtl)); + break; + } + return SUPDRV_ERR_GENERAL_FAILURE; +} + + +/** + * Inter-Driver Communcation (IDC) worker. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_INVALID_PARAMETER if the request is invalid. + * @retval VERR_NOT_SUPPORTED if the request isn't supported. + * + * @param uReq The request (function) code. + * @param pDevExt Device extention. + * @param pSession Session data. + * @param pReqHdr The request header. + */ +int VBOXCALL supdrvIDC(uintptr_t uReq, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQHDR pReqHdr) +{ + /* + * The OS specific code has already validated the pSession + * pointer, and the request size being greater or equal to + * size of the header. + * + * So, just check that pSession is a kernel context session. + */ + if (RT_UNLIKELY( pSession + && pSession->R0Process != NIL_RTR0PROCESS)) + return VERR_INVALID_PARAMETER; + +/* + * Validation macro. + */ +#define REQ_CHECK_IDC_SIZE(Name, cbExpect) \ + do { \ + if (RT_UNLIKELY(pReqHdr->cb != (cbExpect))) \ + { \ + OSDBGPRINT(( #Name ": Invalid input/output sizes. cb=%ld expected %ld.\n", \ + (long)pReqHdr->cb, (long)(cbExpect))); \ + return pReqHdr->rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + + switch (uReq) + { + case SUPDRV_IDC_REQ_CONNECT: + { + PSUPDRVIDCREQCONNECT pReq = (PSUPDRVIDCREQCONNECT)pReqHdr; + REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_CONNECT, sizeof(*pReq)); + + /* + * Validate the cookie and other input. + */ + if (pReq->Hdr.pSession != NULL) + { + OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: pSession=%p expected NULL!\n", pReq->Hdr.pSession)); + return pReqHdr->rc = VERR_INVALID_PARAMETER; + } + if (pReq->u.In.u32MagicCookie != SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE) + { + OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: u32MagicCookie=%#x expected %#x!\n", + pReq->u.In.u32MagicCookie, SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE)); + return pReqHdr->rc = VERR_INVALID_PARAMETER; + } + if ( pReq->u.In.uMinVersion > pReq->u.In.uReqVersion + || (pReq->u.In.uMinVersion & UINT32_C(0xffff0000)) != (pReq->u.In.uReqVersion & UINT32_C(0xffff0000))) + { + OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n", + pReq->u.In.uMinVersion, pReq->u.In.uReqVersion)); + return pReqHdr->rc = VERR_INVALID_PARAMETER; + } + + /* + * Match the version. + * The current logic is very simple, match the major interface version. + */ + if ( pReq->u.In.uMinVersion > SUPDRV_IDC_VERSION + || (pReq->u.In.uMinVersion & 0xffff0000) != (SUPDRV_IDC_VERSION & 0xffff0000)) + { + OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: Version mismatch. Requested: %#x Min: %#x Current: %#x\n", + pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, SUPDRV_IDC_VERSION)); + pReq->u.Out.pSession = NULL; + pReq->u.Out.uSessionVersion = 0xffffffff; + pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION; + pReq->u.Out.uDriverRevision = VBOX_SVN_REV; + pReq->Hdr.rc = VERR_VERSION_MISMATCH; + return VINF_SUCCESS; + } + + pReq->u.Out.pSession = NULL; + pReq->u.Out.uSessionVersion = SUPDRV_IDC_VERSION; + pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION; + pReq->u.Out.uDriverRevision = VBOX_SVN_REV; + + /* + * On NT we will already have a session associated with the + * client, just like with the SUP_IOCTL_COOKIE request, while + * the other doesn't. + */ +#ifdef RT_OS_WINDOWS + pReq->Hdr.rc = VINF_SUCCESS; +#else + AssertReturn(!pSession, VERR_INTERNAL_ERROR); + pReq->Hdr.rc = supdrvCreateSession(pDevExt, false /* fUser */, &pSession); + if (RT_FAILURE(pReq->Hdr.rc)) + { + OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: failed to create session, rc=%d\n", pReq->Hdr.rc)); + return VINF_SUCCESS; + } +#endif + + pReq->u.Out.pSession = pSession; + pReq->Hdr.pSession = pSession; + + return VINF_SUCCESS; + } + + case SUPDRV_IDC_REQ_DISCONNECT: + { + REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_DISCONNECT, sizeof(*pReqHdr)); + +#ifdef RT_OS_WINDOWS + /* Windows will destroy the session when the file object is destroyed. */ +#else + supdrvCloseSession(pDevExt, pSession); +#endif + return pReqHdr->rc = VINF_SUCCESS; + } + + case SUPDRV_IDC_REQ_GET_SYMBOL: + { + PSUPDRVIDCREQGETSYM pReq = (PSUPDRVIDCREQGETSYM)pReqHdr; + REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_GET_SYMBOL, sizeof(*pReq)); + + pReq->Hdr.rc = supdrvIDC_LdrGetSymbol(pDevExt, pSession, pReq); + return VINF_SUCCESS; + } + + case SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY: + { + PSUPDRVIDCREQCOMPREGFACTORY pReq = (PSUPDRVIDCREQCOMPREGFACTORY)pReqHdr; + REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY, sizeof(*pReq)); + + pReq->Hdr.rc = SUPR0ComponentRegisterFactory(pSession, pReq->u.In.pFactory); + return VINF_SUCCESS; + } + + case SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY: + { + PSUPDRVIDCREQCOMPDEREGFACTORY pReq = (PSUPDRVIDCREQCOMPDEREGFACTORY)pReqHdr; + REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY, sizeof(*pReq)); + + pReq->Hdr.rc = SUPR0ComponentDeregisterFactory(pSession, pReq->u.In.pFactory); + return VINF_SUCCESS; + } + + default: + Log(("Unknown IDC %#lx\n", (long)uReq)); + break; + } + +#undef REQ_CHECK_IDC_SIZE + return VERR_NOT_SUPPORTED; +} + + +/** + * Register a object for reference counting. + * The object is registered with one reference in the specified session. + * + * @returns Unique identifier on success (pointer). + * All future reference must use this identifier. + * @returns NULL on failure. + * @param pfnDestructor The destructore function which will be called when the reference count reaches 0. + * @param pvUser1 The first user argument. + * @param pvUser2 The second user argument. + */ +SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2) +{ + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + PSUPDRVDEVEXT pDevExt = pSession->pDevExt; + PSUPDRVOBJ pObj; + PSUPDRVUSAGE pUsage; + + /* + * Validate the input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL); + AssertReturn(enmType > SUPDRVOBJTYPE_INVALID && enmType < SUPDRVOBJTYPE_END, NULL); + AssertPtrReturn(pfnDestructor, NULL); + + /* + * Allocate and initialize the object. + */ + pObj = (PSUPDRVOBJ)RTMemAlloc(sizeof(*pObj)); + if (!pObj) + return NULL; + pObj->u32Magic = SUPDRVOBJ_MAGIC; + pObj->enmType = enmType; + pObj->pNext = NULL; + pObj->cUsage = 1; + pObj->pfnDestructor = pfnDestructor; + pObj->pvUser1 = pvUser1; + pObj->pvUser2 = pvUser2; + pObj->CreatorUid = pSession->Uid; + pObj->CreatorGid = pSession->Gid; + pObj->CreatorProcess= pSession->Process; + supdrvOSObjInitCreator(pObj, pSession); + + /* + * Allocate the usage record. + * (We keep freed usage records around to simplify SUPR0ObjAddRefEx().) + */ + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + + pUsage = pDevExt->pUsageFree; + if (pUsage) + pDevExt->pUsageFree = pUsage->pNext; + else + { + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage)); + if (!pUsage) + { + RTMemFree(pObj); + return NULL; + } + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + } + + /* + * Insert the object and create the session usage record. + */ + /* The object. */ + pObj->pNext = pDevExt->pObjs; + pDevExt->pObjs = pObj; + + /* The session record. */ + pUsage->cUsage = 1; + pUsage->pObj = pObj; + pUsage->pNext = pSession->pUsage; + /* Log2(("SUPR0ObjRegister: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext)); */ + pSession->pUsage = pUsage; + + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + Log(("SUPR0ObjRegister: returns %p (pvUser1=%p, pvUser=%p)\n", pObj, pvUser1, pvUser2)); + return pObj; +} + + +/** + * Increment the reference counter for the object associating the reference + * with the specified session. + * + * @returns IPRT status code. + * @param pvObj The identifier returned by SUPR0ObjRegister(). + * @param pSession The session which is referencing the object. + * + * @remarks The caller should not own any spinlocks and must carefully protect + * itself against potential race with the destructor so freed memory + * isn't accessed here. + */ +SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession) +{ + return SUPR0ObjAddRefEx(pvObj, pSession, false /* fNoBlocking */); +} + + +/** + * Increment the reference counter for the object associating the reference + * with the specified session. + * + * @returns IPRT status code. + * @retval VERR_TRY_AGAIN if fNoBlocking was set and a new usage record + * couldn't be allocated. (If you see this you're not doing the right + * thing and it won't ever work reliably.) + * + * @param pvObj The identifier returned by SUPR0ObjRegister(). + * @param pSession The session which is referencing the object. + * @param fNoBlocking Set if it's not OK to block. Never try to make the + * first reference to an object in a session with this + * argument set. + * + * @remarks The caller should not own any spinlocks and must carefully protect + * itself against potential race with the destructor so freed memory + * isn't accessed here. + */ +SUPR0DECL(int) SUPR0ObjAddRefEx(void *pvObj, PSUPDRVSESSION pSession, bool fNoBlocking) +{ + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + PSUPDRVDEVEXT pDevExt = pSession->pDevExt; + PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj; + int rc = VINF_SUCCESS; + PSUPDRVUSAGE pUsagePre; + PSUPDRVUSAGE pUsage; + + /* + * Validate the input. + * Be ready for the destruction race (someone might be stuck in the + * destructor waiting a lock we own). + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertMsgReturn(pObj->u32Magic == SUPDRVOBJ_MAGIC || pObj->u32Magic == SUPDRVOBJ_MAGIC_DEAD, + ("Invalid pvObj=%p magic=%#x (expected %#x or %#x)\n", pvObj, pObj->u32Magic, SUPDRVOBJ_MAGIC, SUPDRVOBJ_MAGIC_DEAD), + VERR_INVALID_PARAMETER); + + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + + if (RT_UNLIKELY(pObj->u32Magic != SUPDRVOBJ_MAGIC)) + { + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + AssertMsgFailed(("pvObj=%p magic=%#x\n", pvObj, pObj->u32Magic)); + return VERR_WRONG_ORDER; + } + + /* + * Preallocate the usage record if we can. + */ + pUsagePre = pDevExt->pUsageFree; + if (pUsagePre) + pDevExt->pUsageFree = pUsagePre->pNext; + else if (!fNoBlocking) + { + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + pUsagePre = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsagePre)); + if (!pUsagePre) + return VERR_NO_MEMORY; + + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + if (RT_UNLIKELY(pObj->u32Magic != SUPDRVOBJ_MAGIC)) + { + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + AssertMsgFailed(("pvObj=%p magic=%#x\n", pvObj, pObj->u32Magic)); + return VERR_WRONG_ORDER; + } + } + + /* + * Reference the object. + */ + pObj->cUsage++; + + /* + * Look for the session record. + */ + for (pUsage = pSession->pUsage; pUsage; pUsage = pUsage->pNext) + { + /*Log(("SUPR0AddRef: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/ + if (pUsage->pObj == pObj) + break; + } + if (pUsage) + pUsage->cUsage++; + else if (pUsagePre) + { + /* create a new session record. */ + pUsagePre->cUsage = 1; + pUsagePre->pObj = pObj; + pUsagePre->pNext = pSession->pUsage; + pSession->pUsage = pUsagePre; + /*Log(("SUPR0AddRef: pUsagePre=%p:{.pObj=%p, .pNext=%p}\n", pUsagePre, pUsagePre->pObj, pUsagePre->pNext));*/ + + pUsagePre = NULL; + } + else + { + pObj->cUsage--; + rc = VERR_TRY_AGAIN; + } + + /* + * Put any unused usage record into the free list.. + */ + if (pUsagePre) + { + pUsagePre->pNext = pDevExt->pUsageFree; + pDevExt->pUsageFree = pUsagePre; + } + + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + return rc; +} + + +/** + * Decrement / destroy a reference counter record for an object. + * + * The object is uniquely identified by pfnDestructor+pvUser1+pvUser2. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS if not destroyed. + * @retval VINF_OBJECT_DESTROYED if it's destroyed by this release call. + * @retval VERR_INVALID_PARAMETER if the object isn't valid. Will assert in + * string builds. + * + * @param pvObj The identifier returned by SUPR0ObjRegister(). + * @param pSession The session which is referencing the object. + */ +SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession) +{ + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + PSUPDRVDEVEXT pDevExt = pSession->pDevExt; + PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj; + int rc = VERR_INVALID_PARAMETER; + PSUPDRVUSAGE pUsage; + PSUPDRVUSAGE pUsagePrev; + + /* + * Validate the input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC, + ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC), + VERR_INVALID_PARAMETER); + + /* + * Acquire the spinlock and look for the usage record. + */ + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + + for (pUsagePrev = NULL, pUsage = pSession->pUsage; + pUsage; + pUsagePrev = pUsage, pUsage = pUsage->pNext) + { + /*Log2(("SUPR0ObjRelease: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/ + if (pUsage->pObj == pObj) + { + rc = VINF_SUCCESS; + AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage)); + if (pUsage->cUsage > 1) + { + pObj->cUsage--; + pUsage->cUsage--; + } + else + { + /* + * Free the session record. + */ + if (pUsagePrev) + pUsagePrev->pNext = pUsage->pNext; + else + pSession->pUsage = pUsage->pNext; + pUsage->pNext = pDevExt->pUsageFree; + pDevExt->pUsageFree = pUsage; + + /* What about the object? */ + if (pObj->cUsage > 1) + pObj->cUsage--; + else + { + /* + * Object is to be destroyed, unlink it. + */ + pObj->u32Magic = SUPDRVOBJ_MAGIC_DEAD; + rc = VINF_OBJECT_DESTROYED; + if (pDevExt->pObjs == pObj) + pDevExt->pObjs = pObj->pNext; + else + { + PSUPDRVOBJ pObjPrev; + for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext) + if (pObjPrev->pNext == pObj) + { + pObjPrev->pNext = pObj->pNext; + break; + } + Assert(pObjPrev); + } + } + } + break; + } + } + + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + + /* + * Call the destructor and free the object if required. + */ + if (rc == VINF_OBJECT_DESTROYED) + { + Log(("SUPR0ObjRelease: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n", + pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor)); + if (pObj->pfnDestructor) +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapObjDestructor((PFNRT)pObj->pfnDestructor, pObj, pObj->pvUser1, pObj->pvUser2); +#else + pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2); +#endif + RTMemFree(pObj); + } + + AssertMsg(pUsage, ("pvObj=%p\n", pvObj)); + return rc; +} + + +/** + * Verifies that the current process can access the specified object. + * + * @returns The following IPRT status code: + * @retval VINF_SUCCESS if access was granted. + * @retval VERR_PERMISSION_DENIED if denied access. + * @retval VERR_INVALID_PARAMETER if invalid parameter. + * + * @param pvObj The identifier returned by SUPR0ObjRegister(). + * @param pSession The session which wishes to access the object. + * @param pszObjName Object string name. This is optional and depends on the object type. + * + * @remark The caller is responsible for making sure the object isn't removed while + * we're inside this function. If uncertain about this, just call AddRef before calling us. + */ +SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName) +{ + PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj; + int rc; + + /* + * Validate the input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC, + ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC), + VERR_INVALID_PARAMETER); + + /* + * Check access. (returns true if a decision has been made.) + */ + rc = VERR_INTERNAL_ERROR; + if (supdrvOSObjCanAccess(pObj, pSession, pszObjName, &rc)) + return rc; + + /* + * Default policy is to allow the user to access his own + * stuff but nothing else. + */ + if (pObj->CreatorUid == pSession->Uid) + return VINF_SUCCESS; + return VERR_PERMISSION_DENIED; +} + + +/** + * Lock pages. + * + * @returns IPRT status code. + * @param pSession Session to which the locked memory should be associated. + * @param pvR3 Start of the memory range to lock. + * This must be page aligned. + * @param cb Size of the memory range to lock. + * This must be page aligned. + */ +SUPR0DECL(int) SUPR0LockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages) +{ + int rc; + SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED }; + const size_t cb = (size_t)cPages << PAGE_SHIFT; + LogFlow(("SUPR0LockMem: pSession=%p pvR3=%p cPages=%d paPages=%p\n", pSession, (void *)pvR3, cPages, paPages)); + + /* + * Verify input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrReturn(paPages, VERR_INVALID_PARAMETER); + if ( RT_ALIGN_R3PT(pvR3, PAGE_SIZE, RTR3PTR) != pvR3 + || !pvR3) + { + Log(("pvR3 (%p) must be page aligned and not NULL!\n", (void *)pvR3)); + return VERR_INVALID_PARAMETER; + } + +#ifdef RT_OS_WINDOWS /* A temporary hack for windows, will be removed once all ring-3 code has been cleaned up. */ + /* First check if we allocated it using SUPPageAlloc; if so then we don't need to lock it again */ + rc = supdrvPageGetPhys(pSession, pvR3, cPages, paPages); + if (RT_SUCCESS(rc)) + return rc; +#endif + + /* + * Let IPRT do the job. + */ + Mem.eType = MEMREF_TYPE_LOCKED; + rc = RTR0MemObjLockUser(&Mem.MemObj, pvR3, cb, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + uint32_t iPage = cPages; + AssertMsg(RTR0MemObjAddressR3(Mem.MemObj) == pvR3, ("%p == %p\n", RTR0MemObjAddressR3(Mem.MemObj), pvR3)); + AssertMsg(RTR0MemObjSize(Mem.MemObj) == cb, ("%x == %x\n", RTR0MemObjSize(Mem.MemObj), cb)); + + while (iPage-- > 0) + { + paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage); + if (RT_UNLIKELY(paPages[iPage] == NIL_RTCCPHYS)) + { + AssertMsgFailed(("iPage=%d\n", iPage)); + rc = VERR_INTERNAL_ERROR; + break; + } + } + if (RT_SUCCESS(rc)) + rc = supdrvMemAdd(&Mem, pSession); + if (RT_FAILURE(rc)) + { + int rc2 = RTR0MemObjFree(Mem.MemObj, false); + AssertRC(rc2); + } + } + + return rc; +} + + +/** + * Unlocks the memory pointed to by pv. + * + * @returns IPRT status code. + * @param pSession Session to which the memory was locked. + * @param pvR3 Memory to unlock. + */ +SUPR0DECL(int) SUPR0UnlockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3) +{ + LogFlow(("SUPR0UnlockMem: pSession=%p pvR3=%p\n", pSession, (void *)pvR3)); + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); +#ifdef RT_OS_WINDOWS + /* + * Temporary hack for windows - SUPR0PageFree will unlock SUPR0PageAlloc + * allocations; ignore this call. + */ + if (supdrvPageWasLockedByPageAlloc(pSession, pvR3)) + { + LogFlow(("Page will be unlocked in SUPR0PageFree -> ignore\n")); + return VINF_SUCCESS; + } +#endif + return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED); +} + + +/** + * Allocates a chunk of page aligned memory with contiguous and fixed physical + * backing. + * + * @returns IPRT status code. + * @param pSession Session data. + * @param cb Number of bytes to allocate. + * @param ppvR0 Where to put the address of Ring-0 mapping the allocated memory. + * @param ppvR3 Where to put the address of Ring-3 mapping the allocated memory. + * @param pHCPhys Where to put the physical address of allocated memory. + */ +SUPR0DECL(int) SUPR0ContAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys) +{ + int rc; + SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED }; + LogFlow(("SUPR0ContAlloc: pSession=%p cPages=%d ppvR0=%p ppvR3=%p pHCPhys=%p\n", pSession, cPages, ppvR0, ppvR3, pHCPhys)); + + /* + * Validate input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + if (!ppvR3 || !ppvR0 || !pHCPhys) + { + Log(("Null pointer. All of these should be set: pSession=%p ppvR0=%p ppvR3=%p pHCPhys=%p\n", + pSession, ppvR0, ppvR3, pHCPhys)); + return VERR_INVALID_PARAMETER; + + } + if (cPages < 1 || cPages >= 256) + { + Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages)); + return VERR_PAGE_COUNT_OUT_OF_RANGE; + } + + /* + * Let IPRT do the job. + */ + rc = RTR0MemObjAllocCont(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable R0 mapping */); + if (RT_SUCCESS(rc)) + { + int rc2; + rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0, + RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + Mem.eType = MEMREF_TYPE_CONT; + rc = supdrvMemAdd(&Mem, pSession); + if (!rc) + { + *ppvR0 = RTR0MemObjAddress(Mem.MemObj); + *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3); + *pHCPhys = RTR0MemObjGetPagePhysAddr(Mem.MemObj, 0); + return 0; + } + + rc2 = RTR0MemObjFree(Mem.MapObjR3, false); + AssertRC(rc2); + } + rc2 = RTR0MemObjFree(Mem.MemObj, false); + AssertRC(rc2); + } + + return rc; +} + + +/** + * Frees memory allocated using SUPR0ContAlloc(). + * + * @returns IPRT status code. + * @param pSession The session to which the memory was allocated. + * @param uPtr Pointer to the memory (ring-3 or ring-0). + */ +SUPR0DECL(int) SUPR0ContFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr) +{ + LogFlow(("SUPR0ContFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr)); + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_CONT); +} + + +/** + * Allocates a chunk of page aligned memory with fixed physical backing below 4GB. + * + * The memory isn't zeroed. + * + * @returns IPRT status code. + * @param pSession Session data. + * @param cPages Number of pages to allocate. + * @param ppvR0 Where to put the address of Ring-0 mapping of the allocated memory. + * @param ppvR3 Where to put the address of Ring-3 mapping of the allocated memory. + * @param paPages Where to put the physical addresses of allocated memory. + */ +SUPR0DECL(int) SUPR0LowAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS paPages) +{ + unsigned iPage; + int rc; + SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED }; + LogFlow(("SUPR0LowAlloc: pSession=%p cPages=%d ppvR3=%p ppvR0=%p paPages=%p\n", pSession, cPages, ppvR3, ppvR0, paPages)); + + /* + * Validate input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + if (!ppvR3 || !ppvR0 || !paPages) + { + Log(("Null pointer. All of these should be set: pSession=%p ppvR3=%p ppvR0=%p paPages=%p\n", + pSession, ppvR3, ppvR0, paPages)); + return VERR_INVALID_PARAMETER; + + } + if (cPages < 1 || cPages >= 256) + { + Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages)); + return VERR_PAGE_COUNT_OUT_OF_RANGE; + } + + /* + * Let IPRT do the work. + */ + rc = RTR0MemObjAllocLow(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable ring-0 mapping */); + if (RT_SUCCESS(rc)) + { + int rc2; + rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0, + RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + Mem.eType = MEMREF_TYPE_LOW; + rc = supdrvMemAdd(&Mem, pSession); + if (!rc) + { + for (iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage); + AssertMsg(!(paPages[iPage] & (PAGE_SIZE - 1)), ("iPage=%d Phys=%RHp\n", paPages[iPage])); + } + *ppvR0 = RTR0MemObjAddress(Mem.MemObj); + *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3); + return 0; + } + + rc2 = RTR0MemObjFree(Mem.MapObjR3, false); + AssertRC(rc2); + } + + rc2 = RTR0MemObjFree(Mem.MemObj, false); + AssertRC(rc2); + } + + return rc; +} + + +/** + * Frees memory allocated using SUPR0LowAlloc(). + * + * @returns IPRT status code. + * @param pSession The session to which the memory was allocated. + * @param uPtr Pointer to the memory (ring-3 or ring-0). + */ +SUPR0DECL(int) SUPR0LowFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr) +{ + LogFlow(("SUPR0LowFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr)); + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_LOW); +} + + + +/** + * Allocates a chunk of memory with both R0 and R3 mappings. + * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys(). + * + * @returns IPRT status code. + * @param pSession The session to associated the allocation with. + * @param cb Number of bytes to allocate. + * @param ppvR0 Where to store the address of the Ring-0 mapping. + * @param ppvR3 Where to store the address of the Ring-3 mapping. + */ +SUPR0DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3) +{ + int rc; + SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED }; + LogFlow(("SUPR0MemAlloc: pSession=%p cb=%d ppvR0=%p ppvR3=%p\n", pSession, cb, ppvR0, ppvR3)); + + /* + * Validate input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrReturn(ppvR0, VERR_INVALID_POINTER); + AssertPtrReturn(ppvR3, VERR_INVALID_POINTER); + if (cb < 1 || cb >= _4M) + { + Log(("Illegal request cb=%u; must be greater than 0 and smaller than 4MB.\n", cb)); + return VERR_INVALID_PARAMETER; + } + + /* + * Let IPRT do the work. + */ + rc = RTR0MemObjAllocPage(&Mem.MemObj, cb, true /* executable ring-0 mapping */); + if (RT_SUCCESS(rc)) + { + int rc2; + rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0, + RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + Mem.eType = MEMREF_TYPE_MEM; + rc = supdrvMemAdd(&Mem, pSession); + if (!rc) + { + *ppvR0 = RTR0MemObjAddress(Mem.MemObj); + *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3); + return VINF_SUCCESS; + } + + rc2 = RTR0MemObjFree(Mem.MapObjR3, false); + AssertRC(rc2); + } + + rc2 = RTR0MemObjFree(Mem.MemObj, false); + AssertRC(rc2); + } + + return rc; +} + + +/** + * Get the physical addresses of memory allocated using SUPR0MemAlloc(). + * + * @returns IPRT status code. + * @param pSession The session to which the memory was allocated. + * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc(). + * @param paPages Where to store the physical addresses. + */ +SUPR0DECL(int) SUPR0MemGetPhys(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, PSUPPAGE paPages) /** @todo switch this bugger to RTHCPHYS */ +{ + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + LogFlow(("SUPR0MemGetPhys: pSession=%p uPtr=%p paPages=%p\n", pSession, (void *)uPtr, paPages)); + + /* + * Validate input. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrReturn(paPages, VERR_INVALID_POINTER); + AssertReturn(uPtr, VERR_INVALID_PARAMETER); + + /* + * Search for the address. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed > 0) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if ( pBundle->aMem[i].eType == MEMREF_TYPE_MEM + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr + || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr) + ) + ) + { + const size_t cPages = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT; + size_t iPage; + for (iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage); + paPages[iPage].uReserved = 0; + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return VINF_SUCCESS; + } + } + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + Log(("Failed to find %p!!!\n", (void *)uPtr)); + return VERR_INVALID_PARAMETER; +} + + +/** + * Free memory allocated by SUPR0MemAlloc(). + * + * @returns IPRT status code. + * @param pSession The session owning the allocation. + * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc(). + */ +SUPR0DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr) +{ + LogFlow(("SUPR0MemFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr)); + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_MEM); +} + + +/** + * Allocates a chunk of memory with only a R3 mappings. + * + * The memory is fixed and it's possible to query the physical addresses using + * SUPR0MemGetPhys(). + * + * @returns IPRT status code. + * @param pSession The session to associated the allocation with. + * @param cPages The number of pages to allocate. + * @param ppvR3 Where to store the address of the Ring-3 mapping. + * @param paPages Where to store the addresses of the pages. Optional. + */ +SUPR0DECL(int) SUPR0PageAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR3PTR ppvR3, PRTHCPHYS paPages) +{ + AssertPtrReturn(ppvR3, VERR_INVALID_POINTER); + return SUPR0PageAllocEx(pSession, cPages, 0 /*fFlags*/, ppvR3, NULL, paPages); +} + + +/** + * Allocates a chunk of memory with a kernel or/and a user mode mapping. + * + * The memory is fixed and it's possible to query the physical addresses using + * SUPR0MemGetPhys(). + * + * @returns IPRT status code. + * @param pSession The session to associated the allocation with. + * @param cPages The number of pages to allocate. + * @param fFlags Flags, reserved for the future. Must be zero. + * @param ppvR3 Where to store the address of the Ring-3 mapping. + * NULL if no ring-3 mapping. + * @param ppvR3 Where to store the address of the Ring-0 mapping. + * NULL if no ring-0 mapping. + * @param paPages Where to store the addresses of the pages. Optional. + */ +SUPR0DECL(int) SUPR0PageAllocEx(PSUPDRVSESSION pSession, uint32_t cPages, uint32_t fFlags, PRTR3PTR ppvR3, PRTR0PTR ppvR0, PRTHCPHYS paPages) +{ + int rc; + SUPDRVMEMREF Mem = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, MEMREF_TYPE_UNUSED }; + LogFlow(("SUPR0PageAlloc: pSession=%p cb=%d ppvR3=%p\n", pSession, cPages, ppvR3)); + + /* + * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrNullReturn(ppvR3, VERR_INVALID_POINTER); + AssertPtrNullReturn(ppvR0, VERR_INVALID_POINTER); + AssertReturn(ppvR3 || ppvR0, VERR_INVALID_PARAMETER); + AssertReturn(!fFlags, VERR_INVALID_PARAMETER); + if (cPages < 1 || cPages > VBOX_MAX_ALLOC_PAGE_COUNT) + { + Log(("SUPR0PageAlloc: Illegal request cb=%u; must be greater than 0 and smaller than 128MB.\n", cPages)); + return VERR_PAGE_COUNT_OUT_OF_RANGE; + } + + /* + * Let IPRT do the work. + */ + if (ppvR0) + rc = RTR0MemObjAllocPage(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, true /* fExecutable */); + else + rc = RTR0MemObjAllocPhysNC(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, NIL_RTHCPHYS); + if (RT_SUCCESS(rc)) + { + int rc2; + if (ppvR3) + rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0, + RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf()); + else + Mem.MapObjR3 = NIL_RTR0MEMOBJ; + if (RT_SUCCESS(rc)) + { + Mem.eType = MEMREF_TYPE_PAGE; + rc = supdrvMemAdd(&Mem, pSession); + if (!rc) + { + if (ppvR3) + *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3); + if (ppvR0) + *ppvR0 = RTR0MemObjAddress(Mem.MemObj); + if (paPages) + { + uint32_t iPage = cPages; + while (iPage-- > 0) + { + paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MapObjR3, iPage); + Assert(paPages[iPage] != NIL_RTHCPHYS); + } + } + return VINF_SUCCESS; + } + + rc2 = RTR0MemObjFree(Mem.MapObjR3, false); + AssertRC(rc2); + } + + rc2 = RTR0MemObjFree(Mem.MemObj, false); + AssertRC(rc2); + } + return rc; +} + + +/** + * Allocates a chunk of memory with a kernel or/and a user mode mapping. + * + * The memory is fixed and it's possible to query the physical addresses using + * SUPR0MemGetPhys(). + * + * @returns IPRT status code. + * @param pSession The session to associated the allocation with. + * @param cPages The number of pages to allocate. + * @param fFlags Flags, reserved for the future. Must be zero. + * @param ppvR3 Where to store the address of the Ring-3 mapping. + * NULL if no ring-3 mapping. + * @param ppvR3 Where to store the address of the Ring-0 mapping. + * NULL if no ring-0 mapping. + * @param paPages Where to store the addresses of the pages. Optional. + */ +SUPR0DECL(int) SUPR0PageMapKernel(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t offSub, uint32_t cbSub, + uint32_t fFlags, PRTR0PTR ppvR0) +{ + int rc; + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + RTR0MEMOBJ hMemObj = NIL_RTR0MEMOBJ; + LogFlow(("SUPR0PageMapKernel: pSession=%p pvR3=%p offSub=%#x cbSub=%#x\n", pSession, pvR3, offSub, cbSub)); + + /* + * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrNullReturn(ppvR0, VERR_INVALID_POINTER); + AssertReturn(!fFlags, VERR_INVALID_PARAMETER); + AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER); + AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER); + AssertReturn(cbSub, VERR_INVALID_PARAMETER); + + /* + * Find the memory object. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed > 0) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if ( ( pBundle->aMem[i].eType == MEMREF_TYPE_PAGE + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3) + || ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && pBundle->aMem[i].MapObjR3 == NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MemObj) == pvR3)) + { + hMemObj = pBundle->aMem[i].MemObj; + break; + } + } + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + + rc = VERR_INVALID_PARAMETER; + if (hMemObj != NIL_RTR0MEMOBJ) + { + /* + * Do some furter input validations before calling IPRT. + * (Cleanup is done indirectly by telling RTR0MemObjFree to include mappings.) + */ + size_t cbMemObj = RTR0MemObjSize(hMemObj); + if ( offSub < cbMemObj + && cbSub <= cbMemObj + && offSub + cbSub <= cbMemObj) + { + RTR0MEMOBJ hMapObj; + rc = RTR0MemObjMapKernelEx(&hMapObj, hMemObj, (void *)-1, 0, + RTMEM_PROT_READ | RTMEM_PROT_WRITE, offSub, cbSub); + if (RT_SUCCESS(rc)) + *ppvR0 = RTR0MemObjAddress(hMapObj); + } + else + SUPR0Printf("SUPR0PageMapKernel: cbMemObj=%#x offSub=%#x cbSub=%#x\n", cbMemObj, offSub, cbSub); + + } + return rc; +} + + + +#ifdef RT_OS_WINDOWS +/** + * Check if the pages were locked by SUPR0PageAlloc + * + * This function will be removed along with the lock/unlock hacks when + * we've cleaned up the ring-3 code properly. + * + * @returns boolean + * @param pSession The session to which the memory was allocated. + * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc(). + */ +static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3) +{ + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + LogFlow(("SUPR0PageIsLockedByPageAlloc: pSession=%p pvR3=%p\n", pSession, (void *)pvR3)); + + /* + * Search for the address. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed > 0) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if ( pBundle->aMem[i].eType == MEMREF_TYPE_PAGE + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3) + { + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return true; + } + } + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return false; +} + + +/** + * Get the physical addresses of memory allocated using SUPR0PageAllocEx(). + * + * This function will be removed along with the lock/unlock hacks when + * we've cleaned up the ring-3 code properly. + * + * @returns IPRT status code. + * @param pSession The session to which the memory was allocated. + * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc(). + * @param cPages Number of pages in paPages + * @param paPages Where to store the physical addresses. + */ +static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages) +{ + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + LogFlow(("supdrvPageGetPhys: pSession=%p pvR3=%p cPages=%#lx paPages=%p\n", pSession, (void *)pvR3, (long)cPages, paPages)); + + /* + * Search for the address. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed > 0) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if ( pBundle->aMem[i].eType == MEMREF_TYPE_PAGE + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3) + { + uint32_t iPage; + size_t cMaxPages = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT; + cPages = (uint32_t)RT_MIN(cMaxPages, cPages); + for (iPage = 0; iPage < cPages; iPage++) + paPages[iPage] = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage); + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return VINF_SUCCESS; + } + } + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return VERR_INVALID_PARAMETER; +} +#endif /* RT_OS_WINDOWS */ + + +/** + * Free memory allocated by SUPR0PageAlloc() and SUPR0PageAllocEx(). + * + * @returns IPRT status code. + * @param pSession The session owning the allocation. + * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc() or + * SUPR0PageAllocEx(). + */ +SUPR0DECL(int) SUPR0PageFree(PSUPDRVSESSION pSession, RTR3PTR pvR3) +{ + LogFlow(("SUPR0PageFree: pSession=%p pvR3=%p\n", pSession, (void *)pvR3)); + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_PAGE); +} + + +/** + * Maps the GIP into userspace and/or get the physical address of the GIP. + * + * @returns IPRT status code. + * @param pSession Session to which the GIP mapping should belong. + * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional) + * @param pHCPhysGip Where to store the physical address. (optional) + * + * @remark There is no reference counting on the mapping, so one call to this function + * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP + * and remove the session as a GIP user. + */ +SUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip) +{ + int rc = 0; + PSUPDRVDEVEXT pDevExt = pSession->pDevExt; + RTR3PTR pGip = NIL_RTR3PTR; + RTHCPHYS HCPhys = NIL_RTHCPHYS; + LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip)); + + /* + * Validate + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER); + AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER); + + RTSemFastMutexRequest(pDevExt->mtxGip); + if (pDevExt->pGip) + { + /* + * Map it? + */ + if (ppGipR3) + { + if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ) + rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0, + RTMEM_PROT_READ, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + pGip = RTR0MemObjAddressR3(pSession->GipMapObjR3); + rc = VINF_SUCCESS; /** @todo remove this and replace the !rc below with RT_SUCCESS(rc). */ + } + } + + /* + * Get physical address. + */ + if (pHCPhysGip && !rc) + HCPhys = pDevExt->HCPhysGip; + + /* + * Reference globally. + */ + if (!pSession->fGipReferenced && !rc) + { + pSession->fGipReferenced = 1; + pDevExt->cGipUsers++; + if (pDevExt->cGipUsers == 1) + { + PSUPGLOBALINFOPAGE pGip = pDevExt->pGip; + unsigned i; + + LogFlow(("SUPR0GipMap: Resumes GIP updating\n")); + + for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++) + ASMAtomicXchgU32(&pGip->aCPUs[i].u32TransactionId, pGip->aCPUs[i].u32TransactionId & ~(GIP_UPDATEHZ_RECALC_FREQ * 2 - 1)); + ASMAtomicXchgU64(&pGip->u64NanoTSLastUpdateHz, 0); + + rc = RTTimerStart(pDevExt->pGipTimer, 0); + AssertRC(rc); rc = VINF_SUCCESS; + } + } + } + else + { + rc = SUPDRV_ERR_GENERAL_FAILURE; + Log(("SUPR0GipMap: GIP is not available!\n")); + } + RTSemFastMutexRelease(pDevExt->mtxGip); + + /* + * Write returns. + */ + if (pHCPhysGip) + *pHCPhysGip = HCPhys; + if (ppGipR3) + *ppGipR3 = pGip; + +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGip=%p GipMapObjR3\n", rc, (unsigned long)HCPhys, pGip, pSession->GipMapObjR3)); +#else + LogFlow(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)(uintptr_t)pGip)); +#endif + return rc; +} + + +/** + * Unmaps any user mapping of the GIP and terminates all GIP access + * from this session. + * + * @returns IPRT status code. + * @param pSession Session to which the GIP mapping should belong. + */ +SUPR0DECL(int) SUPR0GipUnmap(PSUPDRVSESSION pSession) +{ + int rc = VINF_SUCCESS; + PSUPDRVDEVEXT pDevExt = pSession->pDevExt; +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("SUPR0GipUnmap: pSession=%p pGip=%p GipMapObjR3=%p\n", + pSession, + pSession->GipMapObjR3 != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pSession->GipMapObjR3) : NULL, + pSession->GipMapObjR3)); +#else + LogFlow(("SUPR0GipUnmap: pSession=%p\n", pSession)); +#endif + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + + RTSemFastMutexRequest(pDevExt->mtxGip); + + /* + * Unmap anything? + */ + if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ) + { + rc = RTR0MemObjFree(pSession->GipMapObjR3, false); + AssertRC(rc); + if (RT_SUCCESS(rc)) + pSession->GipMapObjR3 = NIL_RTR0MEMOBJ; + } + + /* + * Dereference global GIP. + */ + if (pSession->fGipReferenced && !rc) + { + pSession->fGipReferenced = 0; + if ( pDevExt->cGipUsers > 0 + && !--pDevExt->cGipUsers) + { + LogFlow(("SUPR0GipUnmap: Suspends GIP updating\n")); + rc = RTTimerStop(pDevExt->pGipTimer); AssertRC(rc); rc = 0; + } + } + + RTSemFastMutexRelease(pDevExt->mtxGip); + + return rc; +} + + +/** + * Register a component factory with the support driver. + * + * This is currently restricted to kernel sessions only. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NO_MEMORY if we're out of memory. + * @retval VERR_ALREADY_EXISTS if the factory has already been registered. + * @retval VERR_ACCESS_DENIED if it isn't a kernel session. + * @retval VERR_INVALID_PARAMETER on invalid parameter. + * @retval VERR_INVALID_POINTER on invalid pointer parameter. + * + * @param pSession The SUPDRV session (must be a ring-0 session). + * @param pFactory Pointer to the component factory registration structure. + * + * @remarks This interface is also available via SUPR0IdcComponentRegisterFactory. + */ +SUPR0DECL(int) SUPR0ComponentRegisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory) +{ + PSUPDRVFACTORYREG pNewReg; + const char *psz; + int rc; + + /* + * Validate parameters. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED); + AssertPtrReturn(pFactory, VERR_INVALID_POINTER); + AssertPtrReturn(pFactory->pfnQueryFactoryInterface, VERR_INVALID_POINTER); + psz = (const char *)memchr(pFactory->szName, '\0', sizeof(pFactory->szName)); + AssertReturn(psz, VERR_INVALID_PARAMETER); + + /* + * Allocate and initialize a new registration structure. + */ + pNewReg = (PSUPDRVFACTORYREG)RTMemAlloc(sizeof(SUPDRVFACTORYREG)); + if (pNewReg) + { + pNewReg->pNext = NULL; + pNewReg->pFactory = pFactory; + pNewReg->pSession = pSession; + pNewReg->cchName = psz - &pFactory->szName[0]; + + /* + * Add it to the tail of the list after checking for prior registration. + */ + rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory); + if (RT_SUCCESS(rc)) + { + PSUPDRVFACTORYREG pPrev = NULL; + PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead; + while (pCur && pCur->pFactory != pFactory) + { + pPrev = pCur; + pCur = pCur->pNext; + } + if (!pCur) + { + if (pPrev) + pPrev->pNext = pNewReg; + else + pSession->pDevExt->pComponentFactoryHead = pNewReg; + rc = VINF_SUCCESS; + } + else + rc = VERR_ALREADY_EXISTS; + + RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory); + } + + if (RT_FAILURE(rc)) + RTMemFree(pNewReg); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Deregister a component factory. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_FOUND if the factory wasn't registered. + * @retval VERR_ACCESS_DENIED if it isn't a kernel session. + * @retval VERR_INVALID_PARAMETER on invalid parameter. + * @retval VERR_INVALID_POINTER on invalid pointer parameter. + * + * @param pSession The SUPDRV session (must be a ring-0 session). + * @param pFactory Pointer to the component factory registration structure + * previously passed SUPR0ComponentRegisterFactory(). + * + * @remarks This interface is also available via SUPR0IdcComponentDeregisterFactory. + */ +SUPR0DECL(int) SUPR0ComponentDeregisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory) +{ + int rc; + + /* + * Validate parameters. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED); + AssertPtrReturn(pFactory, VERR_INVALID_POINTER); + + /* + * Take the lock and look for the registration record. + */ + rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory); + if (RT_SUCCESS(rc)) + { + PSUPDRVFACTORYREG pPrev = NULL; + PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead; + while (pCur && pCur->pFactory != pFactory) + { + pPrev = pCur; + pCur = pCur->pNext; + } + if (pCur) + { + if (!pPrev) + pSession->pDevExt->pComponentFactoryHead = pCur->pNext; + else + pPrev->pNext = pCur->pNext; + + pCur->pNext = NULL; + pCur->pFactory = NULL; + pCur->pSession = NULL; + rc = VINF_SUCCESS; + } + else + rc = VERR_NOT_FOUND; + + RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory); + + RTMemFree(pCur); + } + return rc; +} + + +/** + * Queries a component factory. + * + * @returns VBox status code. + * @retval VERR_INVALID_PARAMETER on invalid parameter. + * @retval VERR_INVALID_POINTER on invalid pointer parameter. + * @retval VERR_SUPDRV_COMPONENT_NOT_FOUND if the component factory wasn't found. + * @retval VERR_SUPDRV_INTERFACE_NOT_SUPPORTED if the interface wasn't supported. + * + * @param pSession The SUPDRV session. + * @param pszName The name of the component factory. + * @param pszInterfaceUuid The UUID of the factory interface (stringified). + * @param ppvFactoryIf Where to store the factory interface. + */ +SUPR0DECL(int) SUPR0ComponentQueryFactory(PSUPDRVSESSION pSession, const char *pszName, const char *pszInterfaceUuid, void **ppvFactoryIf) +{ + const char *pszEnd; + size_t cchName; + int rc; + + /* + * Validate parameters. + */ + AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + pszEnd = memchr(pszName, '\0', RT_SIZEOFMEMB(SUPDRVFACTORY, szName)); + AssertReturn(pszEnd, VERR_INVALID_PARAMETER); + cchName = pszEnd - pszName; + + AssertPtrReturn(pszInterfaceUuid, VERR_INVALID_POINTER); + pszEnd = memchr(pszInterfaceUuid, '\0', RTUUID_STR_LENGTH); + AssertReturn(pszEnd, VERR_INVALID_PARAMETER); + + AssertPtrReturn(ppvFactoryIf, VERR_INVALID_POINTER); + *ppvFactoryIf = NULL; + + /* + * Take the lock and try all factories by this name. + */ + rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory); + if (RT_SUCCESS(rc)) + { + PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead; + rc = VERR_SUPDRV_COMPONENT_NOT_FOUND; + while (pCur) + { + if ( pCur->cchName == cchName + && !memcmp(pCur->pFactory->szName, pszName, cchName)) + { +#ifdef RT_WITH_W64_UNWIND_HACK + void *pvFactory = supdrvNtWrapQueryFactoryInterface((PFNRT)pCur->pFactory->pfnQueryFactoryInterface, pCur->pFactory, pSession, pszInterfaceUuid); +#else + void *pvFactory = pCur->pFactory->pfnQueryFactoryInterface(pCur->pFactory, pSession, pszInterfaceUuid); +#endif + if (pvFactory) + { + *ppvFactoryIf = pvFactory; + rc = VINF_SUCCESS; + break; + } + rc = VERR_SUPDRV_INTERFACE_NOT_SUPPORTED; + } + + /* next */ + pCur = pCur->pNext; + } + + RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory); + } + return rc; +} + + +/** + * Adds a memory object to the session. + * + * @returns IPRT status code. + * @param pMem Memory tracking structure containing the + * information to track. + * @param pSession The session. + */ +static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession) +{ + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + + /* + * Find free entry and record the allocation. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed < RT_ELEMENTS(pBundle->aMem)) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if (pBundle->aMem[i].MemObj == NIL_RTR0MEMOBJ) + { + pBundle->cUsed++; + pBundle->aMem[i] = *pMem; + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + return VINF_SUCCESS; + } + } + AssertFailed(); /* !!this can't be happening!!! */ + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + + /* + * Need to allocate a new bundle. + * Insert into the last entry in the bundle. + */ + pBundle = (PSUPDRVBUNDLE)RTMemAllocZ(sizeof(*pBundle)); + if (!pBundle) + return VERR_NO_MEMORY; + + /* take last entry. */ + pBundle->cUsed++; + pBundle->aMem[RT_ELEMENTS(pBundle->aMem) - 1] = *pMem; + + /* insert into list. */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + pBundle->pNext = pSession->Bundle.pNext; + pSession->Bundle.pNext = pBundle; + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + + return VINF_SUCCESS; +} + + +/** + * Releases a memory object referenced by pointer and type. + * + * @returns IPRT status code. + * @param pSession Session data. + * @param uPtr Pointer to memory. This is matched against both the R0 and R3 addresses. + * @param eType Memory type. + */ +static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType) +{ + PSUPDRVBUNDLE pBundle; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + + /* + * Validate input. + */ + if (!uPtr) + { + Log(("Illegal address %p\n", (void *)uPtr)); + return VERR_INVALID_PARAMETER; + } + + /* + * Search for the address. + */ + RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp); + for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext) + { + if (pBundle->cUsed > 0) + { + unsigned i; + for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++) + { + if ( pBundle->aMem[i].eType == eType + && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ + && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr + || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ + && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr)) + ) + { + /* Make a copy of it and release it outside the spinlock. */ + SUPDRVMEMREF Mem = pBundle->aMem[i]; + pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED; + pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ; + pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ; + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + + if (Mem.MapObjR3 != NIL_RTR0MEMOBJ) + { + int rc = RTR0MemObjFree(Mem.MapObjR3, false); + AssertRC(rc); /** @todo figure out how to handle this. */ + } + if (Mem.MemObj != NIL_RTR0MEMOBJ) + { + int rc = RTR0MemObjFree(Mem.MemObj, true /* fFreeMappings */); + AssertRC(rc); /** @todo figure out how to handle this. */ + } + return VINF_SUCCESS; + } + } + } + } + RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp); + Log(("Failed to find %p!!! (eType=%d)\n", (void *)uPtr, eType)); + return VERR_INVALID_PARAMETER; +} + + +/** + * Opens an image. If it's the first time it's opened the call must upload + * the bits using the supdrvIOCtl_LdrLoad() / SUPDRV_IOCTL_LDR_LOAD function. + * + * This is the 1st step of the loading. + * + * @returns IPRT status code. + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pReq The open request. + */ +static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq) +{ + PSUPDRVLDRIMAGE pImage; + unsigned cb; + void *pv; + LogFlow(("supdrvIOCtl_LdrOpen: szName=%s cbImage=%d\n", pReq->u.In.szName, pReq->u.In.cbImage)); + + /* + * Check if we got an instance of the image already. + */ + RTSemFastMutexRequest(pDevExt->mtxLdr); + for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext) + { + if (!strcmp(pImage->szName, pReq->u.In.szName)) + { + pImage->cUsage++; + pReq->u.Out.pvImageBase = pImage->pvImage; + pReq->u.Out.fNeedsLoading = pImage->uState == SUP_IOCTL_LDR_OPEN; + supdrvLdrAddUsage(pSession, pImage); + RTSemFastMutexRelease(pDevExt->mtxLdr); + return VINF_SUCCESS; + } + } + /* (not found - add it!) */ + + /* + * Allocate memory. + */ + cb = pReq->u.In.cbImage + sizeof(SUPDRVLDRIMAGE) + 31; + pv = RTMemExecAlloc(cb); + if (!pv) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("supdrvIOCtl_LdrOpen: RTMemExecAlloc(%u) failed\n", cb)); + return VERR_NO_MEMORY; + } + + /* + * Setup and link in the LDR stuff. + */ + pImage = (PSUPDRVLDRIMAGE)pv; + pImage->pvImage = RT_ALIGN_P(pImage + 1, 32); + pImage->cbImage = pReq->u.In.cbImage; + pImage->pfnModuleInit = NULL; + pImage->pfnModuleTerm = NULL; + pImage->pfnServiceReqHandler = NULL; + pImage->uState = SUP_IOCTL_LDR_OPEN; + pImage->cUsage = 1; + strcpy(pImage->szName, pReq->u.In.szName); + + pImage->pNext = pDevExt->pLdrImages; + pDevExt->pLdrImages = pImage; + + supdrvLdrAddUsage(pSession, pImage); + + pReq->u.Out.pvImageBase = pImage->pvImage; + pReq->u.Out.fNeedsLoading = true; + RTSemFastMutexRelease(pDevExt->mtxLdr); + +#if defined(RT_OS_WINDOWS) && defined(DEBUG) + SUPR0Printf("VBoxDrv: windbg> .reload /f %s=%#p\n", pImage->szName, pImage->pvImage); +#endif + return VINF_SUCCESS; +} + + +/** + * Loads the image bits. + * + * This is the 2nd step of the loading. + * + * @returns IPRT status code. + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pReq The request. + */ +static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq) +{ + PSUPDRVLDRUSAGE pUsage; + PSUPDRVLDRIMAGE pImage; + int rc; + LogFlow(("supdrvIOCtl_LdrLoad: pvImageBase=%p cbImage=%d\n", pReq->u.In.pvImageBase, pReq->u.In.cbImage)); + + /* + * Find the ldr image. + */ + RTSemFastMutexRequest(pDevExt->mtxLdr); + pUsage = pSession->pLdrUsage; + while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase) + pUsage = pUsage->pNext; + if (!pUsage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_LOAD: couldn't find image!\n")); + return VERR_INVALID_HANDLE; + } + pImage = pUsage->pImage; + if (pImage->cbImage != pReq->u.In.cbImage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_LOAD: image size mismatch!! %d(prep) != %d(load)\n", pImage->cbImage, pReq->u.In.cbImage)); + return VERR_INVALID_HANDLE; + } + if (pImage->uState != SUP_IOCTL_LDR_OPEN) + { + unsigned uState = pImage->uState; + RTSemFastMutexRelease(pDevExt->mtxLdr); + if (uState != SUP_IOCTL_LDR_LOAD) + AssertMsgFailed(("SUP_IOCTL_LDR_LOAD: invalid image state %d (%#x)!\n", uState, uState)); + return SUPDRV_ERR_ALREADY_LOADED; + } + switch (pReq->u.In.eEPType) + { + case SUPLDRLOADEP_NOTHING: + break; + + case SUPLDRLOADEP_VMMR0: + if ( !pReq->u.In.EP.VMMR0.pvVMMR0 + || !pReq->u.In.EP.VMMR0.pvVMMR0EntryInt + || !pReq->u.In.EP.VMMR0.pvVMMR0EntryFast + || !pReq->u.In.EP.VMMR0.pvVMMR0EntryEx) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("NULL pointer: pvVMMR0=%p pvVMMR0EntryInt=%p pvVMMR0EntryFast=%p pvVMMR0EntryEx=%p!\n", + pReq->u.In.EP.VMMR0.pvVMMR0, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt, + pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx)); + return VERR_INVALID_PARAMETER; + } + /** @todo validate pReq->u.In.EP.VMMR0.pvVMMR0 against pvImage! */ + if ( (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryInt - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage + || (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryFast - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage + || (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryEx - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("Out of range (%p LB %#x): pvVMMR0EntryInt=%p, pvVMMR0EntryFast=%p or pvVMMR0EntryEx=%p is NULL!\n", + pImage->pvImage, pReq->u.In.cbImage, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt, + pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx)); + return VERR_INVALID_PARAMETER; + } + break; + + case SUPLDRLOADEP_SERVICE: + if (!pReq->u.In.EP.Service.pfnServiceReq) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("NULL pointer: pfnServiceReq=%p!\n", pReq->u.In.EP.Service.pfnServiceReq)); + return VERR_INVALID_PARAMETER; + } + if ((uintptr_t)pReq->u.In.EP.Service.pfnServiceReq - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("Out of range (%p LB %#x): pfnServiceReq=%p, pvVMMR0EntryFast=%p or pvVMMR0EntryEx=%p is NULL!\n", + pImage->pvImage, pReq->u.In.cbImage, pReq->u.In.EP.Service.pfnServiceReq)); + return VERR_INVALID_PARAMETER; + } + if ( pReq->u.In.EP.Service.apvReserved[0] != NIL_RTR0PTR + || pReq->u.In.EP.Service.apvReserved[1] != NIL_RTR0PTR + || pReq->u.In.EP.Service.apvReserved[2] != NIL_RTR0PTR) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("Out of range (%p LB %#x): apvReserved={%p,%p,%p} MBZ!\n", + pImage->pvImage, pReq->u.In.cbImage, + pReq->u.In.EP.Service.apvReserved[0], + pReq->u.In.EP.Service.apvReserved[1], + pReq->u.In.EP.Service.apvReserved[2])); + return VERR_INVALID_PARAMETER; + } + break; + + default: + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("Invalid eEPType=%d\n", pReq->u.In.eEPType)); + return VERR_INVALID_PARAMETER; + } + if ( pReq->u.In.pfnModuleInit + && (uintptr_t)pReq->u.In.pfnModuleInit - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_LOAD: pfnModuleInit=%p is outside the image (%p %d bytes)\n", + pReq->u.In.pfnModuleInit, pImage->pvImage, pReq->u.In.cbImage)); + return VERR_INVALID_PARAMETER; + } + if ( pReq->u.In.pfnModuleTerm + && (uintptr_t)pReq->u.In.pfnModuleTerm - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_LOAD: pfnModuleTerm=%p is outside the image (%p %d bytes)\n", + pReq->u.In.pfnModuleTerm, pImage->pvImage, pReq->u.In.cbImage)); + return VERR_INVALID_PARAMETER; + } + + /* + * Copy the memory. + */ + /* no need to do try/except as this is a buffered request. */ + memcpy(pImage->pvImage, &pReq->u.In.achImage[0], pImage->cbImage); + pImage->uState = SUP_IOCTL_LDR_LOAD; + pImage->pfnModuleInit = pReq->u.In.pfnModuleInit; + pImage->pfnModuleTerm = pReq->u.In.pfnModuleTerm; + pImage->offSymbols = pReq->u.In.offSymbols; + pImage->cSymbols = pReq->u.In.cSymbols; + pImage->offStrTab = pReq->u.In.offStrTab; + pImage->cbStrTab = pReq->u.In.cbStrTab; + + /* + * Update any entry points. + */ + switch (pReq->u.In.eEPType) + { + default: + case SUPLDRLOADEP_NOTHING: + rc = VINF_SUCCESS; + break; + case SUPLDRLOADEP_VMMR0: + rc = supdrvLdrSetVMMR0EPs(pDevExt, pReq->u.In.EP.VMMR0.pvVMMR0, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt, + pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx); + break; + case SUPLDRLOADEP_SERVICE: + pImage->pfnServiceReqHandler = pReq->u.In.EP.Service.pfnServiceReq; + rc = VINF_SUCCESS; + break; + } + + /* + * On success call the module initialization. + */ + LogFlow(("supdrvIOCtl_LdrLoad: pfnModuleInit=%p\n", pImage->pfnModuleInit)); + if (RT_SUCCESS(rc) && pImage->pfnModuleInit) + { + Log(("supdrvIOCtl_LdrLoad: calling pfnModuleInit=%p\n", pImage->pfnModuleInit)); +#ifdef RT_WITH_W64_UNWIND_HACK + rc = supdrvNtWrapModuleInit((PFNRT)pImage->pfnModuleInit); +#else + rc = pImage->pfnModuleInit(); +#endif + if (rc && pDevExt->pvVMMR0 == pImage->pvImage) + supdrvLdrUnsetVMMR0EPs(pDevExt); + } + + if (rc) + pImage->uState = SUP_IOCTL_LDR_OPEN; + + RTSemFastMutexRelease(pDevExt->mtxLdr); + return rc; +} + + +/** + * Frees a previously loaded (prep'ed) image. + * + * @returns IPRT status code. + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pReq The request. + */ +static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq) +{ + int rc; + PSUPDRVLDRUSAGE pUsagePrev; + PSUPDRVLDRUSAGE pUsage; + PSUPDRVLDRIMAGE pImage; + LogFlow(("supdrvIOCtl_LdrFree: pvImageBase=%p\n", pReq->u.In.pvImageBase)); + + /* + * Find the ldr image. + */ + RTSemFastMutexRequest(pDevExt->mtxLdr); + pUsagePrev = NULL; + pUsage = pSession->pLdrUsage; + while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase) + { + pUsagePrev = pUsage; + pUsage = pUsage->pNext; + } + if (!pUsage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_FREE: couldn't find image!\n")); + return VERR_INVALID_HANDLE; + } + + /* + * Check if we can remove anything. + */ + rc = VINF_SUCCESS; + pImage = pUsage->pImage; + if (pImage->cUsage <= 1 || pUsage->cUsage <= 1) + { + /* + * Check if there are any objects with destructors in the image, if + * so leave it for the session cleanup routine so we get a chance to + * clean things up in the right order and not leave them all dangling. + */ + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + if (pImage->cUsage <= 1) + { + PSUPDRVOBJ pObj; + for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext) + if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage)) + { + rc = VERR_DANGLING_OBJECTS; + break; + } + } + else + { + PSUPDRVUSAGE pGenUsage; + for (pGenUsage = pSession->pUsage; pGenUsage; pGenUsage = pGenUsage->pNext) + if (RT_UNLIKELY((uintptr_t)pGenUsage->pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage)) + { + rc = VERR_DANGLING_OBJECTS; + break; + } + } + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + if (rc == VINF_SUCCESS) + { + /* unlink it */ + if (pUsagePrev) + pUsagePrev->pNext = pUsage->pNext; + else + pSession->pLdrUsage = pUsage->pNext; + + /* free it */ + pUsage->pImage = NULL; + pUsage->pNext = NULL; + RTMemFree(pUsage); + + /* + * Derefrence the image. + */ + if (pImage->cUsage <= 1) + supdrvLdrFree(pDevExt, pImage); + else + pImage->cUsage--; + } + else + { + Log(("supdrvIOCtl_LdrFree: Dangling objects in %p/%s!\n", pImage->pvImage, pImage->szName)); + rc = VINF_SUCCESS; /** @todo BRANCH-2.1: remove this after branching. */ + } + } + else + { + /* + * Dereference both image and usage. + */ + pImage->cUsage--; + pUsage->cUsage--; + } + + RTSemFastMutexRelease(pDevExt->mtxLdr); + return rc; +} + + +/** + * Gets the address of a symbol in an open image. + * + * @returns 0 on success. + * @returns SUPDRV_ERR_* on failure. + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pReq The request buffer. + */ +static int supdrvIOCtl_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq) +{ + PSUPDRVLDRIMAGE pImage; + PSUPDRVLDRUSAGE pUsage; + uint32_t i; + PSUPLDRSYM paSyms; + const char *pchStrings; + const size_t cbSymbol = strlen(pReq->u.In.szSymbol) + 1; + void *pvSymbol = NULL; + int rc = VERR_GENERAL_FAILURE; + Log3(("supdrvIOCtl_LdrGetSymbol: pvImageBase=%p szSymbol=\"%s\"\n", pReq->u.In.pvImageBase, pReq->u.In.szSymbol)); + + /* + * Find the ldr image. + */ + RTSemFastMutexRequest(pDevExt->mtxLdr); + pUsage = pSession->pLdrUsage; + while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase) + pUsage = pUsage->pNext; + if (!pUsage) + { + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_GET_SYMBOL: couldn't find image!\n")); + return VERR_INVALID_HANDLE; + } + pImage = pUsage->pImage; + if (pImage->uState != SUP_IOCTL_LDR_LOAD) + { + unsigned uState = pImage->uState; + RTSemFastMutexRelease(pDevExt->mtxLdr); + Log(("SUP_IOCTL_LDR_GET_SYMBOL: invalid image state %d (%#x)!\n", uState, uState)); NOREF(uState); + return VERR_ALREADY_LOADED; + } + + /* + * Search the symbol strings. + */ + pchStrings = (const char *)((uint8_t *)pImage->pvImage + pImage->offStrTab); + paSyms = (PSUPLDRSYM)((uint8_t *)pImage->pvImage + pImage->offSymbols); + for (i = 0; i < pImage->cSymbols; i++) + { + if ( paSyms[i].offSymbol < pImage->cbImage /* paranoia */ + && paSyms[i].offName + cbSymbol <= pImage->cbStrTab + && !memcmp(pchStrings + paSyms[i].offName, pReq->u.In.szSymbol, cbSymbol)) + { + pvSymbol = (uint8_t *)pImage->pvImage + paSyms[i].offSymbol; + rc = VINF_SUCCESS; + break; + } + } + RTSemFastMutexRelease(pDevExt->mtxLdr); + pReq->u.Out.pvSymbol = pvSymbol; + return rc; +} + + +/** + * Gets the address of a symbol in an open image or the support driver. + * + * @returns VINF_SUCCESS on success. + * @returns + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pReq The request buffer. + */ +static int supdrvIDC_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQGETSYM pReq) +{ + int rc = VINF_SUCCESS; + const char *pszSymbol = pReq->u.In.pszSymbol; + const char *pszModule = pReq->u.In.pszModule; + size_t cbSymbol; + char const *pszEnd; + uint32_t i; + + /* + * Input validation. + */ + AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER); + pszEnd = (char *)memchr(pszSymbol, '\0', 512); + AssertReturn(pszEnd, VERR_INVALID_PARAMETER); + cbSymbol = pszEnd - pszSymbol + 1; + + if (pszModule) + { + AssertPtrReturn(pszModule, VERR_INVALID_POINTER); + pszEnd = (char *)memchr(pszModule, '\0', 64); + AssertReturn(pszEnd, VERR_INVALID_PARAMETER); + } + Log3(("supdrvIDC_LdrGetSymbol: pszModule=%p:{%s} pszSymbol=%p:{%s}\n", pszModule, pszModule, pszSymbol, pszSymbol)); + + + if ( !pszModule + || !strcmp(pszModule, "SupDrv")) + { + /* + * Search the support driver export table. + */ + for (i = 0; i < RT_ELEMENTS(g_aFunctions); i++) + if (!strcmp(g_aFunctions[i].szName, pszSymbol)) + { + pReq->u.Out.pfnSymbol = g_aFunctions[i].pfn; + break; + } + } + else + { + /* + * Find the loader image. + */ + PSUPDRVLDRIMAGE pImage; + + RTSemFastMutexRequest(pDevExt->mtxLdr); + + for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext) + if (!strcmp(pImage->szName, pszModule)) + break; + if (pImage && pImage->uState == SUP_IOCTL_LDR_LOAD) + { + /* + * Search the symbol strings. + */ + const char *pchStrings = (const char *)((uint8_t *)pImage->pvImage + pImage->offStrTab); + PCSUPLDRSYM paSyms = (PCSUPLDRSYM)((uint8_t *)pImage->pvImage + pImage->offSymbols); + for (i = 0; i < pImage->cSymbols; i++) + { + if ( paSyms[i].offSymbol < pImage->cbImage /* paranoia */ + && paSyms[i].offName + cbSymbol <= pImage->cbStrTab + && !memcmp(pchStrings + paSyms[i].offName, pszSymbol, cbSymbol)) + { + /* + * Found it! Calc the symbol address and add a reference to the module. + */ + pReq->u.Out.pfnSymbol = (PFNRT)((uint8_t *)pImage->pvImage + paSyms[i].offSymbol); + rc = supdrvLdrAddUsage(pSession, pImage); + break; + } + } + } + else + rc = pImage ? VERR_WRONG_ORDER : VERR_MODULE_NOT_FOUND; + + RTSemFastMutexRelease(pDevExt->mtxLdr); + } + return rc; +} + + +/** + * Updates the VMMR0 entry point pointers. + * + * @returns IPRT status code. + * @param pDevExt Device globals. + * @param pSession Session data. + * @param pVMMR0 VMMR0 image handle. + * @param pvVMMR0EntryInt VMMR0EntryInt address. + * @param pvVMMR0EntryFast VMMR0EntryFast address. + * @param pvVMMR0EntryEx VMMR0EntryEx address. + * @remark Caller must own the loader mutex. + */ +static int supdrvLdrSetVMMR0EPs(PSUPDRVDEVEXT pDevExt, void *pvVMMR0, void *pvVMMR0EntryInt, void *pvVMMR0EntryFast, void *pvVMMR0EntryEx) +{ + int rc = VINF_SUCCESS; + LogFlow(("supdrvLdrSetR0EP pvVMMR0=%p pvVMMR0EntryInt=%p\n", pvVMMR0, pvVMMR0EntryInt)); + + + /* + * Check if not yet set. + */ + if (!pDevExt->pvVMMR0) + { + pDevExt->pvVMMR0 = pvVMMR0; + pDevExt->pfnVMMR0EntryInt = pvVMMR0EntryInt; + pDevExt->pfnVMMR0EntryFast = pvVMMR0EntryFast; + pDevExt->pfnVMMR0EntryEx = pvVMMR0EntryEx; + } + else + { + /* + * Return failure or success depending on whether the values match or not. + */ + if ( pDevExt->pvVMMR0 != pvVMMR0 + || (void *)pDevExt->pfnVMMR0EntryInt != pvVMMR0EntryInt + || (void *)pDevExt->pfnVMMR0EntryFast != pvVMMR0EntryFast + || (void *)pDevExt->pfnVMMR0EntryEx != pvVMMR0EntryEx) + { + AssertMsgFailed(("SUP_IOCTL_LDR_SETR0EP: Already set pointing to a different module!\n")); + rc = VERR_INVALID_PARAMETER; + } + } + return rc; +} + + +/** + * Unsets the VMMR0 entry point installed by supdrvLdrSetR0EP. + * + * @param pDevExt Device globals. + */ +static void supdrvLdrUnsetVMMR0EPs(PSUPDRVDEVEXT pDevExt) +{ + pDevExt->pvVMMR0 = NULL; + pDevExt->pfnVMMR0EntryInt = NULL; + pDevExt->pfnVMMR0EntryFast = NULL; + pDevExt->pfnVMMR0EntryEx = NULL; +} + + +/** + * Adds a usage reference in the specified session of an image. + * + * Called while owning the loader semaphore. + * + * @returns VINF_SUCCESS on success and VERR_NO_MEMORY on failure. + * @param pSession Session in question. + * @param pImage Image which the session is using. + */ +static int supdrvLdrAddUsage(PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage) +{ + PSUPDRVLDRUSAGE pUsage; + LogFlow(("supdrvLdrAddUsage: pImage=%p\n", pImage)); + + /* + * Referenced it already? + */ + pUsage = pSession->pLdrUsage; + while (pUsage) + { + if (pUsage->pImage == pImage) + { + pUsage->cUsage++; + return VINF_SUCCESS; + } + pUsage = pUsage->pNext; + } + + /* + * Allocate new usage record. + */ + pUsage = (PSUPDRVLDRUSAGE)RTMemAlloc(sizeof(*pUsage)); + AssertReturn(pUsage, VERR_NO_MEMORY); + pUsage->cUsage = 1; + pUsage->pImage = pImage; + pUsage->pNext = pSession->pLdrUsage; + pSession->pLdrUsage = pUsage; + return VINF_SUCCESS; +} + + +/** + * Frees a load image. + * + * @param pDevExt Pointer to device extension. + * @param pImage Pointer to the image we're gonna free. + * This image must exit! + * @remark The caller MUST own SUPDRVDEVEXT::mtxLdr! + */ +static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage) +{ + PSUPDRVLDRIMAGE pImagePrev; + LogFlow(("supdrvLdrFree: pImage=%p\n", pImage)); + + /* find it - arg. should've used doubly linked list. */ + Assert(pDevExt->pLdrImages); + pImagePrev = NULL; + if (pDevExt->pLdrImages != pImage) + { + pImagePrev = pDevExt->pLdrImages; + while (pImagePrev->pNext != pImage) + pImagePrev = pImagePrev->pNext; + Assert(pImagePrev->pNext == pImage); + } + + /* unlink */ + if (pImagePrev) + pImagePrev->pNext = pImage->pNext; + else + pDevExt->pLdrImages = pImage->pNext; + + /* check if this is VMMR0.r0 unset its entry point pointers. */ + if (pDevExt->pvVMMR0 == pImage->pvImage) + supdrvLdrUnsetVMMR0EPs(pDevExt); + + /* check for objects with destructors in this image. (Shouldn't happen.) */ + if (pDevExt->pObjs) + { + unsigned cObjs = 0; + PSUPDRVOBJ pObj; + RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp); + for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext) + if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage)) + { + pObj->pfnDestructor = NULL; + cObjs++; + } + RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp); + if (cObjs) + OSDBGPRINT(("supdrvLdrFree: Image '%s' has %d dangling objects!\n", pImage->szName, cObjs)); + } + + /* call termination function if fully loaded. */ + if ( pImage->pfnModuleTerm + && pImage->uState == SUP_IOCTL_LDR_LOAD) + { + LogFlow(("supdrvIOCtl_LdrLoad: calling pfnModuleTerm=%p\n", pImage->pfnModuleTerm)); +#ifdef RT_WITH_W64_UNWIND_HACK + supdrvNtWrapModuleTerm(pImage->pfnModuleTerm); +#else + pImage->pfnModuleTerm(); +#endif + } + + /* free the image */ + pImage->cUsage = 0; + pImage->pNext = 0; + pImage->uState = SUP_IOCTL_LDR_FREE; + RTMemExecFree(pImage); +} + + +/** + * Implements the service call request. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The calling session. + * @param pReq The request packet, valid. + */ +static int supdrvIOCtl_CallServiceModule(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPCALLSERVICE pReq) +{ +#if !defined(RT_OS_WINDOWS) || defined(DEBUG) + int rc; + + /* + * Find the module first in the module referenced by the calling session. + */ + rc = RTSemFastMutexRequest(pDevExt->mtxLdr); + if (RT_SUCCESS(rc)) + { + PFNSUPR0SERVICEREQHANDLER pfnServiceReqHandler = NULL; + PSUPDRVLDRUSAGE pUsage; + + for (pUsage = pSession->pLdrUsage; pUsage; pUsage = pUsage->pNext) + if ( pUsage->pImage->pfnServiceReqHandler + && !strcmp(pUsage->pImage->szName, pReq->u.In.szName)) + { + pfnServiceReqHandler = pUsage->pImage->pfnServiceReqHandler; + break; + } + RTSemFastMutexRelease(pDevExt->mtxLdr); + + if (pfnServiceReqHandler) + { + /* + * Call it. + */ + if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_SERVICE_SIZE(0)) +#ifdef RT_WITH_W64_UNWIND_HACK + rc = supdrvNtWrapServiceReqHandler((PFNRT)pfnServiceReqHandler, pSession, pReq->u.In.uOperation, pReq->u.In.u64Arg, NULL); +#else + rc = pfnServiceReqHandler(pSession, pReq->u.In.uOperation, pReq->u.In.u64Arg, NULL); +#endif + else +#ifdef RT_WITH_W64_UNWIND_HACK + rc = supdrvNtWrapServiceReqHandler((PFNRT)pfnServiceReqHandler, pSession, pReq->u.In.uOperation, + pReq->u.In.u64Arg, (PSUPR0SERVICEREQHDR)&pReq->abReqPkt[0]); +#else + rc = pfnServiceReqHandler(pSession, pReq->u.In.uOperation, pReq->u.In.u64Arg, (PSUPR0SERVICEREQHDR)&pReq->abReqPkt[0]); +#endif + } + else + rc = VERR_SUPDRV_SERVICE_NOT_FOUND; + } + + /* log it */ + if ( RT_FAILURE(rc) + && rc != VERR_INTERRUPTED + && rc != VERR_TIMEOUT) + Log(("SUP_IOCTL_CALL_SERVICE: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + else + Log4(("SUP_IOCTL_CALL_SERVICE: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n", + rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf())); + return rc; +#else /* RT_OS_WINDOWS && !DEBUG */ + return VERR_NOT_IMPLEMENTED; +#endif /* RT_OS_WINDOWS && !DEBUG */ +} + + +/** + * Gets the paging mode of the current CPU. + * + * @returns Paging mode, SUPPAGEINGMODE_INVALID on error. + */ +SUPR0DECL(SUPPAGINGMODE) SUPR0GetPagingMode(void) +{ + SUPPAGINGMODE enmMode; + + RTR0UINTREG cr0 = ASMGetCR0(); + if ((cr0 & (X86_CR0_PG | X86_CR0_PE)) != (X86_CR0_PG | X86_CR0_PE)) + enmMode = SUPPAGINGMODE_INVALID; + else + { + RTR0UINTREG cr4 = ASMGetCR4(); + uint32_t fNXEPlusLMA = 0; + if (cr4 & X86_CR4_PAE) + { + uint32_t fAmdFeatures = ASMCpuId_EDX(0x80000001); + if (fAmdFeatures & (X86_CPUID_AMD_FEATURE_EDX_NX | X86_CPUID_AMD_FEATURE_EDX_LONG_MODE)) + { + uint64_t efer = ASMRdMsr(MSR_K6_EFER); + if ((fAmdFeatures & X86_CPUID_AMD_FEATURE_EDX_NX) && (efer & MSR_K6_EFER_NXE)) + fNXEPlusLMA |= RT_BIT(0); + if ((fAmdFeatures & X86_CPUID_AMD_FEATURE_EDX_LONG_MODE) && (efer & MSR_K6_EFER_LMA)) + fNXEPlusLMA |= RT_BIT(1); + } + } + + switch ((cr4 & (X86_CR4_PAE | X86_CR4_PGE)) | fNXEPlusLMA) + { + case 0: + enmMode = SUPPAGINGMODE_32_BIT; + break; + + case X86_CR4_PGE: + enmMode = SUPPAGINGMODE_32_BIT_GLOBAL; + break; + + case X86_CR4_PAE: + enmMode = SUPPAGINGMODE_PAE; + break; + + case X86_CR4_PAE | RT_BIT(0): + enmMode = SUPPAGINGMODE_PAE_NX; + break; + + case X86_CR4_PAE | X86_CR4_PGE: + enmMode = SUPPAGINGMODE_PAE_GLOBAL; + break; + + case X86_CR4_PAE | X86_CR4_PGE | RT_BIT(0): + enmMode = SUPPAGINGMODE_PAE_GLOBAL; + break; + + case RT_BIT(1) | X86_CR4_PAE: + enmMode = SUPPAGINGMODE_AMD64; + break; + + case RT_BIT(1) | X86_CR4_PAE | RT_BIT(0): + enmMode = SUPPAGINGMODE_AMD64_NX; + break; + + case RT_BIT(1) | X86_CR4_PAE | X86_CR4_PGE: + enmMode = SUPPAGINGMODE_AMD64_GLOBAL; + break; + + case RT_BIT(1) | X86_CR4_PAE | X86_CR4_PGE | RT_BIT(0): + enmMode = SUPPAGINGMODE_AMD64_GLOBAL_NX; + break; + + default: + AssertMsgFailed(("Cannot happen! cr4=%#x fNXEPlusLMA=%d\n", cr4, fNXEPlusLMA)); + enmMode = SUPPAGINGMODE_INVALID; + break; + } + } + return enmMode; +} + + +/** + * Enables or disabled hardware virtualization extensions using native OS APIs. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_SUPPORTED if not supported by the native OS. + * + * @param fEnable Whether to enable or disable. + */ +SUPR0DECL(int) SUPR0EnableVTx(bool fEnable) +{ +#ifdef RT_OS_DARWIN + return supdrvOSEnableVTx(fEnable); +#else + return VERR_NOT_SUPPORTED; +#endif +} + + +/** + * Creates the GIP. + * + * @returns VBox status code. + * @param pDevExt Instance data. GIP stuff may be updated. + */ +static int supdrvGipCreate(PSUPDRVDEVEXT pDevExt) +{ + PSUPGLOBALINFOPAGE pGip; + RTHCPHYS HCPhysGip; + uint32_t u32SystemResolution; + uint32_t u32Interval; + int rc; + + LogFlow(("supdrvGipCreate:\n")); + + /* assert order */ + Assert(pDevExt->u32SystemTimerGranularityGrant == 0); + Assert(pDevExt->GipMemObj == NIL_RTR0MEMOBJ); + Assert(!pDevExt->pGipTimer); + + /* + * Allocate a suitable page with a default kernel mapping. + */ + rc = RTR0MemObjAllocLow(&pDevExt->GipMemObj, PAGE_SIZE, false); + if (RT_FAILURE(rc)) + { + OSDBGPRINT(("supdrvGipCreate: failed to allocate the GIP page. rc=%d\n", rc)); + return rc; + } + pGip = (PSUPGLOBALINFOPAGE)RTR0MemObjAddress(pDevExt->GipMemObj); AssertPtr(pGip); + HCPhysGip = RTR0MemObjGetPagePhysAddr(pDevExt->GipMemObj, 0); Assert(HCPhysGip != NIL_RTHCPHYS); + +#if 0 /** @todo Disabled this as we didn't used to do it before and causes unnecessary stress on laptops. + * It only applies to Windows and should probably revisited later, if possible made part of the + * timer code (return min granularity in RTTimerGetSystemGranularity and set it in RTTimerStart). */ + /* + * Try bump up the system timer resolution. + * The more interrupts the better... + */ + if ( RT_SUCCESS(RTTimerRequestSystemGranularity( 488281 /* 2048 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 500000 /* 2000 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 1953125 /* 512 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 2000000 /* 500 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 3906250 /* 256 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 4000000 /* 250 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity( 7812500 /* 128 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity(10000000 /* 100 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity(15625000 /* 64 HZ */, &u32SystemResolution)) + || RT_SUCCESS(RTTimerRequestSystemGranularity(31250000 /* 32 HZ */, &u32SystemResolution)) + ) + { + Assert(RTTimerGetSystemGranularity() <= u32SystemResolution); + pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution; + } +#endif + + /* + * Find a reasonable update interval and initialize the structure. + */ + u32Interval = u32SystemResolution = RTTimerGetSystemGranularity(); + while (u32Interval < 10000000 /* 10 ms */) + u32Interval += u32SystemResolution; + + supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), 1000000000 / u32Interval /*=Hz*/); + + /* + * Create the timer. + * If CPU_ALL isn't supported we'll have to fall back to synchronous mode. + */ + if (pGip->u32Mode == SUPGIPMODE_ASYNC_TSC) + { + rc = RTTimerCreateEx(&pDevExt->pGipTimer, u32Interval, RTTIMER_FLAGS_CPU_ALL, supdrvGipAsyncTimer, pDevExt); + if (rc == VERR_NOT_SUPPORTED) + { + OSDBGPRINT(("supdrvGipCreate: omni timer not supported, falling back to synchronous mode\n")); + pGip->u32Mode = SUPGIPMODE_SYNC_TSC; + } + } + if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC) + rc = RTTimerCreateEx(&pDevExt->pGipTimer, u32Interval, 0, supdrvGipSyncTimer, pDevExt); + if (RT_SUCCESS(rc)) + { + if (pGip->u32Mode == SUPGIPMODE_ASYNC_TSC) + rc = RTMpNotificationRegister(supdrvGipMpEvent, pDevExt); + if (RT_SUCCESS(rc)) + { + /* + * We're good. + */ + dprintf(("supdrvGipCreate: %ld ns interval.\n", (long)u32Interval)); + return VINF_SUCCESS; + } + + OSDBGPRINT(("supdrvGipCreate: failed register MP event notfication. rc=%d\n", rc)); + } + else + { + OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %ld ns interval. rc=%d\n", (long)u32Interval, rc)); + Assert(!pDevExt->pGipTimer); + } + supdrvGipDestroy(pDevExt); + return rc; +} + + +/** + * Terminates the GIP. + * + * @param pDevExt Instance data. GIP stuff may be updated. + */ +static void supdrvGipDestroy(PSUPDRVDEVEXT pDevExt) +{ + int rc; +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("supdrvGipDestroy: pDevExt=%p pGip=%p pGipTimer=%p GipMemObj=%p\n", pDevExt, + pDevExt->GipMemObj != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pDevExt->GipMemObj) : NULL, + pDevExt->pGipTimer, pDevExt->GipMemObj)); +#endif + + /* + * Invalid the GIP data. + */ + if (pDevExt->pGip) + { + supdrvGipTerm(pDevExt->pGip); + pDevExt->pGip = NULL; + } + + /* + * Destroy the timer and free the GIP memory object. + */ + if (pDevExt->pGipTimer) + { + rc = RTTimerDestroy(pDevExt->pGipTimer); AssertRC(rc); + pDevExt->pGipTimer = NULL; + } + + if (pDevExt->GipMemObj != NIL_RTR0MEMOBJ) + { + rc = RTR0MemObjFree(pDevExt->GipMemObj, true /* free mappings */); AssertRC(rc); + pDevExt->GipMemObj = NIL_RTR0MEMOBJ; + } + + /* + * Finally, release the system timer resolution request if one succeeded. + */ + if (pDevExt->u32SystemTimerGranularityGrant) + { + rc = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant); AssertRC(rc); + pDevExt->u32SystemTimerGranularityGrant = 0; + } +} + + +/** + * Timer callback function sync GIP mode. + * @param pTimer The timer. + * @param pvUser The device extension. + */ +static DECLCALLBACK(void) supdrvGipSyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick) +{ + RTCCUINTREG fOldFlags = ASMIntDisableFlags(); /* No interruptions please (real problem on S10). */ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; + + supdrvGipUpdate(pDevExt->pGip, RTTimeSystemNanoTS()); + + ASMSetFlags(fOldFlags); +} + + +/** + * Timer callback function for async GIP mode. + * @param pTimer The timer. + * @param pvUser The device extension. + */ +static DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick) +{ + RTCCUINTREG fOldFlags = ASMIntDisableFlags(); /* No interruptions please (real problem on S10). */ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; + RTCPUID idCpu = RTMpCpuId(); + uint64_t NanoTS = RTTimeSystemNanoTS(); + + /** @todo reset the transaction number and whatnot when iTick == 1. */ + if (pDevExt->idGipMaster == idCpu) + supdrvGipUpdate(pDevExt->pGip, NanoTS); + else + supdrvGipUpdatePerCpu(pDevExt->pGip, NanoTS, ASMGetApicId()); + + ASMSetFlags(fOldFlags); +} + + +/** + * Multiprocessor event notification callback. + * + * This is used to make sue that the GIP master gets passed on to + * another CPU. + * + * @param enmEvent The event. + * @param idCpu The cpu it applies to. + * @param pvUser Pointer to the device extension. + */ +static DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; + if (enmEvent == RTMPEVENT_OFFLINE) + { + RTCPUID idGipMaster; + ASMAtomicReadSize(&pDevExt->idGipMaster, &idGipMaster); + if (idGipMaster == idCpu) + { + /* + * Find a new GIP master. + */ + bool fIgnored; + unsigned i; + RTCPUID idNewGipMaster = NIL_RTCPUID; + RTCPUSET OnlineCpus; + RTMpGetOnlineSet(&OnlineCpus); + + for (i = 0; i < RTCPUSET_MAX_CPUS; i++) + { + RTCPUID idCurCpu = RTMpCpuIdFromSetIndex(i); + if ( RTCpuSetIsMember(&OnlineCpus, idCurCpu) + && idCurCpu != idGipMaster) + { + idNewGipMaster = idCurCpu; + break; + } + } + + dprintf(("supdrvGipMpEvent: Gip master %#lx -> %#lx\n", (long)idGipMaster, (long)idNewGipMaster)); + ASMAtomicCmpXchgSize(&pDevExt->idGipMaster, idNewGipMaster, idGipMaster, fIgnored); + NOREF(fIgnored); + } + } +} + + +/** + * Initializes the GIP data. + * + * @returns IPRT status code. + * @param pDevExt Pointer to the device instance data. + * @param pGip Pointer to the read-write kernel mapping of the GIP. + * @param HCPhys The physical address of the GIP. + * @param u64NanoTS The current nanosecond timestamp. + * @param uUpdateHz The update freqence. + */ +int VBOXCALL supdrvGipInit(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, RTHCPHYS HCPhys, uint64_t u64NanoTS, unsigned uUpdateHz) +{ + unsigned i; +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz)); +#else + LogFlow(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz)); +#endif + + /* + * Initialize the structure. + */ + memset(pGip, 0, PAGE_SIZE); + pGip->u32Magic = SUPGLOBALINFOPAGE_MAGIC; + pGip->u32Version = SUPGLOBALINFOPAGE_VERSION; + pGip->u32Mode = supdrvGipDeterminTscMode(pDevExt); + pGip->u32UpdateHz = uUpdateHz; + pGip->u32UpdateIntervalNS = 1000000000 / uUpdateHz; + pGip->u64NanoTSLastUpdateHz = u64NanoTS; + + for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++) + { + pGip->aCPUs[i].u32TransactionId = 2; + pGip->aCPUs[i].u64NanoTS = u64NanoTS; + pGip->aCPUs[i].u64TSC = ASMReadTSC(); + + /* + * We don't know the following values until we've executed updates. + * So, we'll just insert very high values. + */ + pGip->aCPUs[i].u64CpuHz = _4G + 1; + pGip->aCPUs[i].u32UpdateIntervalTSC = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[0] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[1] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[2] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[3] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[4] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[5] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[6] = _2G / 4; + pGip->aCPUs[i].au32TSCHistory[7] = _2G / 4; + } + + /* + * Link it to the device extension. + */ + pDevExt->pGip = pGip; + pDevExt->HCPhysGip = HCPhys; + pDevExt->cGipUsers = 0; + + return VINF_SUCCESS; +} + + +/** + * Callback used by supdrvDetermineAsyncTSC to read the TSC on a CPU. + * + * @param idCpu Ignored. + * @param pvUser1 Where to put the TSC. + * @param pvUser2 Ignored. + */ +static DECLCALLBACK(void) supdrvDetermineAsyncTscWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2) +{ +#if 1 + ASMAtomicWriteU64((uint64_t volatile *)pvUser1, ASMReadTSC()); +#else + *(uint64_t *)pvUser1 = ASMReadTSC(); +#endif +} + + +/** + * Determine if Async GIP mode is required because of TSC drift. + * + * When using the default/normal timer code it is essential that the time stamp counter + * (TSC) runs never backwards, that is, a read operation to the counter should return + * a bigger value than any previous read operation. This is guaranteed by the latest + * AMD CPUs and by newer Intel CPUs which never enter the C2 state (P4). In any other + * case we have to choose the asynchronous timer mode. + * + * @param poffMin Pointer to the determined difference between different cores. + * @return false if the time stamp counters appear to be synchron, true otherwise. + */ +bool VBOXCALL supdrvDetermineAsyncTsc(uint64_t *poffMin) +{ + /* + * Just iterate all the cpus 8 times and make sure that the TSC is + * ever increasing. We don't bother taking TSC rollover into account. + */ + RTCPUSET CpuSet; + int iLastCpu = RTCpuLastIndex(RTMpGetSet(&CpuSet)); + int iCpu; + int cLoops = 8; + bool fAsync = false; + int rc = VINF_SUCCESS; + uint64_t offMax = 0; + uint64_t offMin = ~(uint64_t)0; + uint64_t PrevTsc = ASMReadTSC(); + + while (cLoops-- > 0) + { + for (iCpu = 0; iCpu <= iLastCpu; iCpu++) + { + uint64_t CurTsc; + rc = RTMpOnSpecific(RTMpCpuIdFromSetIndex(iCpu), supdrvDetermineAsyncTscWorker, &CurTsc, NULL); + if (RT_SUCCESS(rc)) + { + if (CurTsc <= PrevTsc) + { + fAsync = true; + offMin = offMax = PrevTsc - CurTsc; + dprintf(("supdrvDetermineAsyncTsc: iCpu=%d cLoops=%d CurTsc=%llx PrevTsc=%llx\n", + iCpu, cLoops, CurTsc, PrevTsc)); + break; + } + + /* Gather statistics (except the first time). */ + if (iCpu != 0 || cLoops != 7) + { + uint64_t off = CurTsc - PrevTsc; + if (off < offMin) + offMin = off; + if (off > offMax) + offMax = off; + dprintf2(("%d/%d: off=%llx\n", cLoops, iCpu, off)); + } + + /* Next */ + PrevTsc = CurTsc; + } + else if (rc == VERR_NOT_SUPPORTED) + break; + else + AssertMsg(rc == VERR_CPU_NOT_FOUND || rc == VERR_CPU_OFFLINE, ("%d\n", rc)); + } + + /* broke out of the loop. */ + if (iCpu <= iLastCpu) + break; + } + + *poffMin = offMin; /* Almost RTMpOnSpecific profiling. */ + dprintf(("supdrvDetermineAsyncTsc: returns %d; iLastCpu=%d rc=%d offMin=%llx offMax=%llx\n", + fAsync, iLastCpu, rc, offMin, offMax)); +#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) + OSDBGPRINT(("vboxdrv: fAsync=%d offMin=%#lx offMax=%#lx\n", fAsync, (long)offMin, (long)offMax)); +#endif + return fAsync; +} + + +/** + * Determin the GIP TSC mode. + * + * @returns The most suitable TSC mode. + * @param pDevExt Pointer to the device instance data. + */ +static SUPGIPMODE supdrvGipDeterminTscMode(PSUPDRVDEVEXT pDevExt) +{ + /* + * On SMP we're faced with two problems: + * (1) There might be a skew between the CPU, so that cpu0 + * returns a TSC that is sligtly different from cpu1. + * (2) Power management (and other things) may cause the TSC + * to run at a non-constant speed, and cause the speed + * to be different on the cpus. This will result in (1). + * + * So, on SMP systems we'll have to select the ASYNC update method + * if there are symphoms of these problems. + */ + if (RTMpGetCount() > 1) + { + uint32_t uEAX, uEBX, uECX, uEDX; + uint64_t u64DiffCoresIgnored; + + /* Permit the user and/or the OS specfic bits to force async mode. */ + if (supdrvOSGetForcedAsyncTscMode(pDevExt)) + return SUPGIPMODE_ASYNC_TSC; + + /* Try check for current differences between the cpus. */ + if (supdrvDetermineAsyncTsc(&u64DiffCoresIgnored)) + return SUPGIPMODE_ASYNC_TSC; + + /* + * If the CPU supports power management and is an AMD one we + * won't trust it unless it has the TscInvariant bit is set. + */ + /* Check for "AuthenticAMD" */ + ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX); + if ( uEAX >= 1 + && uEBX == X86_CPUID_VENDOR_AMD_EBX + && uECX == X86_CPUID_VENDOR_AMD_ECX + && uEDX == X86_CPUID_VENDOR_AMD_EDX) + { + /* Check for APM support and that TscInvariant is cleared. */ + ASMCpuId(0x80000000, &uEAX, &uEBX, &uECX, &uEDX); + if (uEAX >= 0x80000007) + { + ASMCpuId(0x80000007, &uEAX, &uEBX, &uECX, &uEDX); + if ( !(uEDX & RT_BIT(8))/* TscInvariant */ + && (uEDX & 0x3e)) /* STC|TM|THERMTRIP|VID|FID. Ignore TS. */ + return SUPGIPMODE_ASYNC_TSC; + } + } + } + return SUPGIPMODE_SYNC_TSC; +} + + +/** + * Invalidates the GIP data upon termination. + * + * @param pGip Pointer to the read-write kernel mapping of the GIP. + */ +void VBOXCALL supdrvGipTerm(PSUPGLOBALINFOPAGE pGip) +{ + unsigned i; + pGip->u32Magic = 0; + for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++) + { + pGip->aCPUs[i].u64NanoTS = 0; + pGip->aCPUs[i].u64TSC = 0; + pGip->aCPUs[i].iTSCHistoryHead = 0; + } +} + + +/** + * Worker routine for supdrvGipUpdate and supdrvGipUpdatePerCpu that + * updates all the per cpu data except the transaction id. + * + * @param pGip The GIP. + * @param pGipCpu Pointer to the per cpu data. + * @param u64NanoTS The current time stamp. + */ +static void supdrvGipDoUpdateCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS) +{ + uint64_t u64TSC; + uint64_t u64TSCDelta; + uint32_t u32UpdateIntervalTSC; + uint32_t u32UpdateIntervalTSCSlack; + unsigned iTSCHistoryHead; + uint64_t u64CpuHz; + + /* + * Update the NanoTS. + */ + ASMAtomicXchgU64(&pGipCpu->u64NanoTS, u64NanoTS); + + /* + * Calc TSC delta. + */ + /** @todo validate the NanoTS delta, don't trust the OS to call us when it should... */ + u64TSC = ASMReadTSC(); + u64TSCDelta = u64TSC - pGipCpu->u64TSC; + ASMAtomicXchgU64(&pGipCpu->u64TSC, u64TSC); + + if (u64TSCDelta >> 32) + { + u64TSCDelta = pGipCpu->u32UpdateIntervalTSC; + pGipCpu->cErrors++; + } + + /* + * TSC History. + */ + Assert(RT_ELEMENTS(pGipCpu->au32TSCHistory) == 8); + + iTSCHistoryHead = (pGipCpu->iTSCHistoryHead + 1) & 7; + ASMAtomicXchgU32(&pGipCpu->iTSCHistoryHead, iTSCHistoryHead); + ASMAtomicXchgU32(&pGipCpu->au32TSCHistory[iTSCHistoryHead], (uint32_t)u64TSCDelta); + + /* + * UpdateIntervalTSC = average of last 8,2,1 intervals depending on update HZ. + */ + if (pGip->u32UpdateHz >= 1000) + { + uint32_t u32; + u32 = pGipCpu->au32TSCHistory[0]; + u32 += pGipCpu->au32TSCHistory[1]; + u32 += pGipCpu->au32TSCHistory[2]; + u32 += pGipCpu->au32TSCHistory[3]; + u32 >>= 2; + u32UpdateIntervalTSC = pGipCpu->au32TSCHistory[4]; + u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[5]; + u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[6]; + u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[7]; + u32UpdateIntervalTSC >>= 2; + u32UpdateIntervalTSC += u32; + u32UpdateIntervalTSC >>= 1; + + /* Value choosen for a 2GHz Athlon64 running linux 2.6.10/11, . */ + u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 14; + } + else if (pGip->u32UpdateHz >= 90) + { + u32UpdateIntervalTSC = (uint32_t)u64TSCDelta; + u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[(iTSCHistoryHead - 1) & 7]; + u32UpdateIntervalTSC >>= 1; + + /* value choosen on a 2GHz thinkpad running windows */ + u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 7; + } + else + { + u32UpdateIntervalTSC = (uint32_t)u64TSCDelta; + + /* This value hasn't be checked yet.. waiting for OS/2 and 33Hz timers.. :-) */ + u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 6; + } + ASMAtomicXchgU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack); + + /* + * CpuHz. + */ + u64CpuHz = ASMMult2xU32RetU64(u32UpdateIntervalTSC, pGip->u32UpdateHz); + ASMAtomicXchgU64(&pGipCpu->u64CpuHz, u64CpuHz); +} + + +/** + * Updates the GIP. + * + * @param pGip Pointer to the GIP. + * @param u64NanoTS The current nanosecond timesamp. + */ +void VBOXCALL supdrvGipUpdate(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS) +{ + /* + * Determin the relevant CPU data. + */ + PSUPGIPCPU pGipCpu; + if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC) + pGipCpu = &pGip->aCPUs[0]; + else + { + unsigned iCpu = ASMGetApicId(); + if (RT_LIKELY(iCpu >= RT_ELEMENTS(pGip->aCPUs))) + return; + pGipCpu = &pGip->aCPUs[iCpu]; + } + + /* + * Start update transaction. + */ + if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1)) + { + /* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */ + AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId)); + ASMAtomicIncU32(&pGipCpu->u32TransactionId); + pGipCpu->cErrors++; + return; + } + + /* + * Recalc the update frequency every 0x800th time. + */ + if (!(pGipCpu->u32TransactionId & (GIP_UPDATEHZ_RECALC_FREQ * 2 - 2))) + { + if (pGip->u64NanoTSLastUpdateHz) + { +#ifdef RT_ARCH_AMD64 /** @todo fix 64-bit div here to work on x86 linux. */ + uint64_t u64Delta = u64NanoTS - pGip->u64NanoTSLastUpdateHz; + uint32_t u32UpdateHz = (uint32_t)((UINT64_C(1000000000) * GIP_UPDATEHZ_RECALC_FREQ) / u64Delta); + if (u32UpdateHz <= 2000 && u32UpdateHz >= 30) + { + ASMAtomicXchgU32(&pGip->u32UpdateHz, u32UpdateHz); + ASMAtomicXchgU32(&pGip->u32UpdateIntervalNS, 1000000000 / u32UpdateHz); + } +#endif + } + ASMAtomicXchgU64(&pGip->u64NanoTSLastUpdateHz, u64NanoTS); + } + + /* + * Update the data. + */ + supdrvGipDoUpdateCpu(pGip, pGipCpu, u64NanoTS); + + /* + * Complete transaction. + */ + ASMAtomicIncU32(&pGipCpu->u32TransactionId); +} + + +/** + * Updates the per cpu GIP data for the calling cpu. + * + * @param pGip Pointer to the GIP. + * @param u64NanoTS The current nanosecond timesamp. + * @param iCpu The CPU index. + */ +void VBOXCALL supdrvGipUpdatePerCpu(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS, unsigned iCpu) +{ + PSUPGIPCPU pGipCpu; + + if (RT_LIKELY(iCpu < RT_ELEMENTS(pGip->aCPUs))) + { + pGipCpu = &pGip->aCPUs[iCpu]; + + /* + * Start update transaction. + */ + if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1)) + { + AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId)); + ASMAtomicIncU32(&pGipCpu->u32TransactionId); + pGipCpu->cErrors++; + return; + } + + /* + * Update the data. + */ + supdrvGipDoUpdateCpu(pGip, pGipCpu, u64NanoTS); + + /* + * Complete transaction. + */ + ASMAtomicIncU32(&pGipCpu->u32TransactionId); + } +} + diff --git a/src/VBox/HostDrivers/Support/SUPDrvIDC.h b/src/VBox/HostDrivers/Support/SUPDrvIDC.h new file mode 100644 index 000000000..785dc66ac --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPDrvIDC.h @@ -0,0 +1,276 @@ +/* $Id: SUPDrvIDC.h 10377 2008-07-08 16:26:13Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - Inter-Driver Communciation (IDC) definitions. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPDrvIDC_h___ +#define ___SUPDrvIDC_h___ + +#include <VBox/types.h> + +/** @def SUP_IDC_CODE + * Creates IDC function code. + * + * @param Function The function number to encode, 1..255. + * + * @remarks We can take a sligtly more relaxed attitude wrt to size encoding + * here since only windows will use standard I/O control function code. + */ +#ifdef RT_OS_WINDOWS +# define SUP_IDC_CODE(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) + 2542, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#else +# define SUP_IDC_CODE(Function) ( UINT32_C(0xc0ffee00) | (uint32_t)(0x000000ff & (Function)) ) +#endif + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +#ifdef RT_ARCH_AMD64 +# pragma pack(8) /* paranoia. */ +#else +# pragma pack(4) /* paranoia. */ +#endif + + +/** + * An IDC request packet header. + * + * The main purpose of this header is to pass the session handle + * and status code in a generic manner in order to make things + * easier on the receiving end. + */ +typedef struct SUPDRVIDCREQHDR +{ + /** IN: The size of the request. */ + uint32_t cb; + /** OUT: Status code of the request. */ + int32_t rc; + /** IN: Pointer to the session handle. */ + PSUPDRVSESSION pSession; +#if ARCH_BITS == 32 + /** Padding the structure to 16-bytes. */ + uint32_t u32Padding; +#endif +} SUPDRVIDCREQHDR; +/** Pointer to an IDC request packet header. */ +typedef SUPDRVIDCREQHDR *PSUPDRVIDCREQHDR; +/** Pointer to a const IDC request packet header. */ +typedef SUPDRVIDCREQHDR const *PCSUPDRVIDCREQHDR; + + +/** + * SUPDRV IDC: Connect request. + * This request takes a SUPDRVIDCREQCONNECT packet. + */ +#define SUPDRV_IDC_REQ_CONNECT SUP_IDC_CODE(1) +/** A SUPDRV IDC connect request packet. */ +typedef struct SUPDRVIDCREQCONNECT +{ + /** The request header. */ + SUPDRVIDCREQHDR Hdr; + /** The payload union. */ + union + { + /** The input. */ + struct SUPDRVIDCREQCONNECTIN + { + /** The magic cookie (SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE). */ + uint32_t u32MagicCookie; + /** The desired version of the IDC interface. */ + uint32_t uReqVersion; + /** The minimum version of the IDC interface. */ + uint32_t uMinVersion; + } In; + + /** The output. */ + struct SUPDRVIDCREQCONNECTOUT + { + /** The support driver session. (An opaque.) */ + PSUPDRVSESSION pSession; + /** The version of the IDC interface for this session. */ + uint32_t uSessionVersion; + /** The version of the IDC interface . */ + uint32_t uDriverVersion; + /** The SVN revision of the driver. + * This will be set to 0 if not compiled into the driver. */ + uint32_t uDriverRevision; + } Out; + } u; +} SUPDRVIDCREQCONNECT; +/** Pointer to a SUPDRV IDC connect request. */ +typedef SUPDRVIDCREQCONNECT *PSUPDRVIDCREQCONNECT; +/** Magic cookie value (SUPDRVIDCREQCONNECT::In.u32MagicCookie). ('tori') */ +#define SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE UINT32_C(0x69726f74) + + +/** + * SUPDRV IDC: Disconnect request. + * This request only requires a SUPDRVIDCREQHDR. + */ +#define SUPDRV_IDC_REQ_DISCONNECT SUP_IDC_CODE(2) + + +/** + * SUPDRV IDC: Query a symbol address. + * This request takes a SUPDRVIDCREQGETSYM packet. + */ +#define SUPDRV_IDC_REQ_GET_SYMBOL SUP_IDC_CODE(3) +/** A SUPDRV IDC get symbol request packet. */ +typedef struct SUPDRVIDCREQGETSYM +{ + /** The request header. */ + SUPDRVIDCREQHDR Hdr; + /** The payload union. */ + union + { + /** The input. */ + struct SUPDRVIDCREQGETSYMIN + { + /** The module name. + * NULL is an alias for the support driver. */ + const char *pszModule; + /** The symbol name. */ + const char *pszSymbol; + } In; + + /** The output. */ + struct SUPDRVIDCREQGETSYMOUT + { + /** The symbol address. */ + PFNRT pfnSymbol; + } Out; + } u; +} SUPDRVIDCREQGETSYM; +/** Pointer to a SUPDRV IDC get symbol request. */ +typedef SUPDRVIDCREQGETSYM *PSUPDRVIDCREQGETSYM; + + +/** + * SUPDRV IDC: Request the registration of a component factory. + * This request takes a SUPDRVIDCREQCOMPREGFACTORY packet. + */ +#define SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY SUP_IDC_CODE(10) +/** A SUPDRV IDC register component factory request packet. */ +typedef struct SUPDRVIDCREQCOMPREGFACTORY +{ + /** The request header. */ + SUPDRVIDCREQHDR Hdr; + /** The payload union. */ + union + { + /** The input. */ + struct SUPDRVIDCREQCOMPREGFACTORYIN + { + /** Pointer to the factory. */ + PCSUPDRVFACTORY pFactory; + } In; + } u; +} SUPDRVIDCREQCOMPREGFACTORY; +/** Pointer to a SUPDRV IDC register component factory request. */ +typedef SUPDRVIDCREQCOMPREGFACTORY *PSUPDRVIDCREQCOMPREGFACTORY; + + +/** + * SUPDRV IDC: Dergister a component factory. + * This request takes a SUPDRVIDCREQCOMPDEREGFACTORY packet. + */ +#define SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY SUP_IDC_CODE(11) +/** A SUPDRV IDC deregister component factory request packet. */ +typedef struct SUPDRVIDCREQCOMPDEREGFACTORY +{ + /** The request header. */ + SUPDRVIDCREQHDR Hdr; + /** The payload union. */ + union + { + /** The input. */ + struct SUPDRVIDCREQCOMPDEREGFACTORYIN + { + /** Pointer to the factory. */ + PCSUPDRVFACTORY pFactory; + } In; + } u; +} SUPDRVIDCREQCOMPDEREGFACTORY; +/** Pointer to a SUPDRV IDC deregister component factory request. */ +typedef SUPDRVIDCREQCOMPDEREGFACTORY *PSUPDRVIDCREQCOMPDEREGFACTORY; + + +/* + * The OS specific prototypes. + * Most OSes uses + */ +__BEGIN_DECLS + +#if defined(RT_OS_DARWIN) +extern int VBOXCALL SUPDrvDarwinIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq); + +#elif defined(RT_OS_FREEBSD) +extern int VBOXCALL SUPDrvFreeBSDIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq); + +#elif defined(RT_OS_LINUX) +extern int VBOXCALL SUPDrvLinuxIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq); + +#elif defined(RT_OS_OS2) +/** @todo Port to OS/2. */ + +#elif defined(RT_OS_SOLARIS) +extern int VBOXCALL SUPDrvSolarisIDC(uint32_t iReq, PSUPDRVIDCREQHDR pReq); + +#elif defined(RT_OS_WINDOWS) +/* Nothing special for windows. */ + +#else +/* PORTME: OS specific IDC stuff goes here. */ +#endif + +__END_DECLS + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code indicating the validity of the session, request and + * the return data packet. The status of the request it self is found + * in the packet (specific to each request). + * + * @param pSession The session. (This is NULL for SUPDRV_IDC_REQ_CONNECT.) + * @param uReq The request number. + * @param pvReq Pointer to the request packet. Optional for some requests. + * @param cbReq The size of the request packet. + */ +/** @todo move this and change to function proto */ +typedef DECLCALLBACK(int) FNSUPDRVIDCENTRY(PSUPDRVSESSION pSession, uint32_t uReq, void *pvReq, uint32_t cbReq); + +/** @} */ + + +#pragma pack() /* paranoia */ + +#endif + + diff --git a/src/VBox/HostDrivers/Support/SUPDrvIOC.h b/src/VBox/HostDrivers/Support/SUPDrvIOC.h new file mode 100644 index 000000000..d025dd6b9 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPDrvIOC.h @@ -0,0 +1,988 @@ +/* $Revision: 15838 $ */ +/** @file + * VirtualBox Support Driver - IOCtl definitions. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPDrvIOC_h___ +#define ___SUPDrvIOC_h___ + +/* + * Basic types. + */ +#include <iprt/stdint.h> + +/* + * IOCtl numbers. + * We're using the Win32 type of numbers here, thus the macros below. + * The SUP_IOCTL_FLAG macro is used to separate requests from 32-bit + * and 64-bit processes. + */ +#ifdef RT_ARCH_AMD64 +# define SUP_IOCTL_FLAG 128 +#elif defined(RT_ARCH_X86) +# define SUP_IOCTL_FLAG 0 +#else +# error "dunno which arch this is!" +#endif + +#ifdef RT_OS_WINDOWS +# ifndef CTL_CODE +# include <Windows.h> +# endif + /* Automatic buffering, size not encoded. */ +# define SUP_CTL_CODE_SIZE(Function, Size) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_BUFFERED, FILE_WRITE_ACCESS) +# define SUP_CTL_CODE_BIG(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_BUFFERED, FILE_WRITE_ACCESS) +# define SUP_CTL_CODE_FAST(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, (Function) | SUP_IOCTL_FLAG, METHOD_NEITHER, FILE_WRITE_ACCESS) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl) + +#elif defined(RT_OS_SOLARIS) + /* No automatic buffering, size limited to 255 bytes. */ +# include <sys/ioccom.h> +# define SUP_CTL_CODE_SIZE(Function, Size) _IOWRN('V', (Function) | SUP_IOCTL_FLAG, sizeof(SUPREQHDR)) +# define SUP_CTL_CODE_BIG(Function) _IOWRN('V', (Function) | SUP_IOCTL_FLAG, sizeof(SUPREQHDR)) +# define SUP_CTL_CODE_FAST(Function) _IO( 'V', (Function) | SUP_IOCTL_FLAG) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl) + +#elif defined(RT_OS_OS2) + /* No automatic buffering, size not encoded. */ +# define SUP_CTL_CATEGORY 0xc0 +# define SUP_CTL_CODE_SIZE(Function, Size) ((unsigned char)(Function)) +# define SUP_CTL_CODE_BIG(Function) ((unsigned char)(Function)) +# define SUP_CTL_CATEGORY_FAST 0xc1 +# define SUP_CTL_CODE_FAST(Function) ((unsigned char)(Function)) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl) + +#elif defined(RT_OS_LINUX) + /* No automatic buffering, size limited to 16KB. */ +# include <linux/ioctl.h> +# define SUP_CTL_CODE_SIZE(Function, Size) _IOC(_IOC_READ | _IOC_WRITE, 'V', (Function) | SUP_IOCTL_FLAG, (Size)) +# define SUP_CTL_CODE_BIG(Function) _IO('V', (Function) | SUP_IOCTL_FLAG) +# define SUP_CTL_CODE_FAST(Function) _IO('V', (Function) | SUP_IOCTL_FLAG) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) ((uIOCtl) & ~IOCSIZE_MASK) + +#elif defined(RT_OS_L4) + /* Implemented in suplib, no worries. */ +# define SUP_CTL_CODE_SIZE(Function, Size) (Function) +# define SUP_CTL_CODE_BIG(Function) (Function) +# define SUP_CTL_CODE_FAST(Function) (Function) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) (uIOCtl) + +#else /* BSD Like */ + /* Automatic buffering, size limited to 4KB on *BSD and 8KB on Darwin - commands the limit, 4KB. */ +# include <sys/ioccom.h> +# define SUP_CTL_CODE_SIZE(Function, Size) _IOC(IOC_INOUT, 'V', (Function) | SUP_IOCTL_FLAG, (Size)) +# define SUP_CTL_CODE_BIG(Function) _IO('V', (Function) | SUP_IOCTL_FLAG) +# define SUP_CTL_CODE_FAST(Function) _IO('V', (Function) | SUP_IOCTL_FLAG) +# define SUP_CTL_CODE_NO_SIZE(uIOCtl) ( (uIOCtl) & ~_IOC(0,0,0,IOCPARM_MASK) ) +#endif + +/** Fast path IOCtl: VMMR0_DO_RAW_RUN */ +#define SUP_IOCTL_FAST_DO_RAW_RUN SUP_CTL_CODE_FAST(64) +/** Fast path IOCtl: VMMR0_DO_HWACC_RUN */ +#define SUP_IOCTL_FAST_DO_HWACC_RUN SUP_CTL_CODE_FAST(65) +/** Just a NOP call for profiling the latency of a fast ioctl call to VMMR0. */ +#define SUP_IOCTL_FAST_DO_NOP SUP_CTL_CODE_FAST(66) + + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +#ifdef RT_ARCH_AMD64 +# pragma pack(8) /* paranoia. */ +#else +# pragma pack(4) /* paranoia. */ +#endif + + +/** + * Common In/Out header. + */ +typedef struct SUPREQHDR +{ + /** Cookie. */ + uint32_t u32Cookie; + /** Session cookie. */ + uint32_t u32SessionCookie; + /** The size of the input. */ + uint32_t cbIn; + /** The size of the output. */ + uint32_t cbOut; + /** Flags. See SUPREQHDR_FLAGS_* for details and values. */ + uint32_t fFlags; + /** The VBox status code of the operation, out direction only. */ + int32_t rc; +} SUPREQHDR; +/** Pointer to a IOC header. */ +typedef SUPREQHDR *PSUPREQHDR; + +/** @name SUPREQHDR::fFlags values + * @{ */ +/** Masks out the magic value. */ +#define SUPREQHDR_FLAGS_MAGIC_MASK UINT32_C(0xff0000ff) +/** The generic mask. */ +#define SUPREQHDR_FLAGS_GEN_MASK UINT32_C(0x0000ff00) +/** The request specific mask. */ +#define SUPREQHDR_FLAGS_REQ_MASK UINT32_C(0x00ff0000) + +/** There is extra input that needs copying on some platforms. */ +#define SUPREQHDR_FLAGS_EXTRA_IN UINT32_C(0x00000100) +/** There is extra output that needs copying on some platforms. */ +#define SUPREQHDR_FLAGS_EXTRA_OUT UINT32_C(0x00000200) + +/** The magic value. */ +#define SUPREQHDR_FLAGS_MAGIC UINT32_C(0x42000042) +/** The default value. Use this when no special stuff is requested. */ +#define SUPREQHDR_FLAGS_DEFAULT SUPREQHDR_FLAGS_MAGIC +/** @} */ + + +/** @name SUP_IOCTL_COOKIE + * @{ + */ +/** Negotiate cookie. */ +#define SUP_IOCTL_COOKIE SUP_CTL_CODE_SIZE(1, SUP_IOCTL_COOKIE_SIZE) +/** The request size. */ +#define SUP_IOCTL_COOKIE_SIZE sizeof(SUPCOOKIE) +/** The SUPREQHDR::cbIn value. */ +#define SUP_IOCTL_COOKIE_SIZE_IN sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCOOKIE, u.In) +/** The SUPREQHDR::cbOut value. */ +#define SUP_IOCTL_COOKIE_SIZE_OUT sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCOOKIE, u.Out) +/** SUPCOOKIE_IN magic word. */ +#define SUPCOOKIE_MAGIC "The Magic Word!" +/** The initial cookie. */ +#define SUPCOOKIE_INITIAL_COOKIE 0x69726f74 /* 'tori' */ + +/** Current interface version. + * The upper 16-bit is the major version, the the lower the minor version. + * When incompatible changes are made, the upper major number has to be changed. + * + * @todo Pending work on next major version change: + * - Eliminate supdrvPageWasLockedByPageAlloc and supdrvPageGetPhys. + * - Remove SUPR0PageAlloc in favor of SUPR0PageAllocEx, removing + * and renaming the related IOCtls too. + */ +#define SUPDRV_IOC_VERSION 0x000a0009 + +/** SUP_IOCTL_COOKIE. */ +typedef struct SUPCOOKIE +{ + /** The header. + * u32Cookie must be set to SUPCOOKIE_INITIAL_COOKIE. + * u32SessionCookie should be set to some random value. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Magic word. */ + char szMagic[16]; + /** The requested interface version number. */ + uint32_t u32ReqVersion; + /** The minimum interface version number. */ + uint32_t u32MinVersion; + } In; + struct + { + /** Cookie. */ + uint32_t u32Cookie; + /** Session cookie. */ + uint32_t u32SessionCookie; + /** Interface version for this session. */ + uint32_t u32SessionVersion; + /** The actual interface version in the driver. */ + uint32_t u32DriverVersion; + /** Number of functions available for the SUP_IOCTL_QUERY_FUNCS request. */ + uint32_t cFunctions; + /** Session handle. */ + R0PTRTYPE(PSUPDRVSESSION) pSession; + } Out; + } u; +} SUPCOOKIE, *PSUPCOOKIE; +/** @} */ + + +/** @name SUP_IOCTL_QUERY_FUNCS + * Query SUPR0 functions. + * @{ + */ +#define SUP_IOCTL_QUERY_FUNCS(cFuncs) SUP_CTL_CODE_SIZE(2, SUP_IOCTL_QUERY_FUNCS_SIZE(cFuncs)) +#define SUP_IOCTL_QUERY_FUNCS_SIZE(cFuncs) RT_UOFFSETOF(SUPQUERYFUNCS, u.Out.aFunctions[(cFuncs)]) +#define SUP_IOCTL_QUERY_FUNCS_SIZE_IN sizeof(SUPREQHDR) +#define SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(cFuncs) SUP_IOCTL_QUERY_FUNCS_SIZE(cFuncs) + +/** A function. */ +typedef struct SUPFUNC +{ + /** Name - mangled. */ + char szName[32]; + /** Address. */ + RTR0PTR pfn; +} SUPFUNC, *PSUPFUNC; + +typedef struct SUPQUERYFUNCS +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Number of functions returned. */ + uint32_t cFunctions; + /** Array of functions. */ + SUPFUNC aFunctions[1]; + } Out; + } u; +} SUPQUERYFUNCS, *PSUPQUERYFUNCS; +/** @} */ + + +/** @name SUP_IOCTL_IDT_INSTALL + * Install IDT patch for calling processor. + * @{ + */ +#define SUP_IOCTL_IDT_INSTALL SUP_CTL_CODE_SIZE(3, SUP_IOCTL_IDT_INSTALL_SIZE) +#define SUP_IOCTL_IDT_INSTALL_SIZE sizeof(SUPIDTINSTALL) +#define SUP_IOCTL_IDT_INSTALL_SIZE_IN sizeof(SUPREQHDR) +#define SUP_IOCTL_IDT_INSTALL_SIZE_OUT sizeof(SUPIDTINSTALL) +typedef struct SUPIDTINSTALL +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The IDT entry number. */ + uint8_t u8Idt; + } Out; + } u; +} SUPIDTINSTALL, *PSUPIDTINSTALL; +/** @} */ + + +/** @name SUP_IOCTL_IDT_REMOVE + * Remove IDT patch for calling processor. + * @{ + */ +#define SUP_IOCTL_IDT_REMOVE SUP_CTL_CODE_SIZE(4, SUP_IOCTL_IDT_REMOVE_SIZE) +#define SUP_IOCTL_IDT_REMOVE_SIZE sizeof(SUPIDTREMOVE) +#define SUP_IOCTL_IDT_REMOVE_SIZE_IN sizeof(SUPIDTREMOVE) +#define SUP_IOCTL_IDT_REMOVE_SIZE_OUT sizeof(SUPIDTREMOVE) +typedef struct SUPIDTREMOVE +{ + /** The header. */ + SUPREQHDR Hdr; +} SUPIDTREMOVE, *PSUPIDTREMOVE; +/** @}*/ + + +/** @name SUP_IOCTL_LDR_OPEN + * Open an image. + * @{ + */ +#define SUP_IOCTL_LDR_OPEN SUP_CTL_CODE_SIZE(5, SUP_IOCTL_LDR_OPEN_SIZE) +#define SUP_IOCTL_LDR_OPEN_SIZE sizeof(SUPLDROPEN) +#define SUP_IOCTL_LDR_OPEN_SIZE_IN sizeof(SUPLDROPEN) +#define SUP_IOCTL_LDR_OPEN_SIZE_OUT (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLDROPEN, u.Out)) +typedef struct SUPLDROPEN +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Size of the image we'll be loading. */ + uint32_t cbImage; + /** Image name. + * This is the NAME of the image, not the file name. It is used + * to share code with other processes. (Max len is 32 chars!) */ + char szName[32]; + } In; + struct + { + /** The base address of the image. */ + RTR0PTR pvImageBase; + /** Indicate whether or not the image requires loading. */ + bool fNeedsLoading; + } Out; + } u; +} SUPLDROPEN, *PSUPLDROPEN; +/** @} */ + + +/** @name SUP_IOCTL_LDR_LOAD + * Upload the image bits. + * @{ + */ +#define SUP_IOCTL_LDR_LOAD SUP_CTL_CODE_BIG(6) +#define SUP_IOCTL_LDR_LOAD_SIZE(cbImage) RT_UOFFSETOF(SUPLDRLOAD, u.In.achImage[cbImage]) +#define SUP_IOCTL_LDR_LOAD_SIZE_IN(cbImage) RT_UOFFSETOF(SUPLDRLOAD, u.In.achImage[cbImage]) +#define SUP_IOCTL_LDR_LOAD_SIZE_OUT sizeof(SUPREQHDR) + +/** + * Module initialization callback function. + * This is called once after the module has been loaded. + * + * @returns 0 on success. + * @returns Appropriate error code on failure. + */ +typedef DECLCALLBACK(int) FNR0MODULEINIT(void); +/** Pointer to a FNR0MODULEINIT(). */ +typedef R0PTRTYPE(FNR0MODULEINIT *) PFNR0MODULEINIT; + +/** + * Module termination callback function. + * This is called once right before the module is being unloaded. + */ +typedef DECLCALLBACK(void) FNR0MODULETERM(void); +/** Pointer to a FNR0MODULETERM(). */ +typedef R0PTRTYPE(FNR0MODULETERM *) PFNR0MODULETERM; + +/** + * Symbol table entry. + */ +typedef struct SUPLDRSYM +{ + /** Offset into of the string table. */ + uint32_t offName; + /** Offset of the symbol relative to the image load address. */ + uint32_t offSymbol; +} SUPLDRSYM; +/** Pointer to a symbol table entry. */ +typedef SUPLDRSYM *PSUPLDRSYM; +/** Pointer to a const symbol table entry. */ +typedef SUPLDRSYM const *PCSUPLDRSYM; + +/** + * SUPLDRLOAD::u::In::EP type. + */ +typedef enum SUPLDRLOADEP +{ + SUPLDRLOADEP_NOTHING = 0, + SUPLDRLOADEP_VMMR0, + SUPLDRLOADEP_SERVICE, + SUPLDRLOADEP_32BIT_HACK = 0x7fffffff +} SUPLDRLOADEP; + +typedef struct SUPLDRLOAD +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The address of module initialization function. Similar to _DLL_InitTerm(hmod, 0). */ + PFNR0MODULEINIT pfnModuleInit; + /** The address of module termination function. Similar to _DLL_InitTerm(hmod, 1). */ + PFNR0MODULETERM pfnModuleTerm; + /** Special entry points. */ + union + { + /** SUPLDRLOADEP_VMMR0. */ + struct + { + /** The module handle (i.e. address). */ + RTR0PTR pvVMMR0; + /** Address of VMMR0EntryInt function. */ + RTR0PTR pvVMMR0EntryInt; + /** Address of VMMR0EntryFast function. */ + RTR0PTR pvVMMR0EntryFast; + /** Address of VMMR0EntryEx function. */ + RTR0PTR pvVMMR0EntryEx; + } VMMR0; + /** SUPLDRLOADEP_SERVICE. */ + struct + { + /** The service request handler. + * (PFNR0SERVICEREQHANDLER isn't defined yet.) */ + RTR0PTR pfnServiceReq; + /** Reserved, must be NIL. */ + RTR0PTR apvReserved[3]; + } Service; + } EP; + /** Address. */ + RTR0PTR pvImageBase; + /** Entry point type. */ + SUPLDRLOADEP eEPType; + /** The offset of the symbol table. */ + uint32_t offSymbols; + /** The number of entries in the symbol table. */ + uint32_t cSymbols; + /** The offset of the string table. */ + uint32_t offStrTab; + /** Size of the string table. */ + uint32_t cbStrTab; + /** Size of image (including string and symbol tables). */ + uint32_t cbImage; + /** The image data. */ + char achImage[1]; + } In; + } u; +} SUPLDRLOAD, *PSUPLDRLOAD; +/** @} */ + + +/** @name SUP_IOCTL_LDR_FREE + * Free an image. + * @{ + */ +#define SUP_IOCTL_LDR_FREE SUP_CTL_CODE_SIZE(7, SUP_IOCTL_LDR_FREE_SIZE) +#define SUP_IOCTL_LDR_FREE_SIZE sizeof(SUPLDRFREE) +#define SUP_IOCTL_LDR_FREE_SIZE_IN sizeof(SUPLDRFREE) +#define SUP_IOCTL_LDR_FREE_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPLDRFREE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Address. */ + RTR0PTR pvImageBase; + } In; + } u; +} SUPLDRFREE, *PSUPLDRFREE; +/** @} */ + + +/** @name SUP_IOCTL_LDR_GET_SYMBOL + * Get address of a symbol within an image. + * @{ + */ +#define SUP_IOCTL_LDR_GET_SYMBOL SUP_CTL_CODE_SIZE(8, SUP_IOCTL_LDR_GET_SYMBOL_SIZE) +#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE sizeof(SUPLDRGETSYMBOL) +#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE_IN sizeof(SUPLDRGETSYMBOL) +#define SUP_IOCTL_LDR_GET_SYMBOL_SIZE_OUT (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLDRGETSYMBOL, u.Out)) +typedef struct SUPLDRGETSYMBOL +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Address. */ + RTR0PTR pvImageBase; + /** The symbol name. */ + char szSymbol[64]; + } In; + struct + { + /** The symbol address. */ + RTR0PTR pvSymbol; + } Out; + } u; +} SUPLDRGETSYMBOL, *PSUPLDRGETSYMBOL; +/** @} */ + + +/** @name SUP_IOCTL_CALL_VMMR0 + * Call the R0 VMM Entry point. + * + * @todo Might have to convert this to a big request... + * @{ + */ +#define SUP_IOCTL_CALL_VMMR0(cbReq) SUP_CTL_CODE_SIZE(9, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)) +#define SUP_IOCTL_CALL_VMMR0_SIZE(cbReq) RT_UOFFSETOF(SUPCALLVMMR0, abReqPkt[cbReq]) +#define SUP_IOCTL_CALL_VMMR0_SIZE_IN(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq) +#define SUP_IOCTL_CALL_VMMR0_SIZE_OUT(cbReq) SUP_IOCTL_CALL_VMMR0_SIZE(cbReq) +typedef struct SUPCALLVMMR0 +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The VM handle. */ + PVMR0 pVMR0; + /** Which operation to execute. */ + uint32_t uOperation; +#if R0_ARCH_BITS == 64 + /** Alignment. */ + uint32_t u32Reserved; +#endif + /** Argument to use when no request packet is supplied. */ + uint64_t u64Arg; + } In; + } u; + /** The VMMR0Entry request packet. */ + uint8_t abReqPkt[1]; +} SUPCALLVMMR0, *PSUPCALLVMMR0; +/** @} */ + + +/** @name SUP_IOCTL_LOW_ALLOC + * Allocate memory below 4GB (physically). + * @{ + */ +#define SUP_IOCTL_LOW_ALLOC SUP_CTL_CODE_BIG(10) +#define SUP_IOCTL_LOW_ALLOC_SIZE(cPages) ((uint32_t)RT_UOFFSETOF(SUPLOWALLOC, u.Out.aPages[cPages])) +#define SUP_IOCTL_LOW_ALLOC_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPLOWALLOC, u.In)) +#define SUP_IOCTL_LOW_ALLOC_SIZE_OUT(cPages) SUP_IOCTL_LOW_ALLOC_SIZE(cPages) +typedef struct SUPLOWALLOC +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Number of pages to allocate. */ + uint32_t cPages; + } In; + struct + { + /** The ring-3 address of the allocated memory. */ + RTR3PTR pvR3; + /** The ring-0 address of the allocated memory. */ + RTR0PTR pvR0; + /** Array of pages. */ + RTHCPHYS aPages[1]; + } Out; + } u; +} SUPLOWALLOC, *PSUPLOWALLOC; +/** @} */ + + +/** @name SUP_IOCTL_LOW_FREE + * Free low memory. + * @{ + */ +#define SUP_IOCTL_LOW_FREE SUP_CTL_CODE_SIZE(11, SUP_IOCTL_LOW_FREE_SIZE) +#define SUP_IOCTL_LOW_FREE_SIZE sizeof(SUPLOWFREE) +#define SUP_IOCTL_LOW_FREE_SIZE_IN sizeof(SUPLOWFREE) +#define SUP_IOCTL_LOW_FREE_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPLOWFREE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The ring-3 address of the memory to free. */ + RTR3PTR pvR3; + } In; + } u; +} SUPLOWFREE, *PSUPLOWFREE; +/** @} */ + + +/** @name SUP_IOCTL_PAGE_ALLOC + * Allocate memory and map into the user process. + * The memory is of course locked. + * @{ + */ +#define SUP_IOCTL_PAGE_ALLOC SUP_CTL_CODE_BIG(12) +#define SUP_IOCTL_PAGE_ALLOC_SIZE(cPages) RT_UOFFSETOF(SUPPAGEALLOC, u.Out.aPages[cPages]) +#define SUP_IOCTL_PAGE_ALLOC_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPPAGEALLOC, u.In)) +#define SUP_IOCTL_PAGE_ALLOC_SIZE_OUT(cPages) SUP_IOCTL_PAGE_ALLOC_SIZE(cPages) +typedef struct SUPPAGEALLOC +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Number of pages to allocate */ + uint32_t cPages; + } In; + struct + { + /** Returned ring-3 address. */ + RTR3PTR pvR3; + /** The physical addresses of the allocated pages. */ + RTHCPHYS aPages[1]; + } Out; + } u; +} SUPPAGEALLOC, *PSUPPAGEALLOC; +/** @} */ + + +/** @name SUP_IOCTL_PAGE_FREE + * Free memory allocated with SUP_IOCTL_PAGE_ALLOC or SUP_IOCTL_PAGE_ALLOC_EX. + * @{ + */ +#define SUP_IOCTL_PAGE_FREE SUP_CTL_CODE_SIZE(13, SUP_IOCTL_PAGE_FREE_SIZE_IN) +#define SUP_IOCTL_PAGE_FREE_SIZE sizeof(SUPPAGEFREE) +#define SUP_IOCTL_PAGE_FREE_SIZE_IN sizeof(SUPPAGEFREE) +#define SUP_IOCTL_PAGE_FREE_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPPAGEFREE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Address of memory range to free. */ + RTR3PTR pvR3; + } In; + } u; +} SUPPAGEFREE, *PSUPPAGEFREE; +/** @} */ + + +/** @name SUP_IOCTL_PAGE_LOCK + * Pin down physical pages. + * @{ + */ +#define SUP_IOCTL_PAGE_LOCK SUP_CTL_CODE_BIG(14) +#define SUP_IOCTL_PAGE_LOCK_SIZE(cPages) (RT_MAX((size_t)SUP_IOCTL_PAGE_LOCK_SIZE_IN, (size_t)SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages))) +#define SUP_IOCTL_PAGE_LOCK_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPPAGELOCK, u.In)) +#define SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages) RT_UOFFSETOF(SUPPAGELOCK, u.Out.aPages[cPages]) +typedef struct SUPPAGELOCK +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Start of page range. Must be PAGE aligned. */ + RTR3PTR pvR3; + /** The range size given as a page count. */ + uint32_t cPages; + } In; + + struct + { + /** Array of pages. */ + RTHCPHYS aPages[1]; + } Out; + } u; +} SUPPAGELOCK, *PSUPPAGELOCK; +/** @} */ + + +/** @name SUP_IOCTL_PAGE_UNLOCK + * Unpin physical pages. + * @{ */ +#define SUP_IOCTL_PAGE_UNLOCK SUP_CTL_CODE_SIZE(15, SUP_IOCTL_PAGE_UNLOCK_SIZE) +#define SUP_IOCTL_PAGE_UNLOCK_SIZE sizeof(SUPPAGEUNLOCK) +#define SUP_IOCTL_PAGE_UNLOCK_SIZE_IN sizeof(SUPPAGEUNLOCK) +#define SUP_IOCTL_PAGE_UNLOCK_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPPAGEUNLOCK +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Start of page range of a range previuosly pinned. */ + RTR3PTR pvR3; + } In; + } u; +} SUPPAGEUNLOCK, *PSUPPAGEUNLOCK; +/** @} */ + + +/** @name SUP_IOCTL_CONT_ALLOC + * Allocate contious memory. + * @{ + */ +#define SUP_IOCTL_CONT_ALLOC SUP_CTL_CODE_SIZE(16, SUP_IOCTL_CONT_ALLOC_SIZE) +#define SUP_IOCTL_CONT_ALLOC_SIZE sizeof(SUPCONTALLOC) +#define SUP_IOCTL_CONT_ALLOC_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPCONTALLOC, u.In)) +#define SUP_IOCTL_CONT_ALLOC_SIZE_OUT sizeof(SUPCONTALLOC) +typedef struct SUPCONTALLOC +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The allocation size given as a page count. */ + uint32_t cPages; + } In; + + struct + { + /** The address of the ring-0 mapping of the allocated memory. */ + RTR0PTR pvR0; + /** The address of the ring-3 mapping of the allocated memory. */ + RTR3PTR pvR3; + /** The physical address of the allocation. */ + RTHCPHYS HCPhys; + } Out; + } u; +} SUPCONTALLOC, *PSUPCONTALLOC; +/** @} */ + + +/** @name SUP_IOCTL_CONT_FREE Input. + * @{ + */ +/** Free contious memory. */ +#define SUP_IOCTL_CONT_FREE SUP_CTL_CODE_SIZE(17, SUP_IOCTL_CONT_FREE_SIZE) +#define SUP_IOCTL_CONT_FREE_SIZE sizeof(SUPCONTFREE) +#define SUP_IOCTL_CONT_FREE_SIZE_IN sizeof(SUPCONTFREE) +#define SUP_IOCTL_CONT_FREE_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPCONTFREE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The ring-3 address of the memory to free. */ + RTR3PTR pvR3; + } In; + } u; +} SUPCONTFREE, *PSUPCONTFREE; +/** @} */ + + +/** @name SUP_IOCTL_GET_PAGING_MODE + * Get the host paging mode. + * @{ + */ +#define SUP_IOCTL_GET_PAGING_MODE SUP_CTL_CODE_SIZE(18, SUP_IOCTL_GET_PAGING_MODE_SIZE) +#define SUP_IOCTL_GET_PAGING_MODE_SIZE sizeof(SUPGETPAGINGMODE) +#define SUP_IOCTL_GET_PAGING_MODE_SIZE_IN sizeof(SUPREQHDR) +#define SUP_IOCTL_GET_PAGING_MODE_SIZE_OUT sizeof(SUPGETPAGINGMODE) +typedef struct SUPGETPAGINGMODE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The paging mode. */ + SUPPAGINGMODE enmMode; + } Out; + } u; +} SUPGETPAGINGMODE, *PSUPGETPAGINGMODE; +/** @} */ + + +/** @name SUP_IOCTL_SET_VM_FOR_FAST + * Set the VM handle for doing fast call ioctl calls. + * @{ + */ +#define SUP_IOCTL_SET_VM_FOR_FAST SUP_CTL_CODE_SIZE(19, SUP_IOCTL_SET_VM_FOR_FAST_SIZE) +#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE sizeof(SUPSETVMFORFAST) +#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN sizeof(SUPSETVMFORFAST) +#define SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT sizeof(SUPREQHDR) +typedef struct SUPSETVMFORFAST +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The ring-0 VM handle (pointer). */ + PVMR0 pVMR0; + } In; + } u; +} SUPSETVMFORFAST, *PSUPSETVMFORFAST; +/** @} */ + + +/** @name SUP_IOCTL_GIP_MAP + * Map the GIP into user space. + * @{ + */ +#define SUP_IOCTL_GIP_MAP SUP_CTL_CODE_SIZE(20, SUP_IOCTL_GIP_MAP_SIZE) +#define SUP_IOCTL_GIP_MAP_SIZE sizeof(SUPGIPMAP) +#define SUP_IOCTL_GIP_MAP_SIZE_IN sizeof(SUPREQHDR) +#define SUP_IOCTL_GIP_MAP_SIZE_OUT sizeof(SUPGIPMAP) +typedef struct SUPGIPMAP +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The physical address of the GIP. */ + RTHCPHYS HCPhysGip; + /** Pointer to the read-only usermode GIP mapping for this session. */ + R3PTRTYPE(PSUPGLOBALINFOPAGE) pGipR3; + /** Pointer to the supervisor mode GIP mapping. */ + R0PTRTYPE(PSUPGLOBALINFOPAGE) pGipR0; + } Out; + } u; +} SUPGIPMAP, *PSUPGIPMAP; +/** @} */ + + +/** @name SUP_IOCTL_GIP_UNMAP + * Unmap the GIP. + * @{ + */ +#define SUP_IOCTL_GIP_UNMAP SUP_CTL_CODE_SIZE(21, SUP_IOCTL_GIP_UNMAP_SIZE) +#define SUP_IOCTL_GIP_UNMAP_SIZE sizeof(SUPGIPUNMAP) +#define SUP_IOCTL_GIP_UNMAP_SIZE_IN sizeof(SUPGIPUNMAP) +#define SUP_IOCTL_GIP_UNMAP_SIZE_OUT sizeof(SUPGIPUNMAP) +typedef struct SUPGIPUNMAP +{ + /** The header. */ + SUPREQHDR Hdr; +} SUPGIPUNMAP, *PSUPGIPUNMAP; +/** @} */ + + +/** @name SUP_IOCTL_CALL_SERVICE + * Call the a ring-0 service. + * + * @todo Might have to convert this to a big request, just like + * SUP_IOCTL_CALL_VMMR0 + * @{ + */ +#define SUP_IOCTL_CALL_SERVICE(cbReq) SUP_CTL_CODE_SIZE(22, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)) +#define SUP_IOCTL_CALL_SERVICE_SIZE(cbReq) RT_UOFFSETOF(SUPCALLSERVICE, abReqPkt[cbReq]) +#define SUP_IOCTL_CALL_SERVICE_SIZE_IN(cbReq) SUP_IOCTL_CALL_SERVICE_SIZE(cbReq) +#define SUP_IOCTL_CALL_SERVICE_SIZE_OUT(cbReq) SUP_IOCTL_CALL_SERVICE_SIZE(cbReq) +typedef struct SUPCALLSERVICE +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The service name. */ + char szName[28]; + /** Which operation to execute. */ + uint32_t uOperation; + /** Argument to use when no request packet is supplied. */ + uint64_t u64Arg; + } In; + } u; + /** The request packet passed to SUP. */ + uint8_t abReqPkt[1]; +} SUPCALLSERVICE, *PSUPCALLSERVICE; +/** @} */ + +/** @name SUP_IOCTL_PAGE_ALLOC_EX + * Allocate memory and map it into kernel and/or user space. The memory is of + * course locked. This is an extended version of SUP_IOCTL_PAGE_ALLOC and the + * result should be freed using SUP_IOCTL_PAGE_FREE. + * + * @remarks Allocations without a kernel mapping may fail with + * VERR_NOT_SUPPORTED on some platforms just like with + * SUP_IOCTL_PAGE_ALLOC. + * + * @{ + */ +#define SUP_IOCTL_PAGE_ALLOC_EX SUP_CTL_CODE_BIG(23) +#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages) RT_UOFFSETOF(SUPPAGEALLOCEX, u.Out.aPages[cPages]) +#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN (sizeof(SUPREQHDR) + RT_SIZEOFMEMB(SUPPAGEALLOCEX, u.In)) +#define SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(cPages) SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages) +typedef struct SUPPAGEALLOCEX +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** Number of pages to allocate */ + uint32_t cPages; + /** Whether it should have kernel mapping. */ + bool fKernelMapping; + /** Whether it should have a user mapping. */ + bool fUserMapping; + /** Reserved. Must be false. */ + bool fReserved0; + /** Reserved. Must be false. */ + bool fReserved1; + } In; + struct + { + /** Returned ring-3 address. */ + RTR3PTR pvR3; + /** Returned ring-0 address. */ + RTR0PTR pvR0; + /** The physical addresses of the allocated pages. */ + RTHCPHYS aPages[1]; + } Out; + } u; +} SUPPAGEALLOCEX, *PSUPPAGEALLOCEX; +/** @} */ + + +/** @name SUP_IOCTL_PAGE_MAP_KERNEL + * Maps a portion of memory allocated by SUP_IOCTL_PAGE_ALLOC_EX / + * SUPR0PageAllocEx into kernel space for use by a device or similar. + * + * The mapping will be freed together with the ring-3 mapping when + * SUP_IOCTL_PAGE_FREE or SUPR0PageFree is called. + * + * @remarks Not necessarily supported on all platforms. + * + * @{ + */ +#define SUP_IOCTL_PAGE_MAP_KERNEL SUP_CTL_CODE_SIZE(24, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE) +#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE sizeof(SUPPAGEMAPKERNEL) +#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_IN sizeof(SUPPAGEMAPKERNEL) +#define SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_OUT sizeof(SUPPAGEMAPKERNEL) +typedef struct SUPPAGEMAPKERNEL +{ + /** The header. */ + SUPREQHDR Hdr; + union + { + struct + { + /** The pointer of to the previously allocated memory. */ + RTR3PTR pvR3; + /** The offset to start mapping from. */ + uint32_t offSub; + /** Size of the section to map. */ + uint32_t cbSub; + /** Flags reserved for future fun. */ + uint32_t fFlags; + } In; + struct + { + /** The ring-0 address corresponding to pvR3 + offSub. */ + RTR0PTR pvR0; + } Out; + } u; +} SUPPAGEMAPKERNEL, *PSUPPAGEMAPKERNEL; +/** @} */ + + +#pragma pack() /* paranoia */ + +#endif + diff --git a/src/VBox/HostDrivers/Support/SUPDrvInternal.h b/src/VBox/HostDrivers/Support/SUPDrvInternal.h new file mode 100644 index 000000000..5ae9c8b49 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPDrvInternal.h @@ -0,0 +1,641 @@ +/* $Revision: 16029 $ */ +/** @file + * VirtualBox Support Driver - Internal header. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPDrvInternal_h +#define ___SUPDrvInternal_h + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/cdefs.h> +#include <VBox/types.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <VBox/sup.h> +#include <iprt/memobj.h> +#include <iprt/time.h> +#include <iprt/timer.h> +#include <iprt/string.h> +#include <iprt/err.h> + + +#if defined(RT_OS_WINDOWS) + __BEGIN_DECLS +# if (_MSC_VER >= 1400) && !defined(VBOX_WITH_PATCHED_DDK) +# define _InterlockedExchange _InterlockedExchange_StupidDDKVsCompilerCrap +# define _InterlockedExchangeAdd _InterlockedExchangeAdd_StupidDDKVsCompilerCrap +# define _InterlockedCompareExchange _InterlockedCompareExchange_StupidDDKVsCompilerCrap +# define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap +# define _interlockedbittestandset _interlockedbittestandset_StupidDDKVsCompilerCrap +# define _interlockedbittestandreset _interlockedbittestandreset_StupidDDKVsCompilerCrap +# define _interlockedbittestandset64 _interlockedbittestandset64_StupidDDKVsCompilerCrap +# define _interlockedbittestandreset64 _interlockedbittestandreset64_StupidDDKVsCompilerCrap +# include <ntddk.h> +# undef _InterlockedExchange +# undef _InterlockedExchangeAdd +# undef _InterlockedCompareExchange +# undef _InterlockedAddLargeStatistic +# undef _interlockedbittestandset +# undef _interlockedbittestandreset +# undef _interlockedbittestandset64 +# undef _interlockedbittestandreset64 +# else +# include <ntddk.h> +# endif +# include <memory.h> +# define memcmp(a,b,c) mymemcmp(a,b,c) + int VBOXCALL mymemcmp(const void *, const void *, size_t); + __END_DECLS + +#elif defined(RT_OS_LINUX) +# include <linux/autoconf.h> +# include <linux/version.h> +# if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) +# define MODVERSIONS +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 71) +# include <linux/modversions.h> +# endif +# endif +# if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) +# undef ALIGN +# endif +# ifndef KBUILD_STR +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) +# define KBUILD_STR(s) s +# else +# define KBUILD_STR(s) #s +# endif +# endif +# include <linux/string.h> +# include <linux/spinlock.h> +# include <linux/slab.h> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +# include <linux/semaphore.h> +# else /* older kernels */ +# include <asm/semaphore.h> +# endif /* older kernels */ +# include <linux/timer.h> + +# if 0 +# include <linux/hrtimer.h> +# define VBOX_HRTIMER +# endif + +#elif defined(RT_OS_DARWIN) +# include <libkern/libkern.h> +# include <iprt/string.h> + +#elif defined(RT_OS_OS2) + +#elif defined(RT_OS_FREEBSD) +# define memset libkern_memset /** @todo these are just hacks to get it compiling, check out later. */ +# define memcmp libkern_memcmp +# define strchr libkern_strchr +# define strrchr libkern_strrchr +# define ffs libkern_ffs +# define ffsl libkern_ffsl +# define fls libkern_fls +# define flsl libkern_flsl +# include <sys/libkern.h> +# undef memset +# undef memcmp +# undef strchr +# undef strrchr +# undef ffs +# undef ffsl +# undef fls +# undef flsl +# include <iprt/string.h> + +#elif defined(RT_OS_SOLARIS) +# include <sys/cmn_err.h> +# include <iprt/string.h> + +#else +# error "unsupported OS." +#endif + +#include "SUPDrvIOC.h" +#include "SUPDrvIDC.h" + + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/* + * Hardcoded cookies. + */ +#define BIRD 0x64726962 /* 'bird' */ +#define BIRD_INV 0x62697264 /* 'drib' */ + + +/* + * Win32 + */ +#if defined(RT_OS_WINDOWS) + +/* debug printf */ +# define OSDBGPRINT(a) DbgPrint a + +/** Maximum number of bytes we try to lock down in one go. + * This is supposed to have a limit right below 256MB, but this appears + * to actually be much lower. The values here have been determined experimentally. + */ +#ifdef RT_ARCH_X86 +# define MAX_LOCK_MEM_SIZE (32*1024*1024) /* 32mb */ +#endif +#ifdef RT_ARCH_AMD64 +# define MAX_LOCK_MEM_SIZE (24*1024*1024) /* 24mb */ +#endif + + +/* + * Linux + */ +#elif defined(RT_OS_LINUX) + +/* check kernel version */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) +# error Unsupported kernel version! +#endif + +__BEGIN_DECLS +int linux_dprintf(const char *format, ...); +__END_DECLS + +/* debug printf */ +# define OSDBGPRINT(a) printk a + + +/* + * Darwin + */ +#elif defined(RT_OS_DARWIN) + +/* debug printf */ +# define OSDBGPRINT(a) printf a + + +/* + * OS/2 + */ +#elif defined(RT_OS_OS2) + +/* No log API in OS/2 only COM port. */ +# define OSDBGPRINT(a) SUPR0Printf a + + +/* + * FreeBSD + */ +#elif defined(RT_OS_FREEBSD) + +/* debug printf */ +# define OSDBGPRINT(a) printf a + + +/* + * Solaris + */ +#elif defined(RT_OS_SOLARIS) +# define OSDBGPRINT(a) SUPR0Printf a + + +#else +/** @todo other os'es */ +# error "OS interface defines is not done for this OS!" +#endif + + +/* dprintf */ +#if (defined(DEBUG) && !defined(NO_LOGGING)) || defined(RT_OS_FREEBSD) +# ifdef LOG_TO_COM +# include <VBox/log.h> +# define dprintf(a) RTLogComPrintf a +# else +# define dprintf(a) OSDBGPRINT(a) +# endif +#else +# define dprintf(a) do {} while (0) +#endif + +/* dprintf2 - extended logging. */ +#if defined(RT_OS_DARWIN) || defined(RT_OS_OS2) || defined(RT_OS_FREEBSD) +# define dprintf2 dprintf +#else +# define dprintf2(a) do { } while (0) +#endif + + +/** @def RT_WITH_W64_UNWIND_HACK + * Changes a function name into the wrapped version if we've + * enabled the unwind hack. + * + * The unwind hack is for making the NT unwind procedures skip + * our dynamically loaded code when they try to walk the call + * stack. Needless to say, they kind of don't expect what + * we're doing here and get kind of confused and may BSOD. */ +#ifdef DOXYGEN_RUNNING +# define RT_WITH_W64_UNWIND_HACK +#endif +/** @def UNWIND_WRAP + * If RT_WITH_W64_UNWIND_HACK is defined, the name will be prefixed with + * 'supdrvNtWrap'. + * @param Name The function to wrapper. */ +#ifdef RT_WITH_W64_UNWIND_HACK +# define UNWIND_WRAP(Name) supdrvNtWrap##Name +#else +# define UNWIND_WRAP(Name) Name +#endif + + +/* + * Error codes. + */ +/** @todo retire the SUPDRV_ERR_* stuff, we ship err.h now. */ +/** Invalid parameter. */ +#define SUPDRV_ERR_GENERAL_FAILURE (-1) +/** Invalid parameter. */ +#define SUPDRV_ERR_INVALID_PARAM (-2) +/** Invalid magic or cookie. */ +#define SUPDRV_ERR_INVALID_MAGIC (-3) +/** Invalid loader handle. */ +#define SUPDRV_ERR_INVALID_HANDLE (-4) +/** Failed to lock the address range. */ +#define SUPDRV_ERR_LOCK_FAILED (-5) +/** Invalid memory pointer. */ +#define SUPDRV_ERR_INVALID_POINTER (-6) +/** Failed to patch the IDT. */ +#define SUPDRV_ERR_IDT_FAILED (-7) +/** Memory allocation failed. */ +#define SUPDRV_ERR_NO_MEMORY (-8) +/** Already loaded. */ +#define SUPDRV_ERR_ALREADY_LOADED (-9) +/** Permission denied. */ +#define SUPDRV_ERR_PERMISSION_DENIED (-10) +/** Version mismatch. */ +#define SUPDRV_ERR_VERSION_MISMATCH (-11) + + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** Pointer to the device extension. */ +typedef struct SUPDRVDEVEXT *PSUPDRVDEVEXT; + + +/** + * Memory reference types. + */ +typedef enum +{ + /** Unused entry */ + MEMREF_TYPE_UNUSED = 0, + /** Locked memory (r3 mapping only). */ + MEMREF_TYPE_LOCKED, + /** Continous memory block (r3 and r0 mapping). */ + MEMREF_TYPE_CONT, + /** Low memory block (r3 and r0 mapping). */ + MEMREF_TYPE_LOW, + /** Memory block (r3 and r0 mapping). */ + MEMREF_TYPE_MEM, + /** Locked memory (r3 mapping only) allocated by the support driver. */ + MEMREF_TYPE_PAGE, + /** Blow the type up to 32-bit and mark the end. */ + MEMREG_TYPE_32BIT_HACK = 0x7fffffff +} SUPDRVMEMREFTYPE, *PSUPDRVMEMREFTYPE; + + +/** + * Structure used for tracking memory a session + * references in one way or another. + */ +typedef struct SUPDRVMEMREF +{ + /** The memory object handle. */ + RTR0MEMOBJ MemObj; + /** The ring-3 mapping memory object handle. */ + RTR0MEMOBJ MapObjR3; + /** Type of memory. */ + SUPDRVMEMREFTYPE eType; +} SUPDRVMEMREF, *PSUPDRVMEMREF; + + +/** + * Bundle of locked memory ranges. + */ +typedef struct SUPDRVBUNDLE +{ + /** Pointer to the next bundle. */ + struct SUPDRVBUNDLE * volatile pNext; + /** Referenced memory. */ + SUPDRVMEMREF aMem[64]; + /** Number of entries used. */ + uint32_t volatile cUsed; +} SUPDRVBUNDLE, *PSUPDRVBUNDLE; + + +/** + * Loaded image. + */ +typedef struct SUPDRVLDRIMAGE +{ + /** Next in chain. */ + struct SUPDRVLDRIMAGE * volatile pNext; + /** Pointer to the image. */ + void *pvImage; + /** Pointer to the optional module initialization callback. */ + PFNR0MODULEINIT pfnModuleInit; + /** Pointer to the optional module termination callback. */ + PFNR0MODULETERM pfnModuleTerm; + /** Service request handler. This is NULL for non-service modules. */ + PFNSUPR0SERVICEREQHANDLER pfnServiceReqHandler; + /** Size of the image. */ + uint32_t cbImage; + /** The offset of the symbol table. */ + uint32_t offSymbols; + /** The number of entries in the symbol table. */ + uint32_t cSymbols; + /** The offset of the string table. */ + uint32_t offStrTab; + /** Size of the string table. */ + uint32_t cbStrTab; + /** The ldr image state. (IOCtl code of last opration.) */ + uint32_t uState; + /** Usage count. */ + uint32_t volatile cUsage; + /** Image name. */ + char szName[32]; +} SUPDRVLDRIMAGE, *PSUPDRVLDRIMAGE; + + +/** Image usage record. */ +typedef struct SUPDRVLDRUSAGE +{ + /** Next in chain. */ + struct SUPDRVLDRUSAGE * volatile pNext; + /** The image. */ + PSUPDRVLDRIMAGE pImage; + /** Load count. */ + uint32_t volatile cUsage; +} SUPDRVLDRUSAGE, *PSUPDRVLDRUSAGE; + + +/** + * Component factory registration record. + */ +typedef struct SUPDRVFACTORYREG +{ + /** Pointer to the next registration. */ + struct SUPDRVFACTORYREG *pNext; + /** Pointer to the registered factory. */ + PCSUPDRVFACTORY pFactory; + /** The session owning the factory. + * Used for deregistration and session cleanup. */ + PSUPDRVSESSION pSession; + /** Length of the name. */ + size_t cchName; +} SUPDRVFACTORYREG; +/** Pointer to a component factory registration record. */ +typedef SUPDRVFACTORYREG *PSUPDRVFACTORYREG; +/** Pointer to a const component factory registration record. */ +typedef SUPDRVFACTORYREG const *PCSUPDRVFACTORYREG; + + +/** + * Registered object. + * This takes care of reference counting and tracking data for access checks. + */ +typedef struct SUPDRVOBJ +{ + /** Magic value (SUPDRVOBJ_MAGIC). */ + uint32_t u32Magic; + /** The object type. */ + SUPDRVOBJTYPE enmType; + /** Pointer to the next in the global list. */ + struct SUPDRVOBJ * volatile pNext; + /** Pointer to the object destructor. + * This may be set to NULL if the image containing the destructor get unloaded. */ + PFNSUPDRVDESTRUCTOR pfnDestructor; + /** User argument 1. */ + void *pvUser1; + /** User argument 2. */ + void *pvUser2; + /** The total sum of all per-session usage. */ + uint32_t volatile cUsage; + /** The creator user id. */ + RTUID CreatorUid; + /** The creator group id. */ + RTGID CreatorGid; + /** The creator process id. */ + RTPROCESS CreatorProcess; +} SUPDRVOBJ, *PSUPDRVOBJ; + +/** Magic number for SUPDRVOBJ::u32Magic. (Dame Agatha Mary Clarissa Christie). */ +#define SUPDRVOBJ_MAGIC 0x18900915 +/** Dead number magic for SUPDRVOBJ::u32Magic. */ +#define SUPDRVOBJ_MAGIC_DEAD 0x19760112 + +/** + * The per-session object usage record. + */ +typedef struct SUPDRVUSAGE +{ + /** Pointer to the next in the list. */ + struct SUPDRVUSAGE * volatile pNext; + /** Pointer to the object we're recording usage for. */ + PSUPDRVOBJ pObj; + /** The usage count. */ + uint32_t volatile cUsage; +} SUPDRVUSAGE, *PSUPDRVUSAGE; + + +/** + * Per session data. + * This is mainly for memory tracking. + */ +typedef struct SUPDRVSESSION +{ + /** Pointer to the device extension. */ + PSUPDRVDEVEXT pDevExt; + /** Session Cookie. */ + uint32_t u32Cookie; + + /** Load usage records. (protected by SUPDRVDEVEXT::mtxLdr) */ + PSUPDRVLDRUSAGE volatile pLdrUsage; + /** The VM associated with the session. */ + PVM pVM; + /** List of generic usage records. (protected by SUPDRVDEVEXT::SpinLock) */ + PSUPDRVUSAGE volatile pUsage; + + /** Spinlock protecting the bundles and the GIP members. */ + RTSPINLOCK Spinlock; + /** The ring-3 mapping of the GIP (readonly). */ + RTR0MEMOBJ GipMapObjR3; + /** Set if the session is using the GIP. */ + uint32_t fGipReferenced; + /** Bundle of locked memory objects. */ + SUPDRVBUNDLE Bundle; + + /** The user id of the session. (Set by the OS part.) */ + RTUID Uid; + /** The group id of the session. (Set by the OS part.) */ + RTGID Gid; + /** The process (id) of the session. */ + RTPROCESS Process; + /** Which process this session is associated with. + * This is NIL_RTR0PROCESS for kernel sessions and valid for user ones. */ + RTR0PROCESS R0Process; +#if defined(RT_OS_DARWIN) + /** Pointer to the associated org_virtualbox_SupDrvClient object. */ + void *pvSupDrvClient; + /** Whether this session has been opened or not. */ + bool fOpened; +#endif +#if defined(RT_OS_OS2) + /** The system file number of this session. */ + uint16_t sfn; + uint16_t Alignment; /**< Alignment */ +#endif +#if defined(RT_OS_DARWIN) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS) + /** Pointer to the next session with the same hash. */ + PSUPDRVSESSION pNextHash; +#endif +} SUPDRVSESSION; + + +/** + * Device extension. + */ +typedef struct SUPDRVDEVEXT +{ + /** Spinlock to serialize the initialization, + * usage counting and destruction of the IDT entry override and objects. */ + RTSPINLOCK Spinlock; + + /** List of registered objects. Protected by the spinlock. */ + PSUPDRVOBJ volatile pObjs; + /** List of free object usage records. */ + PSUPDRVUSAGE volatile pUsageFree; + + /** Global cookie. */ + uint32_t u32Cookie; + + /** The IDT entry number. + * Only valid if pIdtPatches is set. */ + uint8_t volatile u8Idt; + + /** Loader mutex. + * This protects pvVMMR0, pvVMMR0Entry, pImages and SUPDRVSESSION::pLdrUsage. */ + RTSEMFASTMUTEX mtxLdr; + + /** VMM Module 'handle'. + * 0 if the code VMM isn't loaded and Idt are nops. */ + void * volatile pvVMMR0; + /** VMMR0EntryInt() pointer. */ + DECLR0CALLBACKMEMBER(int, pfnVMMR0EntryInt, (PVM pVM, unsigned uOperation, void *pvArg)); + /** VMMR0EntryFast() pointer. */ + DECLR0CALLBACKMEMBER(void, pfnVMMR0EntryFast, (PVM pVM, unsigned idCpu, unsigned uOperation)); + /** VMMR0EntryEx() pointer. */ + DECLR0CALLBACKMEMBER(int, pfnVMMR0EntryEx, (PVM pVM, unsigned uOperation, PSUPVMMR0REQHDR pReq, uint64_t u64Arg, PSUPDRVSESSION pSession)); + + /** Linked list of loaded code. */ + PSUPDRVLDRIMAGE volatile pLdrImages; + + /** GIP mutex. + * Any changes to any of the GIP members requires ownership of this mutex, + * except on driver init and termination. */ + RTSEMFASTMUTEX mtxGip; + /** Pointer to the Global Info Page (GIP). */ + PSUPGLOBALINFOPAGE pGip; + /** The physical address of the GIP. */ + RTHCPHYS HCPhysGip; + /** Number of processes using the GIP. + * (The updates are suspend while cGipUsers is 0.)*/ + uint32_t volatile cGipUsers; + /** The ring-0 memory object handle for the GIP page. */ + RTR0MEMOBJ GipMemObj; + /** The GIP timer handle. */ + PRTTIMER pGipTimer; + /** If non-zero we've successfully called RTTimerRequestSystemGranularity(). */ + uint32_t u32SystemTimerGranularityGrant; + /** The CPU id of the GIP master. + * This CPU is responsible for the updating the common GIP data. */ + RTCPUID volatile idGipMaster; + +#ifdef RT_OS_WINDOWS + /* Callback object returned by ExCreateCallback. */ + PCALLBACK_OBJECT pObjPowerCallback; + /* Callback handle returned by ExRegisterCallback. */ + PVOID hPowerCallback; +#endif + + /** Component factory mutex. + * This protects pComponentFactoryHead and component factory querying. */ + RTSEMFASTMUTEX mtxComponentFactory; + /** The head of the list of registered component factories. */ + PSUPDRVFACTORYREG pComponentFactoryHead; +} SUPDRVDEVEXT; + + +__BEGIN_DECLS + +/******************************************************************************* +* OS Specific Functions * +*******************************************************************************/ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession); +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc); +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt); +int VBOXCALL supdrvOSEnableVTx(bool fEnabled); + +/******************************************************************************* +* Shared Functions * +*******************************************************************************/ +int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr); +int VBOXCALL supdrvIOCtlFast(uintptr_t uIOCtl, unsigned idCpu, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession); +int VBOXCALL supdrvIDC(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQHDR pReqHdr); +int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt); +void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt); +int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, PSUPDRVSESSION *ppSession); +void VBOXCALL supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession); +void VBOXCALL supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession); +int VBOXCALL supdrvGipInit(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, RTHCPHYS HCPhys, uint64_t u64NanoTS, unsigned uUpdateHz); +void VBOXCALL supdrvGipTerm(PSUPGLOBALINFOPAGE pGip); +void VBOXCALL supdrvGipUpdate(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS); +void VBOXCALL supdrvGipUpdatePerCpu(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS, unsigned iCpu); +bool VBOXCALL supdrvDetermineAsyncTsc(uint64_t *pu64DiffCores); + +__END_DECLS + +#endif + diff --git a/src/VBox/HostDrivers/Support/SUPLib.cpp b/src/VBox/HostDrivers/Support/SUPLib.cpp new file mode 100644 index 000000000..ed3db1125 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPLib.cpp @@ -0,0 +1,1928 @@ +/* $Id: SUPLib.cpp 15841 2009-01-07 16:40:02Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Common code. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/** @page pg_sup SUP - The Support Library + * + * The support library is responsible for providing facilities to load + * VMM Host Ring-0 code, to call Host VMM Ring-0 code from Ring-3 Host + * code, to pin down physical memory, and more. + * + * The VMM Host Ring-0 code can be combined in the support driver if + * permitted by kernel module license policies. If it is not combined + * it will be externalized in a .r0 module that will be loaded using + * the IPRT loader. + * + * The Ring-0 calling is done thru a generic SUP interface which will + * tranfer an argument set and call a predefined entry point in the Host + * VMM Ring-0 code. + * + * See @ref grp_sup "SUP - Support APIs" for API details. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#include <VBox/sup.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <VBox/vmm.h> +#include <VBox/log.h> +#include <VBox/x86.h> + +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/alloca.h> +#include <iprt/ldr.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/cpuset.h> +#include <iprt/thread.h> +#include <iprt/process.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/env.h> +#include <iprt/rand.h> + +#include "SUPLibInternal.h" +#include "SUPDrvIOC.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** R0 VMM module name. */ +#define VMMR0_NAME "VMMR0" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +typedef DECLCALLBACK(int) FNCALLVMMR0(PVMR0 pVMR0, unsigned uOperation, void *pvArg); +typedef FNCALLVMMR0 *PFNCALLVMMR0; + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Init counter. */ +static uint32_t g_cInits = 0; +/** Whether we've been preinitied. */ +static bool g_fPreInited = false; +/** The SUPLib instance data. + * Well, at least parts of it, specificly the parts that are being handed over + * via the pre-init mechanism from the hardened executable stub. */ +static SUPLIBDATA g_supLibData = +{ + NIL_RTFILE +#if defined(RT_OS_DARWIN) + , NULL +#elif defined(RT_OS_LINUX) + , false +#endif +}; + +/** Pointer to the Global Information Page. + * + * This pointer is valid as long as SUPLib has a open session. Anyone using + * the page must treat this pointer as higly volatile and not trust it beyond + * one transaction. + * + * @todo This will probably deserve it's own session or some other good solution... + */ +DECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage; +/** Address of the ring-0 mapping of the GIP. */ +static PSUPGLOBALINFOPAGE g_pSUPGlobalInfoPageR0; +/** The physical address of the GIP. */ +static RTHCPHYS g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS; + +/** The negotiated cookie. */ +uint32_t g_u32Cookie = 0; +/** The negotiated session cookie. */ +uint32_t g_u32SessionCookie; +/** Session handle. */ +PSUPDRVSESSION g_pSession; +/** R0 SUP Functions used for resolving referenced to the SUPR0 module. */ +static PSUPQUERYFUNCS g_pFunctions; + +/** VMMR0 Load Address. */ +static RTR0PTR g_pvVMMR0 = NIL_RTR0PTR; +/** PAGE_ALLOC_EX sans kernel mapping support indicator. */ +static bool g_fSupportsPageAllocNoKernel = true; +/** Fake mode indicator. (~0 at first, 0 or 1 after first test) */ +static uint32_t g_u32FakeMode = ~0; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int supInitFake(PSUPDRVSESSION *ppSession); +static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler, void **ppvImageBase); +static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser); + + +SUPR3DECL(int) SUPInstall(void) +{ + return suplibOsInstall(); +} + + +SUPR3DECL(int) SUPUninstall(void) +{ + return suplibOsUninstall(); +} + + +DECLEXPORT(int) supR3PreInit(PSUPPREINITDATA pPreInitData, uint32_t fFlags) +{ + /* + * The caller is kind of trustworthy, just perform some basic checks. + * + * Note! Do not do any fancy stuff here because IPRT has NOT been + * initialized at this point. + */ + if (!VALID_PTR(pPreInitData)) + return VERR_INVALID_POINTER; + if (g_fPreInited || g_cInits > 0) + return VERR_WRONG_ORDER; + + if ( pPreInitData->u32Magic != SUPPREINITDATA_MAGIC + || pPreInitData->u32EndMagic != SUPPREINITDATA_MAGIC) + return VERR_INVALID_MAGIC; + if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV) + && pPreInitData->Data.hDevice == NIL_RTFILE) + return VERR_INVALID_HANDLE; + if ( (fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV) + && pPreInitData->Data.hDevice != NIL_RTFILE) + return VERR_INVALID_PARAMETER; + + /* + * Hand out the data. + */ + int rc = supR3HardenedRecvPreInitData(pPreInitData); + if (RT_FAILURE(rc)) + return rc; + + /** @todo This may need some small restructuring later, it doesn't quite work with a root service flag... */ + if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)) + { + g_supLibData = pPreInitData->Data; + g_fPreInited = true; + } + + return VINF_SUCCESS; +} + + +SUPR3DECL(int) SUPR3Init(PSUPDRVSESSION *ppSession) +{ + /* + * Perform some sanity checks. + * (Got some trouble with compile time member alignment assertions.) + */ + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, u64NanoTSLastUpdateHz) & 0x7)); + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs) & 0x1f)); + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[1]) & 0x1f)); + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64NanoTS) & 0x7)); + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64TSC) & 0x7)); + Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64CpuHz) & 0x7)); + + /* + * Check if already initialized. + */ + if (ppSession) + *ppSession = g_pSession; + if (g_cInits++ > 0) + return VINF_SUCCESS; + + /* + * Check for fake mode. + * + * Fake mode is used when we're doing smoke testing and debugging. + * It's also useful on platforms where we haven't root access or which + * we haven't ported the support driver to. + */ + if (g_u32FakeMode == ~0U) + { + const char *psz = RTEnvGet("VBOX_SUPLIB_FAKE"); + if (psz && !strcmp(psz, "fake")) + ASMAtomicCmpXchgU32(&g_u32FakeMode, 1, ~0U); + else + ASMAtomicCmpXchgU32(&g_u32FakeMode, 0, ~0U); + } + if (RT_UNLIKELY(g_u32FakeMode)) + return supInitFake(ppSession); + + /* + * Open the support driver. + */ + int rc = suplibOsInit(&g_supLibData, g_fPreInited); + if (RT_SUCCESS(rc)) + { + /* + * Negotiate the cookie. + */ + SUPCOOKIE CookieReq; + memset(&CookieReq, 0xff, sizeof(CookieReq)); + CookieReq.Hdr.u32Cookie = SUPCOOKIE_INITIAL_COOKIE; + CookieReq.Hdr.u32SessionCookie = RTRandU32(); + CookieReq.Hdr.cbIn = SUP_IOCTL_COOKIE_SIZE_IN; + CookieReq.Hdr.cbOut = SUP_IOCTL_COOKIE_SIZE_OUT; + CookieReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + CookieReq.Hdr.rc = VERR_INTERNAL_ERROR; + strcpy(CookieReq.u.In.szMagic, SUPCOOKIE_MAGIC); + CookieReq.u.In.u32ReqVersion = SUPDRV_IOC_VERSION; + const uint32_t MinVersion = (SUPDRV_IOC_VERSION & 0xffff0000) == 0x000a0000 + ? 0x000a0009 + : SUPDRV_IOC_VERSION & 0xffff0000; + CookieReq.u.In.u32MinVersion = MinVersion; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_COOKIE, &CookieReq, SUP_IOCTL_COOKIE_SIZE); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(CookieReq.Hdr.rc)) + { + if ( (CookieReq.u.Out.u32SessionVersion & 0xffff0000) == (SUPDRV_IOC_VERSION & 0xffff0000) + && CookieReq.u.Out.u32SessionVersion >= MinVersion) + { + /* + * Query the functions. + */ + PSUPQUERYFUNCS pFuncsReq = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions)); + if (pFuncsReq) + { + pFuncsReq->Hdr.u32Cookie = CookieReq.u.Out.u32Cookie; + pFuncsReq->Hdr.u32SessionCookie = CookieReq.u.Out.u32SessionCookie; + pFuncsReq->Hdr.cbIn = SUP_IOCTL_QUERY_FUNCS_SIZE_IN; + pFuncsReq->Hdr.cbOut = SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(CookieReq.u.Out.cFunctions); + pFuncsReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pFuncsReq->Hdr.rc = VERR_INTERNAL_ERROR; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_QUERY_FUNCS(CookieReq.u.Out.cFunctions), pFuncsReq, SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions)); + if (RT_SUCCESS(rc)) + rc = pFuncsReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + g_u32Cookie = CookieReq.u.Out.u32Cookie; + g_u32SessionCookie = CookieReq.u.Out.u32SessionCookie; + g_pSession = CookieReq.u.Out.pSession; + g_pFunctions = pFuncsReq; + if (ppSession) + *ppSession = CookieReq.u.Out.pSession; + + /* + * Map the GIP into userspace. + * This is an optional feature, so we will ignore any failures here. + */ + if (!g_pSUPGlobalInfoPage) + { + SUPGIPMAP GipMapReq; + GipMapReq.Hdr.u32Cookie = g_u32Cookie; + GipMapReq.Hdr.u32SessionCookie = g_u32SessionCookie; + GipMapReq.Hdr.cbIn = SUP_IOCTL_GIP_MAP_SIZE_IN; + GipMapReq.Hdr.cbOut = SUP_IOCTL_GIP_MAP_SIZE_OUT; + GipMapReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + GipMapReq.Hdr.rc = VERR_INTERNAL_ERROR; + GipMapReq.u.Out.HCPhysGip = NIL_RTHCPHYS; + GipMapReq.u.Out.pGipR0 = NIL_RTR0PTR; + GipMapReq.u.Out.pGipR3 = NULL; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GIP_MAP, &GipMapReq, SUP_IOCTL_GIP_MAP_SIZE); + if (RT_SUCCESS(rc)) + rc = GipMapReq.Hdr.rc; + if (RT_SUCCESS(rc)) + { + AssertRelease(GipMapReq.u.Out.pGipR3->u32Magic == SUPGLOBALINFOPAGE_MAGIC); + AssertRelease(GipMapReq.u.Out.pGipR3->u32Version >= SUPGLOBALINFOPAGE_VERSION); + ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, GipMapReq.u.Out.HCPhysGip); + ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, GipMapReq.u.Out.pGipR3, NULL); + ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, (void *)GipMapReq.u.Out.pGipR0, NULL); + } + } + return VINF_SUCCESS; + } + + /* bailout */ + RTMemFree(pFuncsReq); + } + else + rc = VERR_NO_MEMORY; + } + else + { + LogRel(("Support driver version mismatch: SessionVersion=%#x DriverVersion=%#x ClientVersion=%#x MinVersion=%#x\n", + CookieReq.u.Out.u32SessionVersion, CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, MinVersion)); + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + } + else + { + if (RT_SUCCESS(rc)) + { + rc = CookieReq.Hdr.rc; + LogRel(("Support driver version mismatch: DriverVersion=%#x ClientVersion=%#x rc=%Rrc\n", + CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, rc)); + if (rc != VERR_VM_DRIVER_VERSION_MISMATCH) + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + else + { + /* for pre 0x00060000 drivers */ + LogRel(("Support driver version mismatch: DriverVersion=too-old ClientVersion=%#x\n", SUPDRV_IOC_VERSION)); + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + } + + suplibOsTerm(&g_supLibData); + } + AssertMsgFailed(("SUPR3Init() failed rc=%Rrc\n", rc)); + g_cInits--; + + return rc; +} + +/** + * Fake mode init. + */ +static int supInitFake(PSUPDRVSESSION *ppSession) +{ + Log(("SUP: Fake mode!\n")); + static const SUPFUNC s_aFakeFunctions[] = + { + /* name function */ + { "SUPR0AbsIs64bit", 0 }, + { "SUPR0Abs64bitKernelCS", 0 }, + { "SUPR0Abs64bitKernelSS", 0 }, + { "SUPR0Abs64bitKernelDS", 0 }, + { "SUPR0AbsKernelCS", 8 }, + { "SUPR0AbsKernelSS", 16 }, + { "SUPR0AbsKernelDS", 16 }, + { "SUPR0AbsKernelES", 16 }, + { "SUPR0AbsKernelFS", 24 }, + { "SUPR0AbsKernelGS", 32 }, + { "SUPR0ComponentRegisterFactory", 0xefeefffd }, + { "SUPR0ComponentDeregisterFactory", 0xefeefffe }, + { "SUPR0ComponentQueryFactory", 0xefeeffff }, + { "SUPR0ObjRegister", 0xefef0000 }, + { "SUPR0ObjAddRef", 0xefef0001 }, + { "SUPR0ObjAddRefEx", 0xefef0001 }, + { "SUPR0ObjRelease", 0xefef0002 }, + { "SUPR0ObjVerifyAccess", 0xefef0003 }, + { "SUPR0LockMem", 0xefef0004 }, + { "SUPR0UnlockMem", 0xefef0005 }, + { "SUPR0ContAlloc", 0xefef0006 }, + { "SUPR0ContFree", 0xefef0007 }, + { "SUPR0MemAlloc", 0xefef0008 }, + { "SUPR0MemGetPhys", 0xefef0009 }, + { "SUPR0MemFree", 0xefef000a }, + { "SUPR0Printf", 0xefef000b }, + { "SUPR0GetPagingMode", 0xefef000c }, + { "SUPR0EnableVTx", 0xefef000c }, + { "RTMemAlloc", 0xefef000d }, + { "RTMemAllocZ", 0xefef000e }, + { "RTMemFree", 0xefef000f }, + { "RTR0MemObjAddress", 0xefef0010 }, + { "RTR0MemObjAddressR3", 0xefef0011 }, + { "RTR0MemObjAllocPage", 0xefef0012 }, + { "RTR0MemObjAllocPhysNC", 0xefef0013 }, + { "RTR0MemObjAllocLow", 0xefef0014 }, + { "RTR0MemObjEnterPhys", 0xefef0014 }, + { "RTR0MemObjFree", 0xefef0015 }, + { "RTR0MemObjGetPagePhysAddr", 0xefef0016 }, + { "RTR0MemObjMapUser", 0xefef0017 }, + { "RTR0MemObjMapKernel", 0xefef0017 }, + { "RTR0MemObjMapKernelEx", 0xefef0017 }, + { "RTProcSelf", 0xefef0038 }, + { "RTR0ProcHandleSelf", 0xefef0039 }, + { "RTSemEventCreate", 0xefef0018 }, + { "RTSemEventSignal", 0xefef0019 }, + { "RTSemEventWait", 0xefef001a }, + { "RTSemEventWaitNoResume", 0xefef001b }, + { "RTSemEventDestroy", 0xefef001c }, + { "RTSemEventMultiCreate", 0xefef001d }, + { "RTSemEventMultiSignal", 0xefef001e }, + { "RTSemEventMultiReset", 0xefef001f }, + { "RTSemEventMultiWait", 0xefef0020 }, + { "RTSemEventMultiWaitNoResume", 0xefef0021 }, + { "RTSemEventMultiDestroy", 0xefef0022 }, + { "RTSemFastMutexCreate", 0xefef0023 }, + { "RTSemFastMutexDestroy", 0xefef0024 }, + { "RTSemFastMutexRequest", 0xefef0025 }, + { "RTSemFastMutexRelease", 0xefef0026 }, + { "RTSpinlockCreate", 0xefef0027 }, + { "RTSpinlockDestroy", 0xefef0028 }, + { "RTSpinlockAcquire", 0xefef0029 }, + { "RTSpinlockRelease", 0xefef002a }, + { "RTSpinlockAcquireNoInts", 0xefef002b }, + { "RTSpinlockReleaseNoInts", 0xefef002c }, + { "RTTimeNanoTS", 0xefef002d }, + { "RTTimeMillieTS", 0xefef002e }, + { "RTTimeSystemNanoTS", 0xefef002f }, + { "RTTimeSystemMillieTS", 0xefef0030 }, + { "RTThreadNativeSelf", 0xefef0031 }, + { "RTThreadSleep", 0xefef0032 }, + { "RTThreadYield", 0xefef0033 }, + { "RTLogDefaultInstance", 0xefef0034 }, + { "RTLogRelDefaultInstance", 0xefef0035 }, + { "RTLogSetDefaultInstanceThread", 0xefef0036 }, + { "RTLogLogger", 0xefef0037 }, + { "RTLogLoggerEx", 0xefef0038 }, + { "RTLogLoggerExV", 0xefef0039 }, + { "AssertMsg1", 0xefef003a }, + { "AssertMsg2", 0xefef003b }, + { "RTAssertMsg1", 0xefef003c }, + { "RTAssertMsg2", 0xefef003d }, + { "RTAssertMsg2V", 0xefef003e }, + }; + + /* fake r0 functions. */ + g_pFunctions = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(RT_ELEMENTS(s_aFakeFunctions))); + if (g_pFunctions) + { + g_pFunctions->u.Out.cFunctions = RT_ELEMENTS(s_aFakeFunctions); + memcpy(&g_pFunctions->u.Out.aFunctions[0], &s_aFakeFunctions[0], sizeof(s_aFakeFunctions)); + g_pSession = (PSUPDRVSESSION)(void *)g_pFunctions; + if (ppSession) + *ppSession = g_pSession; + + /* fake the GIP. */ + g_pSUPGlobalInfoPage = (PSUPGLOBALINFOPAGE)RTMemPageAllocZ(PAGE_SIZE); + if (g_pSUPGlobalInfoPage) + { + g_pSUPGlobalInfoPageR0 = g_pSUPGlobalInfoPage; + g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS & ~(RTHCPHYS)PAGE_OFFSET_MASK; + /* the page is supposed to be invalid, so don't set the magic. */ + return VINF_SUCCESS; + } + + RTMemFree(g_pFunctions); + g_pFunctions = NULL; + } + return VERR_NO_MEMORY; +} + + +SUPR3DECL(int) SUPTerm(bool fForced) +{ + /* + * Verify state. + */ + AssertMsg(g_cInits > 0, ("SUPTerm() is called before SUPR3Init()!\n")); + if (g_cInits == 0) + return VERR_WRONG_ORDER; + if (g_cInits == 1 || fForced) + { + /* + * NULL the GIP pointer. + */ + if (g_pSUPGlobalInfoPage) + { + ASMAtomicXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, NULL); + ASMAtomicXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, NULL); + ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, NIL_RTHCPHYS); + /* just a little safe guard against threads using the page. */ + RTThreadSleep(50); + } + + /* + * Close the support driver. + */ + int rc = suplibOsTerm(&g_supLibData); + if (rc) + return rc; + + g_u32Cookie = 0; + g_u32SessionCookie = 0; + g_cInits = 0; + } + else + g_cInits--; + + return 0; +} + + +SUPR3DECL(SUPPAGINGMODE) SUPGetPagingMode(void) +{ + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) +#ifdef RT_ARCH_AMD64 + return SUPPAGINGMODE_AMD64_GLOBAL_NX; +#else + return SUPPAGINGMODE_32_BIT_GLOBAL; +#endif + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPGETPAGINGMODE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_GET_PAGING_MODE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_GET_PAGING_MODE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_PAGING_MODE, &Req, SUP_IOCTL_GET_PAGING_MODE_SIZE); + if ( RT_FAILURE(rc) + || RT_FAILURE(Req.Hdr.rc)) + { + LogRel(("SUPGetPagingMode: %Rrc %Rrc\n", rc, Req.Hdr.rc)); + Req.u.Out.enmMode = SUPPAGINGMODE_INVALID; + } + + return Req.u.Out.enmMode; +} + + +/** + * For later. + */ +static int supCallVMMR0ExFake(PVMR0 pVMR0, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr) +{ + AssertMsgFailed(("%d\n", uOperation)); + return VERR_NOT_SUPPORTED; +} + + +SUPR3DECL(int) SUPCallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, unsigned idCpu) +{ + if (RT_LIKELY(uOperation == SUP_VMMR0_DO_RAW_RUN)) + return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_RAW_RUN, idCpu); + if (RT_LIKELY(uOperation == SUP_VMMR0_DO_HWACC_RUN)) + return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_HWACC_RUN, idCpu); + if (RT_LIKELY(uOperation == SUP_VMMR0_DO_NOP)) + return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_NOP, idCpu); + + AssertMsgFailed(("%#x\n", uOperation)); + return VERR_INTERNAL_ERROR; +} + + +SUPR3DECL(int) SUPCallVMMR0Ex(PVMR0 pVMR0, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr) +{ + /* + * The following operations don't belong here. + */ + AssertMsgReturn( uOperation != SUP_VMMR0_DO_RAW_RUN + && uOperation != SUP_VMMR0_DO_HWACC_RUN + && uOperation != SUP_VMMR0_DO_NOP, + ("%#x\n", uOperation), + VERR_INTERNAL_ERROR); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + return supCallVMMR0ExFake(pVMR0, uOperation, u64Arg, pReqHdr); + + int rc; + if (!pReqHdr) + { + /* no data. */ + SUPCALLVMMR0 Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(0); + Req.Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0); + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pVMR0 = pVMR0; + Req.u.In.uOperation = uOperation; + Req.u.In.u64Arg = u64Arg; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(0), &Req, SUP_IOCTL_CALL_VMMR0_SIZE(0)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + } + else if (SUP_IOCTL_CALL_VMMR0_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */ + { + AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER); + AssertReturn(pReqHdr->u32Magic == SUPVMMR0REQHDR_MAGIC, VERR_INVALID_MAGIC); + const size_t cbReq = pReqHdr->cbReq; + + PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)alloca(SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(cbReq); + pReq->Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(cbReq); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.pVMR0 = pVMR0; + pReq->u.In.uOperation = uOperation; + pReq->u.In.u64Arg = u64Arg; + memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(cbReq), pReq, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq); + } + else /** @todo may have to remove the size limits one this request... */ + AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_INTERNAL_ERROR); + return rc; +} + + +SUPR3DECL(int) SUPCallVMMR0(PVMR0 pVMR0, unsigned uOperation, void *pvArg) +{ + /* + * The following operations don't belong here. + */ + AssertMsgReturn( uOperation != SUP_VMMR0_DO_RAW_RUN + && uOperation != SUP_VMMR0_DO_HWACC_RUN + && uOperation != SUP_VMMR0_DO_NOP, + ("%#x\n", uOperation), + VERR_INTERNAL_ERROR); + return SUPCallVMMR0Ex(pVMR0, uOperation, (uintptr_t)pvArg, NULL); +} + + +SUPR3DECL(int) SUPSetVMForFastIOCtl(PVMR0 pVMR0) +{ + if (RT_UNLIKELY(g_u32FakeMode)) + return VINF_SUCCESS; + + SUPSETVMFORFAST Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pVMR0 = pVMR0; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SET_VM_FOR_FAST, &Req, SUP_IOCTL_SET_VM_FOR_FAST_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3CallR0Service(const char *pszService, size_t cchService, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr) +{ + AssertReturn(cchService < RT_SIZEOFMEMB(SUPCALLSERVICE, u.In.szName), VERR_INVALID_PARAMETER); + Assert(strlen(pszService) == cchService); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + return VERR_NOT_SUPPORTED; + + int rc; + if (!pReqHdr) + { + /* no data. */ + SUPCALLSERVICE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(0); + Req.Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(0); + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + memcpy(Req.u.In.szName, pszService, cchService); + Req.u.In.szName[cchService] = '\0'; + Req.u.In.uOperation = uOperation; + Req.u.In.u64Arg = u64Arg; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(0), &Req, SUP_IOCTL_CALL_SERVICE_SIZE(0)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + } + else if (SUP_IOCTL_CALL_SERVICE_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */ + { + AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER); + AssertReturn(pReqHdr->u32Magic == SUPR0SERVICEREQHDR_MAGIC, VERR_INVALID_MAGIC); + const size_t cbReq = pReqHdr->cbReq; + + PSUPCALLSERVICE pReq = (PSUPCALLSERVICE)alloca(SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(cbReq); + pReq->Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(cbReq); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + memcpy(pReq->u.In.szName, pszService, cchService); + pReq->u.In.szName[cchService] = '\0'; + pReq->u.In.uOperation = uOperation; + pReq->u.In.u64Arg = u64Arg; + memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(cbReq), pReq, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq); + } + else /** @todo may have to remove the size limits one this request... */ + AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_INTERNAL_ERROR); + return rc; +} + + +SUPR3DECL(int) SUPPageAlloc(size_t cPages, void **ppvPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + +#ifdef RT_OS_WINDOWS + /* + * Temporary hack for windows until we've sorted out the + * locked memory that doesn't need to be accessible from kernel space. + */ + return SUPPageAllocLockedEx(cPages, ppvPages, NULL); +#else + /* + * Call OS specific worker. + */ + return suplibOsPageAlloc(&g_supLibData, cPages, ppvPages); +#endif +} + + +SUPR3DECL(int) SUPPageFree(void *pvPages, size_t cPages) +{ + /* + * Validate. + */ + AssertPtrReturn(pvPages, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + +#ifdef RT_OS_WINDOWS + /* + * Temporary hack for windows, see above. + */ + return SUPPageFreeLocked(pvPages, cPages); +#else + /* + * Call OS specific worker. + */ + return suplibOsPageFree(&g_supLibData, pvPages, cPages); +#endif +} + + +SUPR3DECL(int) SUPPageLock(void *pvStart, size_t cPages, PSUPPAGE paPages) +{ + /* + * Validate. + */ + AssertPtr(pvStart); + AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart)); + AssertPtr(paPages); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + RTHCPHYS Phys = (uintptr_t)pvStart + PAGE_SIZE * 1024; + size_t iPage = cPages; + while (iPage-- > 0) + paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPPAGELOCK pReq = (PSUPPAGELOCK)RTMemTmpAllocZ(SUP_IOCTL_PAGE_LOCK_SIZE(cPages)); + if (RT_LIKELY(pReq)) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_PAGE_LOCK_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.pvR3 = pvStart; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_LOCK, pReq, SUP_IOCTL_PAGE_LOCK_SIZE(cPages)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + for (uint32_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } + } + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + + return rc; +} + + +SUPR3DECL(int) SUPPageUnlock(void *pvStart) +{ + /* + * Validate. + */ + AssertPtr(pvStart); + AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart)); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPPAGEUNLOCK Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_UNLOCK_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_UNLOCK_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvStart; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_UNLOCK, &Req, SUP_IOCTL_PAGE_UNLOCK_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPPageAllocLockedEx(size_t cPages, void **ppvPages, PSUPPAGE paPages) +{ + return SUPR3PageAllocEx(cPages, 0 /*fFlags*/, ppvPages, NULL /*pR0Ptr*/, paPages); +} + + +SUPR3DECL(int) SUPPageFreeLocked(void *pvPages, size_t cPages) +{ + /* + * Validate. + */ + AssertPtrReturn(pvPages, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* + * Check if we're employing the fallback or not to avoid the + * fuzzy handling of this in SUPR3PageFreeEx. + */ + int rc; + if (g_fSupportsPageAllocNoKernel) + rc = SUPR3PageFreeEx(pvPages, cPages); + else + { + /* fallback */ + rc = SUPPageUnlock(pvPages); + if (RT_SUCCESS(rc)) + rc = suplibOsPageFree(&g_supLibData, pvPages, cPages); + } + return rc; +} + + +/** + * Fallback for SUPPageAllocLockedEx on systems where RTR0MemObjPhysAllocNC isn't supported. + */ +static int supPagePageAllocNoKernelFallback(size_t cPages, void **ppvPages, PSUPPAGE paPages) +{ + int rc = suplibOsPageAlloc(&g_supLibData, cPages, ppvPages); + if (RT_SUCCESS(rc)) + { + if (!paPages) + paPages = (PSUPPAGE)alloca(sizeof(paPages[0]) * cPages); + rc = SUPPageLock(*ppvPages, cPages, paPages); + if (RT_FAILURE(rc)) + suplibOsPageFree(&g_supLibData, *ppvPages, cPages); + } + return rc; +} + + +SUPR3DECL(int) SUPR3PageAllocEx(size_t cPages, uint32_t fFlags, void **ppvPages, PRTR0PTR pR0Ptr, PSUPPAGE paPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertPtrNullReturn(pR0Ptr, VERR_INVALID_POINTER); + if (pR0Ptr) + *pR0Ptr = NIL_RTR0PTR; + AssertPtrNullReturn(paPages, VERR_INVALID_POINTER); + AssertMsgReturn(cPages > 0 && cPages <= VBOX_MAX_ALLOC_PAGE_COUNT, ("cPages=%zu\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + void *pv = RTMemPageAllocZ(cPages * PAGE_SIZE); + if (!pv) + return VERR_NO_MEMORY; + *ppvPages = pv; + if (pR0Ptr) + *pR0Ptr = (RTR0PTR)pv; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = (iPage + 4321) << PAGE_SHIFT; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } + return VINF_SUCCESS; + } + + /* + * Use fallback for non-R0 mapping? + */ + if ( !pR0Ptr + && !g_fSupportsPageAllocNoKernel) + return supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages); + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPPAGEALLOCEX pReq = (PSUPPAGEALLOCEX)RTMemTmpAllocZ(SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages)); + if (pReq) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + pReq->u.In.fKernelMapping = pR0Ptr != NULL; + pReq->u.In.fUserMapping = true; + pReq->u.In.fReserved0 = false; + pReq->u.In.fReserved1 = false; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_ALLOC_EX, pReq, SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages)); + if (RT_SUCCESS(rc)) + { + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + *ppvPages = pReq->u.Out.pvR3; + if (pR0Ptr) + *pR0Ptr = pReq->u.Out.pvR0; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } + } + else if ( rc == VERR_NOT_SUPPORTED + && !pR0Ptr) + { + g_fSupportsPageAllocNoKernel = false; + rc = supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages); + } + } + + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + return rc; + +} + + +SUPR3DECL(int) SUPR3PageMapKernel(void *pvR3, uint32_t off, uint32_t cb, uint32_t fFlags, PRTR0PTR pR0Ptr) +{ + /* + * Validate. + */ + AssertPtrReturn(pvR3, VERR_INVALID_POINTER); + AssertPtrReturn(pR0Ptr, VERR_INVALID_POINTER); + Assert(!(off & PAGE_OFFSET_MASK)); + Assert(!(cb & PAGE_OFFSET_MASK) && cb); + Assert(!fFlags); + *pR0Ptr = NIL_RTR0PTR; + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + return VERR_NOT_SUPPORTED; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPPAGEMAPKERNEL Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvR3; + Req.u.In.offSub = off; + Req.u.In.cbSub = cb; + Req.u.In.fFlags = fFlags; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_MAP_KERNEL, &Req, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *pR0Ptr = Req.u.Out.pvR0; + return rc; +} + + +SUPR3DECL(int) SUPR3PageFreeEx(void *pvPages, size_t cPages) +{ + /* + * Validate. + */ + AssertPtrReturn(pvPages, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + RTMemPageFree(pvPages); + return VINF_SUCCESS; + } + + /* + * Try normal free first, then if it fails check if we're using the fallback . + * for the allocations without kernel mappings and attempt unlocking it. + */ + NOREF(cPages); + SUPPAGEFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvPages; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_FREE, &Req, SUP_IOCTL_PAGE_FREE_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + if ( rc == VERR_INVALID_PARAMETER + && !g_fSupportsPageAllocNoKernel) + { + int rc2 = SUPPageUnlock(pvPages); + if (RT_SUCCESS(rc2)) + rc = suplibOsPageFree(&g_supLibData, pvPages, cPages); + } + } + return rc; +} + + +SUPR3DECL(void *) SUPContAlloc(size_t cPages, PRTHCPHYS pHCPhys) +{ + return SUPContAlloc2(cPages, NIL_RTR0PTR, pHCPhys); +} + + +SUPR3DECL(void *) SUPContAlloc2(size_t cPages, PRTR0PTR pR0Ptr, PRTHCPHYS pHCPhys) +{ + /* + * Validate. + */ + AssertPtrReturn(pHCPhys, NULL); + *pHCPhys = NIL_RTHCPHYS; + AssertPtrNullReturn(pR0Ptr, NULL); + if (pR0Ptr) + *pR0Ptr = NIL_RTR0PTR; + AssertPtrNullReturn(pHCPhys, NULL); + AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), NULL); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + void *pv = RTMemPageAllocZ(cPages * PAGE_SIZE); + if (pR0Ptr) + *pR0Ptr = (RTR0PTR)pv; + if (pHCPhys) + *pHCPhys = (uintptr_t)pv + (PAGE_SHIFT * 1024); + return pv; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTALLOC Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CONT_ALLOC_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_CONT_ALLOC_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.cPages = (uint32_t)cPages; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_ALLOC, &Req, SUP_IOCTL_CONT_ALLOC_SIZE); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(Req.Hdr.rc)) + { + *pHCPhys = Req.u.Out.HCPhys; + if (pR0Ptr) + *pR0Ptr = Req.u.Out.pvR0; + return Req.u.Out.pvR3; + } + + return NULL; +} + + +SUPR3DECL(int) SUPContFree(void *pv, size_t cPages) +{ + /* + * Validate. + */ + if (!pv) + return VINF_SUCCESS; + AssertPtrReturn(pv, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + RTMemPageFree(pv); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CONT_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_CONT_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pv; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_FREE, &Req, SUP_IOCTL_CONT_FREE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPLowAlloc(size_t cPages, void **ppvPages, PRTR0PTR ppvPagesR0, PSUPPAGE paPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertPtrReturn(paPages, VERR_INVALID_POINTER); + AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + *ppvPages = RTMemPageAllocZ((size_t)cPages * PAGE_SIZE); + if (!*ppvPages) + return VERR_NO_LOW_MEMORY; + + /* fake physical addresses. */ + RTHCPHYS Phys = (uintptr_t)*ppvPages + PAGE_SIZE * 1024; + size_t iPage = cPages; + while (iPage-- > 0) + paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPLOWALLOC pReq = (PSUPLOWALLOC)RTMemTmpAllocZ(SUP_IOCTL_LOW_ALLOC_SIZE(cPages)); + if (pReq) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_LOW_ALLOC_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_LOW_ALLOC_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_ALLOC, pReq, SUP_IOCTL_LOW_ALLOC_SIZE(cPages)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + *ppvPages = pReq->u.Out.pvR3; + if (ppvPagesR0) + *ppvPagesR0 = pReq->u.Out.pvR0; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + Assert(paPages[iPage].Phys <= UINT32_C(0xfffff000)); + } + } + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + + return rc; +} + + +SUPR3DECL(int) SUPLowFree(void *pv, size_t cPages) +{ + /* + * Validate. + */ + if (!pv) + return VINF_SUCCESS; + AssertPtrReturn(pv, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + RTMemPageFree(pv); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_LOW_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_LOW_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pv; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_FREE, &Req, SUP_IOCTL_LOW_FREE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3HardenedVerifyFile(const char *pszFilename, const char *pszMsg, PRTFILE phFile) +{ + /* + * Quick input validation. + */ + AssertPtr(pszFilename); + AssertPtr(pszMsg); + AssertReturn(!phFile, VERR_NOT_IMPLEMENTED); /** @todo Implement this. The deal is that we make sure the + file is the same we verified after opening it. */ + + /* + * Only do the actual check in hardened builds. + */ +#ifdef VBOX_WITH_HARDENING + int rc = supR3HardenedVerifyFile(pszFilename, false /* fFatal */); + if (RT_FAILURE(rc)) + LogRel(("SUPR3HardenedVerifyFile: %s: Verification of \"%s\" failed, rc=%Rrc\n", pszMsg, pszFilename, rc)); + return rc; +#else + return VINF_SUCCESS; +#endif +} + + +SUPR3DECL(int) SUPLoadModule(const char *pszFilename, const char *pszModule, void **ppvImageBase) +{ + int rc = VINF_SUCCESS; +#ifdef VBOX_WITH_HARDENING + /* + * Check that the module can be trusted. + */ + rc = supR3HardenedVerifyFile(pszFilename, false /* fFatal */); +#endif + if (RT_SUCCESS(rc)) + rc = supLoadModule(pszFilename, pszModule, NULL, ppvImageBase); + else + LogRel(("SUPLoadModule: Verification of \"%s\" failed, rc=%Rrc\n", rc)); + return rc; +} + + +SUPR3DECL(int) SUPR3LoadServiceModule(const char *pszFilename, const char *pszModule, + const char *pszSrvReqHandler, void **ppvImageBase) +{ + int rc = VINF_SUCCESS; + AssertPtrReturn(pszSrvReqHandler, VERR_INVALID_PARAMETER); + +#ifdef VBOX_WITH_HARDENING + /* + * Check that the module can be trusted. + */ + rc = supR3HardenedVerifyFile(pszFilename, false /* fFatal */); +#endif + if (RT_SUCCESS(rc)) + rc = supLoadModule(pszFilename, pszModule, pszSrvReqHandler, ppvImageBase); + else + LogRel(("SUPR3LoadServiceModule: Verification of \"%s\" failed, rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Resolve an external symbol during RTLdrGetBits(). + * + * @returns VBox status code. + * @param hLdrMod The loader module handle. + * @param pszModule Module name. + * @param pszSymbol Symbol name, NULL if uSymbol should be used. + * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used. + * @param pValue Where to store the symbol value (address). + * @param pvUser User argument. + */ +static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, + const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser) +{ + AssertPtr(pValue); + AssertPtr(pvUser); + + /* + * Only SUPR0 and VMMR0.r0 + */ + if ( pszModule + && *pszModule + && strcmp(pszModule, "SUPR0.dll") + && strcmp(pszModule, "VMMR0.r0")) + { + AssertMsgFailed(("%s is importing from %s! (expected 'SUPR0.dll' or 'VMMR0.r0', case-sensitiv)\n", pvUser, pszModule)); + return VERR_SYMBOL_NOT_FOUND; + } + + /* + * No ordinals. + */ + if (pszSymbol < (const char*)0x10000) + { + AssertMsgFailed(("%s is importing by ordinal (ord=%d)\n", pvUser, (int)(uintptr_t)pszSymbol)); + return VERR_SYMBOL_NOT_FOUND; + } + + /* + * Lookup symbol. + */ + /* skip the 64-bit ELF import prefix first. */ + if (!strncmp(pszSymbol, "SUPR0$", sizeof("SUPR0$") - 1)) + pszSymbol += sizeof("SUPR0$") - 1; + + /* + * Check the VMMR0.r0 module if loaded. + */ + /** @todo call the SUPLoadModule caller.... */ + /** @todo proper reference counting and such. */ + if (g_pvVMMR0 != NIL_RTR0PTR) + { + void *pvValue; + if (!SUPGetSymbolR0((void *)g_pvVMMR0, pszSymbol, &pvValue)) + { + *pValue = (uintptr_t)pvValue; + return VINF_SUCCESS; + } + } + + /* iterate the function table. */ + int c = g_pFunctions->u.Out.cFunctions; + PSUPFUNC pFunc = &g_pFunctions->u.Out.aFunctions[0]; + while (c-- > 0) + { + if (!strcmp(pFunc->szName, pszSymbol)) + { + *pValue = (uintptr_t)pFunc->pfn; + return VINF_SUCCESS; + } + pFunc++; + } + + /* + * The GIP. + */ + /** @todo R0 mapping? */ + if ( pszSymbol + && g_pSUPGlobalInfoPage + && g_pSUPGlobalInfoPageR0 + && !strcmp(pszSymbol, "g_SUPGlobalInfoPage")) + { + *pValue = (uintptr_t)g_pSUPGlobalInfoPageR0; + return VINF_SUCCESS; + } + + /* + * Despair. + */ + c = g_pFunctions->u.Out.cFunctions; + pFunc = &g_pFunctions->u.Out.aFunctions[0]; + while (c-- > 0) + { + AssertMsg2("%d: %s\n", g_pFunctions->u.Out.cFunctions - c, pFunc->szName); + pFunc++; + } + + AssertMsg2("%s is importing %s which we couldn't find\n", pvUser, pszSymbol); + AssertMsgFailed(("%s is importing %s which we couldn't find\n", pvUser, pszSymbol)); + if (g_u32FakeMode) + { + *pValue = 0xdeadbeef; + return VINF_SUCCESS; + } + return VERR_SYMBOL_NOT_FOUND; +} + + +/** Argument package for supLoadModuleCalcSizeCB. */ +typedef struct SUPLDRCALCSIZEARGS +{ + size_t cbStrings; + uint32_t cSymbols; + size_t cbImage; +} SUPLDRCALCSIZEARGS, *PSUPLDRCALCSIZEARGS; + +/** + * Callback used to calculate the image size. + * @return VINF_SUCCESS + */ +static DECLCALLBACK(int) supLoadModuleCalcSizeCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser) +{ + PSUPLDRCALCSIZEARGS pArgs = (PSUPLDRCALCSIZEARGS)pvUser; + if ( pszSymbol != NULL + && *pszSymbol + && Value <= pArgs->cbImage) + { + pArgs->cSymbols++; + pArgs->cbStrings += strlen(pszSymbol) + 1; + } + return VINF_SUCCESS; +} + + +/** Argument package for supLoadModuleCreateTabsCB. */ +typedef struct SUPLDRCREATETABSARGS +{ + size_t cbImage; + PSUPLDRSYM pSym; + char *pszBase; + char *psz; +} SUPLDRCREATETABSARGS, *PSUPLDRCREATETABSARGS; + +/** + * Callback used to calculate the image size. + * @return VINF_SUCCESS + */ +static DECLCALLBACK(int) supLoadModuleCreateTabsCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser) +{ + PSUPLDRCREATETABSARGS pArgs = (PSUPLDRCREATETABSARGS)pvUser; + if ( pszSymbol != NULL + && *pszSymbol + && Value <= pArgs->cbImage) + { + pArgs->pSym->offSymbol = (uint32_t)Value; + pArgs->pSym->offName = pArgs->psz - pArgs->pszBase; + pArgs->pSym++; + + size_t cbCopy = strlen(pszSymbol) + 1; + memcpy(pArgs->psz, pszSymbol, cbCopy); + pArgs->psz += cbCopy; + } + return VINF_SUCCESS; +} + + +/** + * Worker for SUPLoadModule(). + * + * @returns VBox status code. + * @param pszFilename Name of the VMMR0 image file + */ +static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler, void **ppvImageBase) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszModule, VERR_INVALID_PARAMETER); + AssertPtrReturn(ppvImageBase, VERR_INVALID_PARAMETER); + AssertReturn(strlen(pszModule) < RT_SIZEOFMEMB(SUPLDROPEN, u.In.szName), VERR_FILENAME_TOO_LONG); + + const bool fIsVMMR0 = !strcmp(pszModule, "VMMR0.r0"); + AssertReturn(!pszSrvReqHandler || !fIsVMMR0, VERR_INTERNAL_ERROR); + *ppvImageBase = NULL; + + /* + * Open image file and figure its size. + */ + RTLDRMOD hLdrMod; + int rc = RTLdrOpen(pszFilename, &hLdrMod); + if (!RT_SUCCESS(rc)) + return rc; + + SUPLDRCALCSIZEARGS CalcArgs; + CalcArgs.cbStrings = 0; + CalcArgs.cSymbols = 0; + CalcArgs.cbImage = RTLdrSize(hLdrMod); + rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCalcSizeCB, &CalcArgs); + if (RT_SUCCESS(rc)) + { + const uint32_t offSymTab = RT_ALIGN_32(CalcArgs.cbImage, 8); + const uint32_t offStrTab = offSymTab + CalcArgs.cSymbols * sizeof(SUPLDRSYM); + const uint32_t cbImage = RT_ALIGN_32(offStrTab + CalcArgs.cbStrings, 8); + + /* + * Open the R0 image. + */ + SUPLDROPEN OpenReq; + OpenReq.Hdr.u32Cookie = g_u32Cookie; + OpenReq.Hdr.u32SessionCookie = g_u32SessionCookie; + OpenReq.Hdr.cbIn = SUP_IOCTL_LDR_OPEN_SIZE_IN; + OpenReq.Hdr.cbOut = SUP_IOCTL_LDR_OPEN_SIZE_OUT; + OpenReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + OpenReq.Hdr.rc = VERR_INTERNAL_ERROR; + OpenReq.u.In.cbImage = cbImage; + strcpy(OpenReq.u.In.szName, pszModule); + if (!g_u32FakeMode) + { + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_OPEN, &OpenReq, SUP_IOCTL_LDR_OPEN_SIZE); + if (RT_SUCCESS(rc)) + rc = OpenReq.Hdr.rc; + } + else + { + OpenReq.u.Out.fNeedsLoading = true; + OpenReq.u.Out.pvImageBase = 0xef423420; + } + *ppvImageBase = (void *)OpenReq.u.Out.pvImageBase; + if ( RT_SUCCESS(rc) + && OpenReq.u.Out.fNeedsLoading) + { + /* + * We need to load it. + * Allocate memory for the image bits. + */ + PSUPLDRLOAD pLoadReq = (PSUPLDRLOAD)RTMemTmpAlloc(SUP_IOCTL_LDR_LOAD_SIZE(cbImage)); + if (pLoadReq) + { + /* + * Get the image bits. + */ + rc = RTLdrGetBits(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, + supLoadModuleResolveImport, (void *)pszModule); + + if (RT_SUCCESS(rc)) + { + /* + * Get the entry points. + */ + RTUINTPTR VMMR0EntryInt = 0; + RTUINTPTR VMMR0EntryFast = 0; + RTUINTPTR VMMR0EntryEx = 0; + RTUINTPTR SrvReqHandler = 0; + RTUINTPTR ModuleInit = 0; + RTUINTPTR ModuleTerm = 0; + if (fIsVMMR0) + { + rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryInt", &VMMR0EntryInt); + if (RT_SUCCESS(rc)) + rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryFast", &VMMR0EntryFast); + if (RT_SUCCESS(rc)) + rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryEx", &VMMR0EntryEx); + } + else if (pszSrvReqHandler) + rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, pszSrvReqHandler, &SrvReqHandler); + if (RT_SUCCESS(rc)) + { + int rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "ModuleInit", &ModuleInit); + if (RT_FAILURE(rc2)) + ModuleInit = 0; + + rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.achImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "ModuleTerm", &ModuleTerm); + if (RT_FAILURE(rc2)) + ModuleTerm = 0; + } + if (RT_SUCCESS(rc)) + { + /* + * Create the symbol and string tables. + */ + SUPLDRCREATETABSARGS CreateArgs; + CreateArgs.cbImage = CalcArgs.cbImage; + CreateArgs.pSym = (PSUPLDRSYM)&pLoadReq->u.In.achImage[offSymTab]; + CreateArgs.pszBase = (char *)&pLoadReq->u.In.achImage[offStrTab]; + CreateArgs.psz = CreateArgs.pszBase; + rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCreateTabsCB, &CreateArgs); + if (RT_SUCCESS(rc)) + { + AssertRelease((size_t)(CreateArgs.psz - CreateArgs.pszBase) <= CalcArgs.cbStrings); + AssertRelease((size_t)(CreateArgs.pSym - (PSUPLDRSYM)&pLoadReq->u.In.achImage[offSymTab]) <= CalcArgs.cSymbols); + + /* + * Upload the image. + */ + pLoadReq->Hdr.u32Cookie = g_u32Cookie; + pLoadReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pLoadReq->Hdr.cbIn = SUP_IOCTL_LDR_LOAD_SIZE_IN(cbImage); + pLoadReq->Hdr.cbOut = SUP_IOCTL_LDR_LOAD_SIZE_OUT; + pLoadReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_IN; + pLoadReq->Hdr.rc = VERR_INTERNAL_ERROR; + + pLoadReq->u.In.pfnModuleInit = (RTR0PTR)ModuleInit; + pLoadReq->u.In.pfnModuleTerm = (RTR0PTR)ModuleTerm; + if (fIsVMMR0) + { + pLoadReq->u.In.eEPType = SUPLDRLOADEP_VMMR0; + pLoadReq->u.In.EP.VMMR0.pvVMMR0 = OpenReq.u.Out.pvImageBase; + pLoadReq->u.In.EP.VMMR0.pvVMMR0EntryInt = (RTR0PTR)VMMR0EntryInt; + pLoadReq->u.In.EP.VMMR0.pvVMMR0EntryFast= (RTR0PTR)VMMR0EntryFast; + pLoadReq->u.In.EP.VMMR0.pvVMMR0EntryEx = (RTR0PTR)VMMR0EntryEx; + } + else if (pszSrvReqHandler) + { + pLoadReq->u.In.eEPType = SUPLDRLOADEP_SERVICE; + pLoadReq->u.In.EP.Service.pfnServiceReq = (RTR0PTR)SrvReqHandler; + pLoadReq->u.In.EP.Service.apvReserved[0] = NIL_RTR0PTR; + pLoadReq->u.In.EP.Service.apvReserved[1] = NIL_RTR0PTR; + pLoadReq->u.In.EP.Service.apvReserved[2] = NIL_RTR0PTR; + } + else + pLoadReq->u.In.eEPType = SUPLDRLOADEP_NOTHING; + pLoadReq->u.In.offStrTab = offStrTab; + pLoadReq->u.In.cbStrTab = (uint32_t)CalcArgs.cbStrings; + AssertRelease(pLoadReq->u.In.cbStrTab == CalcArgs.cbStrings); + pLoadReq->u.In.offSymbols = offSymTab; + pLoadReq->u.In.cSymbols = CalcArgs.cSymbols; + pLoadReq->u.In.cbImage = cbImage; + pLoadReq->u.In.pvImageBase = OpenReq.u.Out.pvImageBase; + if (!g_u32FakeMode) + { + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_LOAD, pLoadReq, SUP_IOCTL_LDR_LOAD_SIZE(cbImage)); + if (RT_SUCCESS(rc)) + rc = pLoadReq->Hdr.rc; + } + else + rc = VINF_SUCCESS; + if ( RT_SUCCESS(rc) + || rc == VERR_ALREADY_LOADED /* A competing process. */ + ) + { + LogRel(("SUP: Loaded %s (%s) at %#p - ModuleInit at %RTptr and ModuleTerm at %RTptr\n", pszModule, pszFilename, + OpenReq.u.Out.pvImageBase, ModuleInit, ModuleTerm)); + if (fIsVMMR0) + { + g_pvVMMR0 = OpenReq.u.Out.pvImageBase; + LogRel(("SUP: VMMR0EntryEx located at %RTptr, VMMR0EntryFast at %RTptr and VMMR0EntryInt at %RTptr\n", + VMMR0EntryEx, VMMR0EntryFast, VMMR0EntryInt)); + } +#ifdef RT_OS_WINDOWS + LogRel(("SUP: windbg> .reload /f %s=%#p\n", pszFilename, OpenReq.u.Out.pvImageBase)); +#endif + + RTMemTmpFree(pLoadReq); + RTLdrClose(hLdrMod); + return VINF_SUCCESS; + } + } + } + } + RTMemTmpFree(pLoadReq); + } + else + { + AssertMsgFailed(("failed to allocated %d bytes for SUPLDRLOAD_IN structure!\n", SUP_IOCTL_LDR_LOAD_SIZE(cbImage))); + rc = VERR_NO_TMP_MEMORY; + } + } + else if (RT_SUCCESS(rc)) + { + if (fIsVMMR0) + g_pvVMMR0 = OpenReq.u.Out.pvImageBase; + LogRel(("SUP: Opened %s (%s) at %#p.\n", pszModule, pszFilename, OpenReq.u.Out.pvImageBase)); +#ifdef RT_OS_WINDOWS + LogRel(("SUP: windbg> .reload /f %s=%#p\n", pszFilename, OpenReq.u.Out.pvImageBase)); +#endif + } + } + RTLdrClose(hLdrMod); + return rc; +} + + +SUPR3DECL(int) SUPFreeModule(void *pvImageBase) +{ + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + g_pvVMMR0 = NIL_RTR0PTR; + return VINF_SUCCESS; + } + + /* + * Free the requested module. + */ + SUPLDRFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_LDR_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_LDR_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvImageBase = (RTR0PTR)pvImageBase; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_FREE, &Req, SUP_IOCTL_LDR_FREE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if ( RT_SUCCESS(rc) + && (RTR0PTR)pvImageBase == g_pvVMMR0) + g_pvVMMR0 = NIL_RTR0PTR; + return rc; +} + + +SUPR3DECL(int) SUPGetSymbolR0(void *pvImageBase, const char *pszSymbol, void **ppvValue) +{ + *ppvValue = NULL; + + /* fake */ + if (RT_UNLIKELY(g_u32FakeMode)) + { + *ppvValue = (void *)(uintptr_t)0xdeadf00d; + return VINF_SUCCESS; + } + + /* + * Do ioctl. + */ + SUPLDRGETSYMBOL Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_LDR_GET_SYMBOL_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_LDR_GET_SYMBOL_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvImageBase = (RTR0PTR)pvImageBase; + size_t cchSymbol = strlen(pszSymbol); + if (cchSymbol >= sizeof(Req.u.In.szSymbol)) + return VERR_SYMBOL_NOT_FOUND; + memcpy(Req.u.In.szSymbol, pszSymbol, cchSymbol + 1); + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_GET_SYMBOL, &Req, SUP_IOCTL_LDR_GET_SYMBOL_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *ppvValue = (void *)Req.u.Out.pvSymbol; + return rc; +} + + +SUPR3DECL(int) SUPLoadVMM(const char *pszFilename) +{ + void *pvImageBase; + return SUPLoadModule(pszFilename, "VMMR0.r0", &pvImageBase); +} + + +SUPR3DECL(int) SUPUnloadVMM(void) +{ + return SUPFreeModule((void*)g_pvVMMR0); +} + + +SUPR3DECL(int) SUPGipGetPhys(PRTHCPHYS pHCPhys) +{ + if (g_pSUPGlobalInfoPage) + { + *pHCPhys = g_HCPhysSUPGlobalInfoPage; + return VINF_SUCCESS; + } + *pHCPhys = NIL_RTHCPHYS; + return VERR_WRONG_ORDER; +} + + +/** + * Worker for SUPR3HardenedLdrLoad and SUPR3HardenedLdrLoadAppPriv. + * + * @returns iprt status code. + * @param pszFilename The full file name. + * @param phLdrMod Where to store the handle to the loaded module. + */ +static int supR3HardenedLdrLoadIt(const char *pszFilename, PRTLDRMOD phLdrMod) +{ +#ifdef VBOX_WITH_HARDENING + /* + * Verify the image file. + */ + int rc = supR3HardenedVerifyFile(pszFilename, false /* fFatal */); + if (RT_FAILURE(rc)) + { + LogRel(("supR3HardenedLdrLoadIt: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc)); + return rc; + } +#endif + + /* + * Try load it. + */ + return RTLdrLoad(pszFilename, phLdrMod); +} + + +SUPR3DECL(int) SUPR3HardenedLdrLoad(const char *pszFilename, PRTLDRMOD phLdrMod) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER); + AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER); + *phLdrMod = NIL_RTLDRMOD; + AssertReturn(RTPathHavePath(pszFilename), VERR_INVALID_PARAMETER); + + /* + * Add the default extension if it's missing. + */ + if (!RTPathHaveExt(pszFilename)) + { + const char *pszSuff = RTLdrGetSuff(); + size_t cchSuff = strlen(pszSuff); + size_t cchFilename = strlen(pszFilename); + char *psz = (char *)alloca(cchFilename + cchSuff + 1); + AssertReturn(psz, VERR_NO_TMP_MEMORY); + memcpy(psz, pszFilename, cchFilename); + memcpy(psz + cchFilename, pszSuff, cchSuff + 1); + pszFilename = psz; + } + + /* + * Pass it on to the common library loader. + */ + return supR3HardenedLdrLoadIt(pszFilename, phLdrMod); +} + + +SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod) +{ + LogFlow(("SUPR3HardenedLdrLoadAppPriv: pszFilename=%p:{%s} phLdrMod=%p\n", pszFilename, pszFilename, phLdrMod)); + + /* + * Validate input. + */ + AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER); + *phLdrMod = NIL_RTLDRMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER); + AssertMsgReturn(!RTPathHavePath(pszFilename), ("%s\n", pszFilename), VERR_INVALID_PARAMETER); + + /* + * Check the filename. + */ + size_t cchFilename = strlen(pszFilename); + AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER); + + const char *pszExt = ""; + size_t cchExt = 0; + if (!RTPathHaveExt(pszFilename)) + { + pszExt = RTLdrGetSuff(); + cchExt = strlen(pszExt); + } + + /* + * Construct the private arch path and check if the file exists. + */ + char szPath[RTPATH_MAX]; + int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - 1 - cchExt - cchFilename); + AssertRCReturn(rc, rc); + + char *psz = strchr(szPath, '\0'); + *psz++ = RTPATH_SLASH; + memcpy(psz, pszFilename, cchFilename); + psz += cchFilename; + memcpy(psz, pszExt, cchExt + 1); + + if (!RTPathExists(szPath)) + { + LogRel(("SUPR3HardenedLdrLoadAppPriv: \"%s\" not found\n", szPath)); + return VERR_FILE_NOT_FOUND; + } + + /* + * Pass it on to SUPR3HardenedLdrLoad. + */ + rc = SUPR3HardenedLdrLoad(szPath, phLdrMod); + + LogFlow(("SUPR3HardenedLdrLoadAppPriv: returns %Rrc\n", rc)); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/SUPLibInternal.h b/src/VBox/HostDrivers/Support/SUPLibInternal.h new file mode 100644 index 000000000..6b026256c --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPLibInternal.h @@ -0,0 +1,324 @@ +/* $Id: SUPLibInternal.h 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Internal header. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPLibInternal_h___ +#define ___SUPLibInternal_h___ + +#include <VBox/cdefs.h> +#include <VBox/types.h> +#include <iprt/stdarg.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** @def SUPLIB_DLL_SUFF + * The (typical) DLL/DYLIB/SO suffix. */ +#if defined(RT_OS_DARWIN) +# define SUPLIB_DLL_SUFF ".dylib" +#elif defined(RT_OS_L4) +# define SUPLIB_DLL_SUFF ".s.so" +#elif defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) +# define SUPLIB_DLL_SUFF ".dll" +#else +# define SUPLIB_DLL_SUFF ".so" +#endif + +/** @def SUPLIB_EXE_SUFF + * The (typical) executable suffix. */ +#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) +# define SUPLIB_EXE_SUFF ".exe" +#else +# define SUPLIB_EXE_SUFF "" +#endif + +/** @def SUP_HARDENED_SUID + * Whether we're employing set-user-ID-on-execute in the hardening. + */ +#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4) +# define SUP_HARDENED_SUID +#else +# undef SUP_HARDENED_SUID +#endif + +#ifdef IN_SUP_HARDENED_R3 +/** @name Make the symbols in SUPR3HardenedStatic different from the VBoxRT ones. + * We cannot rely on DECLHIDDEN to make this separation for us since it doesn't + * work with all GCC versions. So, we resort to old fashion precompiler hacking. + * @{ + */ +# define supR3HardenedPathAppPrivateNoArch supR3HardenedStaticPathAppPrivateNoArch +# define supR3HardenedPathAppPrivateArch supR3HardenedStaticPathAppPrivateArch +# define supR3HardenedPathSharedLibs supR3HardenedStaticPathSharedLibs +# define supR3HardenedPathAppDocs supR3HardenedStaticPathAppDocs +# define supR3HardenedPathProgram supR3HardenedStaticPathProgram +# define supR3HardenedPathFilename supR3HardenedStaticPathFilename +# define supR3HardenedFatalV supR3HardenedStaticFatalV +# define supR3HardenedFatal supR3HardenedStaticFatal +# define supR3HardenedFatalMsgV supR3HardenedStaticFatalMsgV +# define supR3HardenedFatalMsg supR3HardenedStaticFatalMsg +# define supR3HardenedErrorV supR3HardenedStaticErrorV +# define supR3HardenedError supR3HardenedStaticError +# define supR3HardenedVerifyAll supR3HardenedStaticVerifyAll +# define supR3HardenedVerifyDir supR3HardenedStaticVerifyDir +# define supR3HardenedVerifyFile supR3HardenedStaticVerifyFile +# define supR3HardenedGetPreInitData supR3HardenedStaticGetPreInitData +# define supR3HardenedRecvPreInitData supR3HardenedStaticRecvPreInitData +/** @} */ +#endif /* IN_SUP_HARDENED_R3 */ + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The negotiated interrupt number. */ +extern DECLHIDDEN(uint8_t) g_uchInterruptNo; +/** The negotiated cookie. */ +extern DECLHIDDEN(uint32_t) g_u32Cookie; +/** The negotiated cookie. */ +extern DECLHIDDEN(uint32_t) g_u32CookieSession; + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * The type of an installed file. + */ +typedef enum SUPINSTFILETYPE +{ + kSupIFT_Invalid = 0, + kSupIFT_Exe, + kSupIFT_Dll, + kSupIFT_Sys, + kSupIFT_Script, + kSupIFT_Data, + kSupIFT_End +} SUPINSTFILETYPE; + +/** + * Installation directory specifier. + */ +typedef enum SUPINSTDIR +{ + kSupID_Invalid = 0, + kSupID_Bin, + kSupID_AppBin, + kSupID_SharedLib, + kSupID_AppPrivArch, + kSupID_AppPrivArchComp, + kSupID_AppPrivNoArch, + kSupID_End +} SUPINSTDIR; + +/** + * Installed file. + */ +typedef struct SUPINSTFILE +{ + /** File type. */ + SUPINSTFILETYPE enmType; + /** Install directory. */ + SUPINSTDIR enmDir; + /** Optional (true) or mandatory (false. */ + bool fOptional; + /** File name. */ + const char *pszFile; +} SUPINSTFILE; +typedef SUPINSTFILE *PSUPINSTFILE; +typedef SUPINSTFILE const *PCSUPINSTFILE; + +/** + * Status data for a verified file. + */ +typedef struct SUPVERIFIEDFILE +{ + /** The file handle or descriptor. -1 if not open. */ + intptr_t hFile; + /** Whether the file has been validated. */ + bool fValidated; +} SUPVERIFIEDFILE; +typedef SUPVERIFIEDFILE *PSUPVERIFIEDFILE; +typedef SUPVERIFIEDFILE const *PCSUPVERIFIEDFILE; + +/** + * Status data for a verified directory. + */ +typedef struct SUPVERIFIEDDIR +{ + /** The directory handle or descriptor. -1 if not open. */ + intptr_t hDir; + /** Whether the directory has been validated. */ + bool fValidated; +} SUPVERIFIEDDIR; +typedef SUPVERIFIEDDIR *PSUPVERIFIEDDIR; +typedef SUPVERIFIEDDIR const *PCSUPVERIFIEDDIR; + + +/** + * SUPLib instance data. + * + * This is data that is passed from the static to the dynamic SUPLib + * in a hardened setup. + */ +typedef struct SUPLIBDATA +{ + /** The device handle. */ + RTFILE hDevice; +#if defined(RT_OS_DARWIN) + /** The connection to the VBoxSupDrv service. */ + void *pvConnection; +#elif defined(RT_OS_LINUX) + /** Indicates whether madvise(,,MADV_DONTFORK) works. */ + bool fSysMadviseWorks; +#elif defined(RT_OS_WINDOWS) +#endif +} SUPLIBDATA; +/** Pointer to the pre-init data. */ +typedef SUPLIBDATA *PSUPLIBDATA; +/** Pointer to const pre-init data. */ +typedef SUPLIBDATA const *PCSUPLIBDATA; + + +/** + * Pre-init data that is handed over from the hardened executable stub. + */ +typedef struct SUPPREINITDATA +{ + /** Magic value (SUPPREINITDATA_MAGIC). */ + uint32_t u32Magic; + /** The SUPLib instance data. */ + SUPLIBDATA Data; + /** The number of entries in paInstallFiles and paVerifiedFiles. */ + size_t cInstallFiles; + /** g_aSupInstallFiles. */ + PCSUPINSTFILE paInstallFiles; + /** g_aSupVerifiedFiles. */ + PCSUPVERIFIEDFILE paVerifiedFiles; + /** The number of entries in paVerifiedDirs. */ + size_t cVerifiedDirs; + /** g_aSupVerifiedDirs. */ + PCSUPVERIFIEDDIR paVerifiedDirs; + /** Magic value (SUPPREINITDATA_MAGIC). */ + uint32_t u32EndMagic; +} SUPPREINITDATA; +typedef SUPPREINITDATA *PSUPPREINITDATA; +typedef SUPPREINITDATA const *PCSUPPREINITDATA; + +/** Magic value for SUPPREINITDATA::u32Magic and SUPPREINITDATA::u32EndMagic. */ +#define SUPPREINITDATA_MAGIC UINT32_C(0xbeef0001) + +/** @copydoc supR3PreInit */ +typedef DECLCALLBACK(int) FNSUPR3PREINIT(PSUPPREINITDATA pPreInitData, uint32_t fFlags); +/** Pointer to supR3PreInit. */ +typedef FNSUPR3PREINIT *PFNSUPR3PREINIT; + + +/******************************************************************************* +* OS Specific Function * +*******************************************************************************/ +__BEGIN_DECLS +int suplibOsInstall(void); +int suplibOsUninstall(void); +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited); +int suplibOsTerm(PSUPLIBDATA pThis); +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq); +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu); +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages); +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages); + + +/** + * Performs the pre-initialization of the support library. + * + * This is dynamically resolved and invoked by the static library before it + * calls RTR3Init and thereby SUPR3Init. + * + * @returns IPRT status code. + * @param pPreInitData The pre init data. + * @param fFlags The SUPR3HardenedMain flags. + */ +DECLEXPORT(int) supR3PreInit(PSUPPREINITDATA pPreInitData, uint32_t fFlags); + + +/** @copydoc RTPathAppPrivateNoArch */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath); +/** @copydoc RTPathAppPrivateArch */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath); +/** @copydoc RTPathSharedLibs */ +DECLHIDDEN(int) supR3HardenedPathSharedLibs(char *pszPath, size_t cchPath); +/** @copydoc RTPathAppDocs */ +DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath); +/** @copydoc RTPathProgram */ +DECLHIDDEN(int) supR3HardenedPathProgram(char *pszPath, size_t cchPath); +/** @copydoc RTPathFilename */ +DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath); + +/** + * Display a fatal error and try call TrustedError or quit. + */ +DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va); + +/** + * Display a fatal error and try call TrustedError or quit. + */ +DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...); + +/** + * Display a fatal error and quit. + */ +DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va); + +/** + * Display a fatal error and quit. + */ +DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...); + +/** + * Display an error which may or may not be fatal. + */ +DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va); + +/** + * Display an error which may or may not be fatal. + */ +DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...); +DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, bool fLeaveFilesOpen, const char *pszProgName); +DECLHIDDEN(int) supR3HardenedVerifyDir(SUPINSTDIR enmDir, bool fFatal); +DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, bool fFatal); +DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData); +DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData); + + +__END_DECLS + + +#endif + diff --git a/src/VBox/HostDrivers/Support/SUPR0.def b/src/VBox/HostDrivers/Support/SUPR0.def new file mode 100644 index 000000000..659532b0d --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR0.def @@ -0,0 +1,129 @@ +; $Id: SUPR0.def 15838 2009-01-07 15:57:24Z vboxsync $ +;; @file +; VirtualBox Support Driver - Built-in exports. +; + +; +; Copyright (C) 2006-2007 Sun Microsystems, Inc. +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +; Clara, CA 95054 USA or visit http://www.sun.com if you need +; additional information or have any questions. +; + +LIBRARY SUPR0.dll + +EXPORTS + SUPR0AbsIs64bit + SUPR0Abs64bitKernelCS + SUPR0Abs64bitKernelSS + SUPR0Abs64bitKernelDS + SUPR0ComponentRegisterFactory + SUPR0ComponentDeregisterFactory + SUPR0ComponentQueryFactory + SUPR0ObjRegister + SUPR0ObjAddRef + SUPR0ObjAddRefEx + SUPR0ObjRelease + SUPR0ObjVerifyAccess + SUPR0LockMem + SUPR0UnlockMem + SUPR0ContAlloc + SUPR0ContFree + SUPR0LowAlloc + SUPR0LowFree + SUPR0MemAlloc + SUPR0MemGetPhys + SUPR0MemFree + SUPR0Printf + SUPR0GetPagingMode + SUPR0EnableVTx + RTMemAlloc + RTMemAllocZ + RTMemFree + RTMemRealloc + RTR0MemObjAllocLow + RTR0MemObjAllocPage + RTR0MemObjAllocPhys + RTR0MemObjAllocPhysNC + RTR0MemObjAllocCont + RTR0MemObjLockUser + RTR0MemObjMapKernel + RTR0MemObjMapUser + RTR0MemObjAddress + RTR0MemObjAddressR3 + RTR0MemObjSize + RTR0MemObjIsMapping + RTR0MemObjGetPagePhysAddr + RTR0MemObjFree + ; broken - RTSemMutexCreate + ; broken - RTSemMutexRequest + ; broken - RTSemMutexRelease + ; broken - RTSemMutexDestroy + RTSemEventCreate + RTSemEventSignal + RTSemEventWait + RTSemEventWaitNoResume + RTSemEventDestroy + RTSemEventMultiCreate + RTSemEventMultiSignal + RTSemEventMultiReset + RTSemEventMultiWait + RTSemEventMultiWaitNoResume + RTSemEventMultiDestroy + RTSemFastMutexCreate + RTSemFastMutexDestroy + RTSemFastMutexRequest + RTSemFastMutexRelease + RTSpinlockCreate + RTSpinlockDestroy + RTSpinlockAcquire + RTSpinlockRelease + RTSpinlockAcquireNoInts + RTSpinlockReleaseNoInts + RTTimeNanoTS + RTTimeMillieTS + RTTimeSystemNanoTS + RTTimeSystemMillieTS + RTThreadSelf + RTThreadNativeSelf + RTThreadSleep + RTThreadYield + RTMpOnAll + RTMpOnOthers + RTMpOnSpecific + RTMpIsCpuOnline + RTMpGetCount + RTMpCpuIdToSetIndex + RTMpCpuId + RTMpIsCpuWorkPending + RTPowerNotificationRegister + RTPowerNotificationDeregister + RTLogDefaultInstance + RTLogRelDefaultInstance + RTLogSetDefaultInstanceThread + RTLogLoggerExV + RTLogPrintfV + AssertMsg1 + AssertMsg2 + + ; data + g_SUPGlobalInfoPage DATA + diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClient.c b/src/VBox/HostDrivers/Support/SUPR0IdcClient.c new file mode 100644 index 000000000..fed29823d --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR0IdcClient.c @@ -0,0 +1,214 @@ +/* $Id: SUPR0IdcClient.c 11794 2008-08-29 09:13:37Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Core. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +static PSUPDRVIDCHANDLE volatile g_pMainHandle = NULL; + + +/** + * Opens the IDC interface of the support driver. + * + * This will perform basic version negotiations and fail if the + * minmum requirements aren't met. + * + * @returns VBox status code. + * @param pHandle The handle structure (output). + * @param uReqVersion The requested version. Pass 0 for default. + * @param uMinVersion The minimum required version. Pass 0 for default. + * @param puSessionVersion Where to store the session version. Optional. + * @param puDriverVersion Where to store the session version. Optional. + * @param puDriverRevision Where to store the SVN revision of the driver. Optional. + */ +SUPR0DECL(int) SUPR0IdcOpen(PSUPDRVIDCHANDLE pHandle, uint32_t uReqVersion, uint32_t uMinVersion, + uint32_t *puSessionVersion, uint32_t *puDriverVersion, uint32_t *puDriverRevision) +{ + unsigned uDefaultMinVersion; + SUPDRVIDCREQCONNECT Req; + int rc; + + /* + * Validate and set failure return values. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + pHandle->s.pSession = NULL; + + AssertPtrNullReturn(puSessionVersion, VERR_INVALID_POINTER); + if (puSessionVersion) + *puSessionVersion = 0; + + AssertPtrNullReturn(puDriverVersion, VERR_INVALID_POINTER); + if (puDriverVersion) + *puDriverVersion = 0; + + AssertPtrNullReturn(puDriverRevision, VERR_INVALID_POINTER); + if (puDriverRevision) + *puDriverRevision = 0; + + AssertReturn(!uMinVersion || (uMinVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER); + AssertReturn(!uReqVersion || (uReqVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER); + + /* + * Handle default version input and enforce minimum requirements made + * by this library. + * + * The clients will pass defaults (0), and only in the case that some + * special API feature was just added will they set an actual version. + * So, this is the place where can easily enforce a minimum IDC version + * on bugs and similar. It corresponds a bit to what SUPR3Init is + * responsible for. + */ + uDefaultMinVersion = SUPDRV_IDC_VERSION & UINT32_C(0xffff0000); + if (!uMinVersion || uMinVersion < uDefaultMinVersion) + uMinVersion = uDefaultMinVersion; + if (!uReqVersion || uReqVersion < uDefaultMinVersion) + uReqVersion = uDefaultMinVersion; + + /* + * Setup the connect request packet and call the OS specific function. + */ + Req.Hdr.cb = sizeof(Req); + Req.Hdr.rc = VERR_WRONG_ORDER; + Req.Hdr.pSession = NULL; + Req.u.In.u32MagicCookie = SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE; + Req.u.In.uMinVersion = uMinVersion; + Req.u.In.uReqVersion = uReqVersion; + rc = supR0IdcNativeOpen(pHandle, &Req); + if (RT_SUCCESS(rc)) + { + pHandle->s.pSession = Req.u.Out.pSession; + if (puSessionVersion) + *puSessionVersion = Req.u.Out.uSessionVersion; + if (puDriverVersion) + *puDriverVersion = Req.u.Out.uDriverVersion; + if (puDriverRevision) + *puDriverRevision = Req.u.Out.uDriverRevision; + + /* + * We don't really trust anyone, make sure the returned + * session and version values actually makes sense. + */ + if ( VALID_PTR(Req.u.Out.pSession) + && Req.u.Out.uSessionVersion >= uMinVersion + && (Req.u.Out.uSessionVersion & UINT32_C(0xffff0000)) == (SUPDRV_IDC_VERSION & UINT32_C(0xffff0000))) + { + ASMAtomicCmpXchgPtr((void * volatile *)&g_pMainHandle, pHandle, NULL); + return rc; + } + + AssertMsgFailed(("pSession=%p uSessionVersion=0x%x (r%u)\n", Req.u.Out.pSession, Req.u.Out.uSessionVersion, Req.u.Out.uDriverRevision)); + rc = VERR_VERSION_MISMATCH; + SUPR0IdcClose(pHandle); + } + + return rc; +} + + +/** + * Closes a IDC connection established by SUPR0IdcOpen. + * + * @returns VBox status code. + * @param pHandle The IDC handle. + */ +SUPR0DECL(int) SUPR0IdcClose(PSUPDRVIDCHANDLE pHandle) +{ + SUPDRVIDCREQHDR Req; + int rc; + + /* + * Catch closed handles and check that the session is valid. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + if (!pHandle->s.pSession) + return VERR_INVALID_HANDLE; + AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE); + + /* + * Create the request and hand it to the OS specific code. + */ + Req.cb = sizeof(Req); + Req.rc = VERR_WRONG_ORDER; + Req.pSession = pHandle->s.pSession; + rc = supR0IdcNativeClose(pHandle, &Req); + if (RT_SUCCESS(rc)) + { + pHandle->s.pSession = NULL; + ASMAtomicCmpXchgPtr((void * volatile *)&g_pMainHandle, NULL, pHandle); + } + return rc; +} + + +/** + * Get the SUPDRV session for the IDC connection. + * + * This is for use with SUPDRV and component APIs that requires a valid + * session handle. + * + * @returns The session handle on success, NULL if the IDC handle is invalid. + * + * @param pHandle The IDC handle. + */ +SUPR0DECL(PSUPDRVSESSION) SUPR0IdcGetSession(PSUPDRVIDCHANDLE pHandle) +{ + PSUPDRVSESSION pSession; + AssertPtrReturn(pHandle, NULL); + pSession = pHandle->s.pSession; + AssertPtrReturn(pSession, NULL); + return pSession; +} + + +/** + * Looks up a IDC handle by session. + * + * @returns The IDC handle on success, NULL on failure. + * @param pSession The session to lookup. + * + * @internal + */ +PSUPDRVIDCHANDLE supR0IdcGetHandleFromSession(PSUPDRVSESSION pSession) +{ + PSUPDRVIDCHANDLE pHandle = (PSUPDRVIDCHANDLE)ASMAtomicUoReadPtr((void * volatile *)&g_pMainHandle); + if ( VALID_PTR(pHandle) + && pHandle->s.pSession == pSession) + return pHandle; + return NULL; +} + diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c b/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c new file mode 100644 index 000000000..523b0c5e5 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c @@ -0,0 +1,94 @@ +/* $Id: SUPR0IdcClientComponent.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Component APIs. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +/** + * Registers a component factory with SUPDRV. + * + * @returns VBox status code. + * @param pHandle The IDC handle. + * @param pFactory The factory to register. + */ +SUPR0DECL(int) SUPR0IdcComponentRegisterFactory(PSUPDRVIDCHANDLE pHandle, PCSUPDRVFACTORY pFactory) +{ + SUPDRVIDCREQCOMPREGFACTORY Req; + + /* + * Validate the handle before we access it. + */ + AssertPtrReturn(pHandle, VERR_INVALID_HANDLE); + AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE); + + /* + * Construct and fire off the request. + */ + Req.Hdr.cb = sizeof(Req); + Req.Hdr.rc = VERR_WRONG_ORDER; + Req.Hdr.pSession = pHandle->s.pSession; + Req.u.In.pFactory = pFactory; + + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY, &Req.Hdr); +} + + +/** + * Deregisters a component factory with SUPDRV. + * + * @returns VBox status code. + * @param pHandle The IDC handle. + * @param pFactory The factory to register. + */ +SUPR0DECL(int) SUPR0IdcComponentDeregisterFactory(PSUPDRVIDCHANDLE pHandle, PCSUPDRVFACTORY pFactory) +{ + SUPDRVIDCREQCOMPDEREGFACTORY Req; + + /* + * Validate the handle before we access it. + */ + AssertPtrReturn(pHandle, VERR_INVALID_HANDLE); + AssertPtrReturn(pHandle->s.pSession, VERR_INVALID_HANDLE); + + /* + * Construct and fire off the request. + */ + Req.Hdr.cb = sizeof(Req); + Req.Hdr.rc = VERR_WRONG_ORDER; + Req.Hdr.pSession = pHandle->s.pSession; + Req.u.In.pFactory = pFactory; + + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY, &Req.Hdr); +} + diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h b/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h new file mode 100644 index 000000000..22133089b --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h @@ -0,0 +1,88 @@ +/* $Id: SUPR0IdcClientInternal.h 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - Internal header for the IDC client library. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPR0IdcClientInternal_h__ +#define ___SUPR0IdcClientInternal_h__ + +#include <VBox/types.h> +#include <iprt/assert.h> + +#ifdef RT_OS_WINDOWS +# if (_MSC_VER >= 1400) && !defined(VBOX_WITH_PATCHED_DDK) +# include <iprt/asm.h> +# define _InterlockedExchange _InterlockedExchange_StupidDDKVsCompilerCrap +# define _InterlockedExchangeAdd _InterlockedExchangeAdd_StupidDDKVsCompilerCrap +# define _InterlockedCompareExchange _InterlockedCompareExchange_StupidDDKVsCompilerCrap +# define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap +__BEGIN_DECLS +# include <ntddk.h> +__END_DECLS +# undef _InterlockedExchange +# undef _InterlockedExchangeAdd +# undef _InterlockedCompareExchange +# undef _InterlockedAddLargeStatistic +# else +__BEGIN_DECLS +# include <ntddk.h> +__END_DECLS +# endif +#endif /* RT_OS_WINDOWS */ + + +/** + * The hidden part of SUPDRVIDCHANDLE. + */ +struct SUPDRVIDCHANDLEPRIVATE +{ + /** Pointer to the session handle. */ + PSUPDRVSESSION pSession; +# ifdef RT_OS_WINDOWS + /** Pointer to the NT device object. */ + PDEVICE_OBJECT pDeviceObject; + /** Pointer to the NT file object. */ + PFILE_OBJECT pFileObject; +# endif +}; +/** Indicate that the structure is present. */ +#define SUPDRVIDCHANDLEPRIVATE_DECLARED 1 + +#include <VBox/sup.h> +#include "SUPDrvIDC.h" +AssertCompile(RT_SIZEOFMEMB(SUPDRVIDCHANDLE, apvPadding) >= sizeof(struct SUPDRVIDCHANDLEPRIVATE)); + +__BEGIN_DECLS +PSUPDRVIDCHANDLE supR0IdcGetHandleFromSession(PSUPDRVSESSION pSession); +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq); +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq); +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq); +__END_DECLS + +#endif + diff --git a/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c b/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c new file mode 100644 index 000000000..7a21a84c9 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR0IdcClientStubs.c @@ -0,0 +1,147 @@ +/* $Id: SUPR0IdcClientStubs.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Stubs for SUPR0 APIs. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +/** + * Resolves a symbol. + * + * @returns Pointer to the symbol on success, NULL on failure. + * + * @param pHandle The IDC handle. + * @param pszName The name of the symbol. + */ +static void supR0IdcGetSymbol(PSUPDRVIDCHANDLE pHandle, PFNRT *ppfn, const char *pszName) +{ + SUPDRVIDCREQGETSYM Req; + int rc; + + /* + * Create and send a get symbol request. + */ + Req.Hdr.cb = sizeof(Req); + Req.Hdr.rc = VERR_WRONG_ORDER; + Req.Hdr.pSession = pHandle->s.pSession; + Req.u.In.pszSymbol = pszName; + Req.u.In.pszModule = NULL; + rc = supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_GET_SYMBOL, &Req.Hdr); + if (RT_SUCCESS(rc)) + ASMAtomicWritePtr((void * volatile *)ppfn, Req.u.Out.pfnSymbol); +} + + +/** + * Resolves a symbol. + * + * @returns Pointer to the symbol on success, NULL on failure. + * + * @param pHandle The IDC handle. + * @param pszName The name of the symbol. + */ +static void supR0IdcGetSymbolBySession(PSUPDRVSESSION pSession, PFNRT *ppfn, const char *pszName) +{ + PSUPDRVIDCHANDLE pHandle = supR0IdcGetHandleFromSession(pSession); + if (pHandle) + supR0IdcGetSymbol(pHandle, ppfn, pszName); +} + + +SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2) +{ + static DECLCALLBACKPTR(void *, s_pfn)(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2); + DECLCALLBACKPTR(void *, pfn)(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2); + pfn = s_pfn; + if (!pfn) + { + supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjRegister"); + pfn = s_pfn; + if (!pfn) + return NULL; + } + + return pfn(pSession, enmType, pfnDestructor, pvUser1, pvUser2); +} + + +SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession) +{ + static DECLCALLBACKPTR(int, s_pfn)(void *pvObj, PSUPDRVSESSION pSession); + DECLCALLBACKPTR(int, pfn)(void *pvObj, PSUPDRVSESSION pSession); + pfn = s_pfn; + if (!pfn) + { + supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjAddRef"); + pfn = s_pfn; + if (!pfn) + return VERR_NOT_SUPPORTED; + } + + return pfn(pvObj, pSession); +} + + +SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession) +{ + static DECLCALLBACKPTR(int, s_pfn)(void *pvObj, PSUPDRVSESSION pSession); + DECLCALLBACKPTR(int, pfn)(void *pvObj, PSUPDRVSESSION pSession); + pfn = s_pfn; + if (!pfn) + { + supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjRelease"); + pfn = s_pfn; + if (!pfn) + return VERR_NOT_SUPPORTED; + } + + return pfn(pvObj, pSession); +} + + +SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName) +{ + static DECLCALLBACKPTR(int, s_pfn)(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName); + DECLCALLBACKPTR(int, pfn)(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName); + pfn = s_pfn; + if (!pfn) + { + supR0IdcGetSymbolBySession(pSession, (PFNRT *)&s_pfn, "SUPR0ObjVerifyAccess"); + pfn = s_pfn; + if (!pfn) + return VERR_NOT_SUPPORTED; + } + + return pfn(pvObj, pSession, pszObjName); +} + diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp new file mode 100644 index 000000000..08fea59f5 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR3HardenedIPRT.cpp @@ -0,0 +1,160 @@ +/* $Id: SUPR3HardenedIPRT.cpp 15352 2008-12-12 06:08:19Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Hardened Support Routines using IPRT. + */ + +/* + * Copyright (C) 2006-2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#include <VBox/sup.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/string.h> +#include <iprt/stdarg.h> +#include <iprt/assert.h> +#include <iprt/path.h> +#include <iprt/param.h> + +#include "SUPLibInternal.h" + + +/** + * @copydoc RTPathFilename + */ +DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath) +{ + return RTPathFilename(pszPath); +} + + +/** + * @copydoc RTPathAppPrivateNoArch + */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath) +{ + return RTPathAppPrivateNoArch(pszPath, cchPath); +} + + +/** + * @copydoc RTPathAppPrivateArch + */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath) +{ + return RTPathAppPrivateArch(pszPath, cchPath); +} + + +/** + * @copydoc RTPathSharedLibs + */ +DECLHIDDEN(int) supR3HardenedPathSharedLibs(char *pszPath, size_t cchPath) +{ + return RTPathSharedLibs(pszPath, cchPath); +} + + +/** + * @copydoc RTPathAppDocs + */ +DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath) +{ + return RTPathAppDocs(pszPath, cchPath); +} + + +/** + * @copydoc RTPathProgram + */ +DECLHIDDEN(int) supR3HardenedPathProgram(char *pszPath, size_t cchPath) +{ + return RTPathProgram(pszPath, cchPath); +} + + +DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va) +{ + va_list vaCopy; + va_copy(vaCopy, va); + AssertFatalMsgFailed(("%s (rc=%Rrc): %N", pszWhere, rc, pszMsgFmt, &vaCopy)); + va_end(vaCopy); +} + + +DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...) +{ + va_list va; + va_start(va, pszMsgFmt); + supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va); + va_end(va); +} + + +DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va) +{ + va_list vaCopy; + va_copy(vaCopy, va); + AssertFatalMsgFailed(("%N", pszFormat, &vaCopy)); + va_end(vaCopy); +} + + +DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supR3HardenedFatalV(pszFormat, va); + va_end(va); +} + + +DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va) +{ + if (fFatal) + supR3HardenedFatalV(pszFormat, va); + + va_list vaCopy; + va_copy(vaCopy, va); + AssertLogRelMsgFailed(("%N", pszFormat, &vaCopy)); /** @todo figure out why this ain't working, or at seems to be that way... */ + va_end(vaCopy); + + RTLogRelPrintfV(pszFormat, va); + return rc; +} + + +DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supR3HardenedErrorV(rc, fFatal, pszFormat, va); + va_end(va); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp new file mode 100644 index 000000000..58adb1a0f --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp @@ -0,0 +1,1068 @@ +/* $Id: SUPR3HardenedMain.cpp 16067 2009-01-20 07:52:25Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Hardened main(). + */ + +/* + * Copyright (C) 2006-2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#if defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> +# include <stdio.h> +# include <stdlib.h> +# include <dlfcn.h> + +#elif RT_OS_WINDOWS +# include <Windows.h> +# include <stdio.h> + +#else /* UNIXes */ +# include <iprt/types.h> /* stdint fun on darwin. */ + +# include <stdio.h> +# include <stdlib.h> +# include <dlfcn.h> +# include <limits.h> +# include <errno.h> +# include <unistd.h> +# include <sys/stat.h> +# include <sys/time.h> +# include <stdio.h> +# include <sys/types.h> +# if defined(RT_OS_LINUX) +# undef USE_LIB_PCAP /* don't depend on libcap as we had to depend on either + libcap1 or libcap2 */ + +# undef _POSIX_SOURCE +# include <sys/capability.h> +# include <sys/prctl.h> +# ifndef CAP_TO_MASK +# define CAP_TO_MASK(cap) RT_BIT(cap) +# endif +# elif defined(RT_OS_SOLARIS) +# include <priv.h> +# endif +# include <pwd.h> +# ifdef RT_OS_DARWIN +# include <mach-o/dyld.h> +# endif + +#endif + +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/string.h> +#include <iprt/param.h> + +#include "SUPLibInternal.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** @def SUP_HARDENED_SUID + * Whether we're employing set-user-ID-on-execute in the hardening. + */ +#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4) +# define SUP_HARDENED_SUID +#else +# undef SUP_HARDENED_SUID +#endif + +/** @def SUP_HARDENED_SYM + * Decorate a symbol that's resolved dynamically. + */ +#ifdef RT_OS_OS2 +# define SUP_HARDENED_SYM(sym) "_" sym +#else +# define SUP_HARDENED_SYM(sym) sym +#endif + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** @see RTR3InitEx */ +typedef DECLCALLBACK(int) FNRTR3INITEX(uint32_t iVersion, const char *pszProgramPath, bool fInitSUPLib); +typedef FNRTR3INITEX *PFNRTR3INITEX; + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */ +static SUPPREINITDATA g_SupPreInitData; +/** The progam executable path. */ +static char g_szSupLibHardenedExePath[RTPATH_MAX]; +/** The program directory path. */ +static char g_szSupLibHardenedDirPath[RTPATH_MAX]; + +/** The program name. */ +static const char *g_pszSupLibHardenedProgName; + +#ifdef SUP_HARDENED_SUID +/** The real UID at startup. */ +static uid_t g_uid; +/** The real GID at startup. */ +static gid_t g_gid; +# ifdef RT_OS_LINUX +static uint32_t g_uCaps; +# endif +#endif + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +#ifdef SUP_HARDENED_SUID +static void supR3HardenedMainDropPrivileges(void); +#endif +static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName); + + +/** + * @copydoc RTPathStripFilename. + */ +static void suplibHardenedPathStripFilename(char *pszPath) +{ + char *psz = pszPath; + char *pszLastSep = pszPath; + + for (;; psz++) + { + switch (*psz) + { + /* handle separators. */ +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + case ':': + pszLastSep = psz + 1; + break; + + case '\\': +#endif + case '/': + pszLastSep = psz; + break; + + /* the end */ + case '\0': + if (pszLastSep == pszPath) + *pszLastSep++ = '.'; + *pszLastSep = '\0'; + return; + } + } + /* will never get here */ +} + + +/** + * @copydoc RTPathFilename + */ +DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath) +{ + const char *psz = pszPath; + const char *pszLastComp = pszPath; + + for (;; psz++) + { + switch (*psz) + { + /* handle separators. */ +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + case ':': + pszLastComp = psz + 1; + break; + + case '\\': +#endif + case '/': + pszLastComp = psz + 1; + break; + + /* the end */ + case '\0': + if (*pszLastComp) + return (char *)(void *)pszLastComp; + return NULL; + } + } + + /* will never get here */ + return NULL; +} + + +/** + * @copydoc RTPathAppPrivateNoArch + */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath) +{ +#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE) + const char *pszSrcPath = RTPATH_APP_PRIVATE; + size_t cchPathPrivateNoArch = strlen(pszSrcPath); + if (cchPathPrivateNoArch >= cchPath) + supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %lu >= %lu\n", + (unsigned long)cchPathPrivateNoArch, (unsigned long)cchPath); + memcpy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1); + return VINF_SUCCESS; + +#else + return supR3HardenedPathProgram(pszPath, cchPath); +#endif +} + + +/** + * @copydoc RTPathAppPrivateArch + */ +DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath) +{ +#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH) + const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH; + size_t cchPathPrivateArch = strlen(pszSrcPath); + if (cchPathPrivateArch >= cchPath) + supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %lu >= %lu\n", + (unsigned long)cchPathPrivateArch, (unsigned long)cchPath); + memcpy(pszPath, pszSrcPath, cchPathPrivateArch + 1); + return VINF_SUCCESS; + +#else + return supR3HardenedPathProgram(pszPath, cchPath); +#endif +} + + +/** + * @copydoc RTPathSharedLibs + */ +DECLHIDDEN(int) supR3HardenedPathSharedLibs(char *pszPath, size_t cchPath) +{ +#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS) + const char *pszSrcPath = RTPATH_SHARED_LIBS; + size_t cchPathSharedLibs = strlen(pszSrcPath); + if (cchPathSharedLibs >= cchPath) + supR3HardenedFatal("supR3HardenedPathSharedLibs: Buffer overflow, %lu >= %lu\n", + (unsigned long)cchPathSharedLibs, (unsigned long)cchPath); + memcpy(pszPath, pszSrcPath, cchPathSharedLibs + 1); + return VINF_SUCCESS; + +#else + return supR3HardenedPathProgram(pszPath, cchPath); +#endif +} + + +/** + * @copydoc RTPathAppDocs + */ +DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath) +{ +#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS) + const char *pszSrcPath = RTPATH_APP_DOCS; + size_t cchPathAppDocs = strlen(pszSrcPath); + if (cchPathAppDocs >= cchPath) + supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %lu >= %lu\n", + (unsigned long)cchPathAppDocs, (unsigned long)cchPath); + memcpy(pszPath, pszSrcPath, cchPathAppDocs + 1); + return VINF_SUCCESS; + +#else + return supR3HardenedPathProgram(pszPath, cchPath); +#endif +} + + +/** + * Returns the full path to the executable. + * + * @returns IPRT status code. + * @param pszPath Where to store it. + * @param cchPath How big that buffer is. + */ +static void supR3HardenedGetFullExePath(void) +{ + /* + * Get the program filename. + * + * Most UNIXes have no API for obtaining the executable path, but provides a symbolic + * link in the proc file system that tells who was exec'ed. The bad thing about this + * is that we have to use readlink, one of the weirder UNIX APIs. + * + * Darwin, OS/2 and Windows all have proper APIs for getting the program file name. + */ +#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS) +# ifdef RT_OS_LINUX + int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1); +# elif defined(RT_OS_SOLARIS) + char szFileBuf[PATH_MAX + 1]; + sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid()); + int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1); +# else /* RT_OS_FREEBSD: */ + int cchLink = readlink("/proc/curproc/file", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1); +# endif + if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1) + supR3HardenedFatal("supR3HardenedPathProgram: couldn't read \"%s\", errno=%d cchLink=%d\n", + g_szSupLibHardenedExePath, errno, cchLink); + g_szSupLibHardenedExePath[cchLink] = '\0'; + +#elif defined(RT_OS_OS2) || defined(RT_OS_L4) + _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath)); + +#elif defined(RT_OS_DARWIN) + const char *pszImageName = _dyld_get_image_name(0); + if (!pszImageName) + supR3HardenedFatal("supR3HardenedPathProgram: _dyld_get_image_name(0) failed\n"); + size_t cchImageName = strlen(pszImageName); + if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath)) + supR3HardenedFatal("supR3HardenedPathProgram: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName); + memcpy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1); + +#elif defined(RT_OS_WINDOWS) + HMODULE hExe = GetModuleHandle(NULL); + if (!GetModuleFileName(hExe, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath))) + supR3HardenedFatal("supR3HardenedPathProgram: GetModuleFileName failed, rc=%d\n", GetLastError()); +#else +# error needs porting. +#endif + + /* + * Strip off the filename part (RTPathStripFilename()). + */ + strcpy(g_szSupLibHardenedDirPath, g_szSupLibHardenedExePath); + suplibHardenedPathStripFilename(g_szSupLibHardenedDirPath); +} + + +#ifdef RT_OS_LINUX +/** + * Checks if we can read /proc/self/exe. + * + * This is used on linux to see if we have to call init + * with program path or not. + * + * @returns true / false. + */ +static bool supR3HardenedMainIsProcSelfExeAccssible(void) +{ + char szPath[RTPATH_MAX]; + int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath)); + return cchLink != -1; +} +#endif /* RT_OS_LINUX */ + + + +/** + * @copydoc RTPathProgram + */ +DECLHIDDEN(int) supR3HardenedPathProgram(char *pszPath, size_t cchPath) +{ + /* + * Lazy init (probably not required). + */ + if (!g_szSupLibHardenedDirPath[0]) + supR3HardenedGetFullExePath(); + + /* + * Calc the length and check if there is space before copying. + */ + unsigned cch = strlen(g_szSupLibHardenedDirPath) + 1; + if (cch <= cchPath) + { + memcpy(pszPath, g_szSupLibHardenedDirPath, cch + 1); + return VINF_SUCCESS; + } + + supR3HardenedFatal("supR3HardenedPathProgram: Buffer too small (%u < %u)\n", cchPath, cch); + return VERR_BUFFER_OVERFLOW; +} + + +DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va) +{ + /* + * To the console first, like supR3HardenedFatalV. + */ + fprintf(stderr, "%s: Error %d in %s!\n", g_pszSupLibHardenedProgName, rc, pszWhere); + fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName); + va_list vaCopy; + va_copy(vaCopy, va); + vfprintf(stderr, pszMsgFmt, vaCopy); + va_end(vaCopy); + fprintf(stderr, "\n"); + + switch (enmWhat) + { + case kSupInitOp_Driver: + fprintf(stderr, + "\n" + "%s: Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n", + g_pszSupLibHardenedProgName); + break; + + case kSupInitOp_IPRT: + case kSupInitOp_Integrity: + case kSupInitOp_RootCheck: + fprintf(stderr, + "\n" + "%s: Tip! It may help to reinstall VirtualBox.\n", + g_pszSupLibHardenedProgName); + break; + + default: + /* no hints here */ + break; + } + +#ifdef SUP_HARDENED_SUID + /* + * Drop any root privileges we might be holding, this won't return + * if it fails but end up calling supR3HardenedFatal[V]. + */ + supR3HardenedMainDropPrivileges(); +#endif /* SUP_HARDENED_SUID */ + + /* + * Now try resolve and call the TrustedError entry point if we can + * find it. We'll fork before we attempt this because that way the + * session management in main will see us exiting immediately (if + * it's invovled with us). + */ +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + int pid = fork(); + if (pid <= 0) +#endif + { + PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName); + if (pfnTrustedError) + pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va); + } + + /* + * Quit + */ + for (;;) +#ifdef _MSC_VER + exit(1); +#else + _Exit(1); +#endif +} + + +DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...) +{ + va_list va; + va_start(va, pszMsgFmt); + supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va); + va_end(va); +} + + +DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va) +{ + fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName); + vfprintf(stderr, pszFormat, va); + for (;;) +#ifdef _MSC_VER + exit(1); +#else + _Exit(1); +#endif +} + + +DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supR3HardenedFatalV(pszFormat, va); + va_end(va); +} + + +DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va) +{ + if (fFatal) + supR3HardenedFatalV(pszFormat, va); + + fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName); + vfprintf(stderr, pszFormat, va); + return rc; +} + + +DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supR3HardenedErrorV(rc, fFatal, pszFormat, va); + va_end(va); + return rc; +} + + +/** + * Wrapper around snprintf which will throw a fatal error on buffer overflow. + * + * @returns Number of chars in the result string. + * @param pszDst The destination buffer. + * @param cchDst The size of the buffer. + * @param pszFormat The format string. + * @param ... Format arguments. + */ +static size_t supR3HardenedStrPrintf(char *pszDst, size_t cchDst, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); +#ifdef _MSC_VER + int cch = _vsnprintf(pszDst, cchDst, pszFormat, va); +#else + int cch = vsnprintf(pszDst, cchDst, pszFormat, va); +#endif + va_end(va); + if ((unsigned)cch >= cchDst || cch < 0) + supR3HardenedFatal("supR3HardenedStrPrintf: buffer overflow, %d >= %lu\n", cch, (long)cchDst); + return cch; +} + + +/** + * Attempts to open /dev/vboxdrv (or equvivalent). + * + * @remarks This function will not return on failure. + */ +static void supR3HardenedMainOpenDevice(void) +{ + int rc = suplibOsInit(&g_SupPreInitData.Data, false); + if (RT_SUCCESS(rc)) + return; + + switch (rc) + { + /** @todo better messages! */ + case VERR_VM_DRIVER_NOT_INSTALLED: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_VM_DRIVER_NOT_INSTALLED"); + case VERR_VM_DRIVER_NOT_ACCESSIBLE: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_VM_DRIVER_NOT_ACCESSIBLE"); + case VERR_VM_DRIVER_LOAD_ERROR: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_VM_DRIVER_LOAD_ERROR"); + case VERR_VM_DRIVER_OPEN_ERROR: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_VM_DRIVER_OPEN_ERROR"); + case VERR_VM_DRIVER_VERSION_MISMATCH: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_VM_DRIVER_VERSION_MISMATCH"); + case VERR_ACCESS_DENIED: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "VERR_ACCESS_DENIED"); + default: + supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, + "Unknown rc=%d", rc); + } +} + + +#ifdef SUP_HARDENED_SUID + +/** + * Grabs extra non-root capabilities / privileges that we might require. + * + * This is currently only used for being able to do ICMP from the NAT engine. + * + * @note We still have root privileges at the time of this call. + */ +static void supR3HardenedMainGrabCapabilites(void) +{ +# if defined(RT_OS_LINUX) + /* + * We are about to drop all our privileges. Remove all capabilities but + * keep the cap_net_raw capability for ICMP sockets for the NAT stack. + */ + if (g_uCaps != 0) + { +# ifdef USE_LIB_PCAP + /* XXX cap_net_bind_service */ + if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep"))) + prctl(PR_SET_KEEPCAPS, /*keep=*/1, 0, 0, 0); +# else + cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr)); + cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap)); + memset(hdr, 0, sizeof(*hdr)); + hdr->version = _LINUX_CAPABILITY_VERSION; + memset(cap, 0, sizeof(*cap)); + cap->effective = g_uCaps; + cap->permitted = g_uCaps; + if (!capset(hdr, cap)) + prctl(PR_SET_KEEPCAPS, /*keep=*/1, 0, 0, 0); +# endif /* !USE_LIB_PCAP */ + } + +# elif defined(RT_OS_SOLARIS) + /* + * Add net_icmpaccess privilege to permitted, effective and inheritable privileges + * before dropping root privileges. + */ + priv_set_t *pPrivSet = priv_str_to_set("basic", ",", NULL); + if (pPrivSet) + { + priv_addset(pPrivSet, PRIV_NET_ICMPACCESS); + int rc = setppriv(PRIV_SET, PRIV_INHERITABLE, pPrivSet); + if (!rc) + { + rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet); + if (!rc) + { + rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivSet); + if (rc) + supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effectives privilege set.\n"); + } + else + supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n"); + } + else + supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set inheritable privilege set.\n"); + + priv_freeset(pPrivSet); + } + else + supR3HardenedError(-1, false, "SUPR3HardenedMain: failed to get basic privilege set.\n"); + +# endif +} + +/* + * Look at the environment for some special options. + */ +static void supR3GrabOptions(void) +{ + const char *pszOpt; + +# ifdef RT_OS_LINUX + g_uCaps = 0; + + /* + * Do _not_ perform any capability-related system calls for root processes + * (leaving g_uCaps at 0). + * (Hint: getuid gets the real user id, not the effective.) + */ + if (getuid() != 0) + { + /* + * CAP_NET_RAW. + * Default: enabled. + * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'. + */ + pszOpt = getenv("VBOX_HARD_CAP_NET_RAW"); + if ( !pszOpt + || memcmp(pszOpt, "0", sizeof("0")) != 0) + g_uCaps = CAP_TO_MASK(CAP_NET_RAW); + + /* + * CAP_NET_BIND_SERVICE. + * Default: disabled. + * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'. + */ + pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE"); + if ( pszOpt + && memcmp(pszOpt, "0", sizeof("0")) != 0) + g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE); + } +# endif +} + +/** + * Drop any root privileges we might be holding. + */ +static void supR3HardenedMainDropPrivileges(void) +{ + /* + * Try use setre[ug]id since this will clear the save uid/gid and thus + * leave fewer traces behind that libs like GTK+ may pick up. + */ + uid_t euid, ruid, suid; + gid_t egid, rgid, sgid; +# if defined(RT_OS_DARWIN) + /* The really great thing here is that setreuid isn't available on + OS X 10.4, libc emulates it. While 10.4 have a sligtly different and + non-standard setuid implementation compared to 10.5, the following + works the same way with both version since we're super user (10.5 req). + The following will set all three variants of the group and user IDs. */ + setgid(g_gid); + setuid(g_uid); + euid = geteuid(); + ruid = suid = getuid(); + egid = getegid(); + rgid = sgid = getgid(); + +# elif defined(RT_OS_SOLARIS) + /* Solaris doesn't have setresuid, but the setreuid interface is BSD + compatible and will set the saved uid to euid when we pass it a ruid + that isn't -1 (which we do). */ + setregid(g_gid, g_gid); + setreuid(g_uid, g_uid); + euid = geteuid(); + ruid = suid = getuid(); + egid = getegid(); + rgid = sgid = getgid(); + +# else + /* This is the preferred one, full control no questions about semantics. + PORTME: If this isn't work, try join one of two other gangs above. */ + setresgid(g_gid, g_gid, g_gid); + setresuid(g_uid, g_uid, g_uid); + if (getresuid(&ruid, &euid, &suid) != 0) + { + euid = geteuid(); + ruid = suid = getuid(); + } + if (getresgid(&rgid, &egid, &sgid) != 0) + { + egid = getegid(); + rgid = sgid = getgid(); + } +# endif + + + /* Check that it worked out all right. */ + if ( euid != g_uid + || ruid != g_uid + || suid != g_uid + || egid != g_gid + || rgid != g_gid + || sgid != g_gid) + supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!" + " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n", + euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid); + +# if RT_OS_LINUX + /* + * Re-enable the cap_net_raw capability which was disabled during setresuid. + */ + if (g_uCaps != 0) + { +# ifdef USE_LIB_PCAP + /** @todo Warn if that does not work? */ + /* XXX cap_net_bind_service */ + cap_set_proc(cap_from_text("cap_net_raw+ep")); +# else + cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr)); + cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap)); + memset(hdr, 0, sizeof(*hdr)); + hdr->version = _LINUX_CAPABILITY_VERSION; + memset(cap, 0, sizeof(*cap)); + cap->effective = g_uCaps; + cap->permitted = g_uCaps; + /** @todo Warn if that does not work? */ + capset(hdr, cap); +# endif /* !USE_LIB_PCAP */ + } +# endif +} + +#endif /* SUP_HARDENED_SUID */ + +/** + * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver, + * and calls RTR3Init. + * + * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit. + * + * @remarks VBoxRT contains both IPRT and SUPR3. + * @remarks This function will not return on failure. + */ +static void supR3HardenedMainInitRuntime(uint32_t fFlags) +{ + /* + * Construct the name. + */ + char szPath[RTPATH_MAX]; + supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF)); + strcat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF); + + /* + * Open it and resolve the symbols. + */ +#if defined(RT_OS_WINDOWS) + /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */ + HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */); + if (!hMod) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND, + "LoadLibraryEx(\"%s\",,) failed (rc=%d)", + szPath, GetLastError()); + PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx")); + if (!pfnRTInitEx) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND, + "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)", + szPath, GetLastError()); + + PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit")); + if (!pfnSUPPreInit) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND, + "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)", + szPath, GetLastError()); + +#else + /* the dlopen crowd */ + void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL); + if (!pvMod) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND, + "dlopen(\"%s\",) failed: %s", + szPath, dlerror()); + PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx")); + if (!pfnRTInitEx) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND, + "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s", + szPath, dlerror()); + PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit")); + if (!pfnSUPPreInit) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND, + "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s", + szPath, dlerror()); +#endif + + /* + * Make the calls. + */ + supR3HardenedGetPreInitData(&g_SupPreInitData); + int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags); + if (RT_FAILURE(rc)) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc, + "supR3PreInit failed with rc=%d", rc); + const char *pszExePath = NULL; +#ifdef RT_OS_LINUX + if (!supR3HardenedMainIsProcSelfExeAccssible()) + pszExePath = g_szSupLibHardenedExePath; +#endif + rc = pfnRTInitEx(0, pszExePath, !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)); + if (RT_FAILURE(rc)) + supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc, + "RTR3Init failed with rc=%d", rc); +} + + +/** + * Loads the DLL/SO/DYLIB containing the actual program and + * resolves the TrustedError symbol. + * + * This is very similar to supR3HardenedMainGetTrustedMain(). + * + * @returns Pointer to the trusted error symbol if it is exported, NULL + * and no error messages otherwise. + * @param pszProgName The program name. + */ +static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName) +{ + /* + * Construct the name. + */ + char szPath[RTPATH_MAX]; + supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10); + size_t cch = strlen(szPath); + supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF); + + /* + * Open it and resolve the symbol. + */ +#if defined(RT_OS_WINDOWS) + /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */ + HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */); + if (!hMod) + return NULL; + FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError")); + if (!pfn) + return NULL; + return (PFNSUPTRUSTEDERROR)pfn; + +#else + /* the dlopen crowd */ + void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL); + if (!pvMod) + return NULL; + void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError")); + if (!pvSym) + return NULL; + return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym; +#endif +} + + +/** + * Loads the DLL/SO/DYLIB containing the actual program and + * resolves the TrustedMain symbol. + * + * @returns Pointer to the trusted main of the actual program. + * @param pszProgName The program name. + * @remarks This function will not return on failure. + */ +static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName) +{ + /* + * Construct the name. + */ + char szPath[RTPATH_MAX]; + supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10); + size_t cch = strlen(szPath); + supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF); + + /* + * Open it and resolve the symbol. + */ +#if defined(RT_OS_WINDOWS) + /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */ + HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */); + if (!hMod) + supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n", + szPath, GetLastError()); + FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain")); + if (!pfn) + supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n", + szPath, GetLastError()); + return (PFNSUPTRUSTEDMAIN)pfn; + +#else + /* the dlopen crowd */ + void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL); + if (!pvMod) + supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n", + szPath, dlerror()); + void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain")); + if (!pvSym) + supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n", + szPath, dlerror()); + return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym; +#endif +} + + +/** + * Secure main. + * + * This is used for the set-user-ID-on-execute binaries on unixy systems + * and when using the open-vboxdrv-via-root-service setup on Windows. + * + * This function will perform the integrity checks of the VirtualBox + * installation, open the support driver, open the root service (later), + * and load the DLL corresponding to \a pszProgName and execute its main + * function. + * + * @returns Return code appropriate for main(). + * + * @param pszProgName The program name. This will be used to figure out which + * DLL/SO/DYLIB to load and execute. + * @param fFlags Flags. + * @param argc The argument count. + * @param argv The argument vector. + * @param envp The environment vector. + */ +DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp) +{ + /* + * Note! At this point there is no IPRT, so we will have to stick + * to basic CRT functions that everyone agree upon. + */ + g_pszSupLibHardenedProgName = pszProgName; + g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC; + g_SupPreInitData.Data.hDevice = NIL_RTFILE; + g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC; + +#ifdef SUP_HARDENED_SUID +# ifdef RT_OS_LINUX + /* + * On linux we have to make sure the path is initialized because we + * *might* not be able to access /proc/self/exe after the seteuid call. + */ + supR3HardenedGetFullExePath(); + +# endif + + /* + * Grab any options from the environment. + */ + supR3GrabOptions(); + + /* + * Check that we're root, if we aren't then the installation is butchered. + */ + g_uid = getuid(); + g_gid = getgid(); + if (geteuid() != 0 /* root */) + supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED, + "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)", + geteuid(), getegid(), g_uid, g_gid); +#endif + + /* + * Validate the installation. + */ + supR3HardenedVerifyAll(true /* fFatal */, false /* fLeaveFilesOpen */, pszProgName); + + /* + * Open the vboxdrv device. + */ + if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)) + supR3HardenedMainOpenDevice(); + + /* + * Open the root service connection. + */ + //if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_SVC)) + //supR3HardenedMainOpenService(&g_SupPreInitData, true /* fFatal */); + +#ifdef SUP_HARDENED_SUID + /* + * Grab additional capabilities / privileges. + */ + supR3HardenedMainGrabCapabilites(); + + /* + * Drop any root privileges we might be holding (won't return on failure) + */ + supR3HardenedMainDropPrivileges(); +#endif + + /* + * Load the IPRT, hand the SUPLib part the open driver and + * call RTR3Init. + */ + supR3HardenedMainInitRuntime(fFlags); + + /* + * Load the DLL/SO/DYLIB containing the actual program + * and pass control to it. + */ + PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName); + return pfnTrustedMain(argc, argv, envp); +} + + diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp new file mode 100644 index 000000000..5a6adf439 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp @@ -0,0 +1,810 @@ +/* $Id: SUPR3HardenedVerify.cpp 15353 2008-12-12 06:10:39Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Verification of Hardened Installation. + */ + +/* + * Copyright (C) 2006-2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#if defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> +# include <stdio.h> +# include <stdlib.h> +# include <unistd.h> +# include <sys/fcntl.h> +# include <sys/errno.h> +# include <sys/syslimits.h> + +#elif defined(RT_OS_WINDOWS) +# include <Windows.h> +# include <stdio.h> + +#else /* UNIXes */ +# include <sys/types.h> +# include <stdio.h> +# include <stdlib.h> +# include <dlfcn.h> +# include <fcntl.h> +# include <limits.h> +# include <errno.h> +# include <unistd.h> +# include <sys/stat.h> +# include <sys/time.h> +# include <sys/fcntl.h> +# include <stdio.h> +# include <pwd.h> +# ifdef RT_OS_DARWIN +# include <mach-o/dyld.h> +# endif + +#endif + +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/string.h> +#include <iprt/param.h> + +#include "SUPLibInternal.h" + + + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * The files that gets verified. + * + * @todo This needs reviewing against the linux packages. + * @todo The excessive use of kSupID_SharedLib needs to be reviewed at some point. For + * the time being we're building the linux packages with SharedLib pointing to + * AppPrivArch (lazy bird). + */ +static SUPINSTFILE const g_aSupInstallFiles[] = +{ + /* type, dir,fOptional, pszFile */ + /* ---------------------------------------------------------------------- */ + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMR0.r0" }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDR0.r0" }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDD2R0.r0" }, + + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMGC.gc" }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDGC.gc" }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDD2GC.gc" }, + + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxRT" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxVMM" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxREM" SUPLIB_DLL_SUFF }, +#if HC_ARCH_BITS == 32 + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxREM32" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxREM64" SUPLIB_DLL_SUFF }, +#endif + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxDD" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxDD2" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxDDU" SUPLIB_DLL_SUFF }, + +//#ifdef VBOX_WITH_DEBUGGER_GUI + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxDbg" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxDbg3" SUPLIB_DLL_SUFF }, +//#endif + + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxSharedClipboard" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxSharedFolders" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedCrOpenGL" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxGuestPropSvc" SUPLIB_DLL_SUFF }, + + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedCrOpenGL" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhostcrutil" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhosterrorspu" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLrenderspu" SUPLIB_DLL_SUFF }, + + { kSupIFT_Exe, kSupID_AppBin, false, "VBoxManage" SUPLIB_EXE_SUFF }, + + { kSupIFT_Exe, kSupID_AppBin, false, "VBoxSVC" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxSettings" SUPLIB_DLL_SUFF }, +#ifdef RT_OS_WINDOWS + /** @todo */ +#else + { kSupIFT_Exe, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxXPCOM" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxXPCOMIPCC" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxC" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxSVCM" SUPLIB_DLL_SUFF }, + { kSupIFT_Data, kSupID_AppPrivArchComp, false, "VBoxXPCOMBase.xpt" }, +#endif + +//#ifdef VBOX_WITH_VRDP + { kSupIFT_Dll, kSupID_SharedLib, true, "VRDPAuth" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxVRDP" SUPLIB_DLL_SUFF }, +//#endif + +//#ifdef VBOX_WITH_HEADLESS + { kSupIFT_Exe, kSupID_AppBin, true, "VBoxHeadless" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHeadless" SUPLIB_DLL_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxFFmpegFB" SUPLIB_DLL_SUFF }, +//#endif + +//#ifdef VBOX_WITH_QT4GUI + { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBox" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VirtualBox" SUPLIB_DLL_SUFF }, +# if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxKeyboard" SUPLIB_DLL_SUFF }, +# endif +//#endif + +//#ifdef VBOX_WITH_QTGUI + { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBox3" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VirtualBox3" SUPLIB_DLL_SUFF }, +# if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + { kSupIFT_Dll, kSupID_SharedLib, true, "VBoxKeyboard3" SUPLIB_DLL_SUFF }, +# endif +//#endif + +//#ifdef VBOX_WITH_VBOXSDL + { kSupIFT_Exe, kSupID_AppBin, true, "VBoxSDL" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSDL" SUPLIB_DLL_SUFF }, +//#endif + +//#ifdef VBOX_WITH_VBOXBFE + { kSupIFT_Exe, kSupID_AppBin, true, "VBoxBFE" SUPLIB_EXE_SUFF }, + { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxBFE" SUPLIB_DLL_SUFF }, +//#endif + +//#ifdef VBOX_WITH_WEBSERVICES + { kSupIFT_Exe, kSupID_AppBin, true, "vboxwebsrv" SUPLIB_EXE_SUFF }, +//#endif + +#ifdef RT_OS_LINUX + { kSupIFT_Exe, kSupID_AppBin, true, "VBoxTunctl" SUPLIB_EXE_SUFF }, +#endif +}; + + +/** Array parallel to g_aSupInstallFiles containing per-file status info. */ +static SUPVERIFIEDFILE g_aSupVerifiedFiles[RT_ELEMENTS(g_aSupInstallFiles)]; + +/** Array index by install directory specifier containing info about verified directories. */ +static SUPVERIFIEDDIR g_aSupVerifiedDirs[kSupID_End]; + + +/** + * Assembles the path to a dirtory. + * + * @returns VINF_SUCCESS on success, some error code on failure (fFatal + * decides whether it returns or not). + * + * @param enmDir The directory. + * @param pszDst Where to assemble the path. + * @param cchDst The size of the buffer. + * @param fFatal Whether failures should be treated as fatal (true) or not (false). + */ +static int supR3HardenedMakePath(SUPINSTDIR enmDir, char *pszDst, size_t cchDst, bool fFatal) +{ + int rc; + switch (enmDir) + { + case kSupID_AppBin: /** @todo fix this AppBin crap (uncertain wtf some binaries actually are installed). */ + case kSupID_Bin: + rc = supR3HardenedPathProgram(pszDst, cchDst); + break; + case kSupID_SharedLib: + rc = supR3HardenedPathSharedLibs(pszDst, cchDst); + break; + case kSupID_AppPrivArch: + rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst); + break; + case kSupID_AppPrivArchComp: + rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst); + if (RT_SUCCESS(rc)) + { + size_t off = strlen(pszDst); + if (cchDst - off >= sizeof("/components")) + memcpy(&pszDst[off], "/components", sizeof("/components")); + else + rc = VERR_BUFFER_OVERFLOW; + } + break; + case kSupID_AppPrivNoArch: + rc = supR3HardenedPathAppPrivateNoArch(pszDst, cchDst); + break; + default: + return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedMakePath: enmDir=%d\n", enmDir); + } + if (RT_FAILURE(rc)) + supR3HardenedError(rc, fFatal, + "supR3HardenedMakePath: enmDir=%d rc=%d\n", enmDir, rc); + return rc; +} + + + +/** + * Assembles the path to a file table entry, with or without the actual filename. + * + * @returns VINF_SUCCESS on success, some error code on failure (fFatal + * decides whether it returns or not). + * + * @param pFile The file table entry. + * @param pszDst Where to assemble the path. + * @param cchDst The size of the buffer. + * @param fWithFilename If set, the filename is included, otherwise it is omitted (no trailing slash). + * @param fFatal Whether failures should be treated as fatal (true) or not (false). + */ +static int supR3HardenedMakeFilePath(PCSUPINSTFILE pFile, char *pszDst, size_t cchDst, bool fWithFilename, bool fFatal) +{ + /* + * Combine supR3HardenedMakePath and the filename. + */ + int rc = supR3HardenedMakePath(pFile->enmDir, pszDst, cchDst, fFatal); + if (RT_SUCCESS(rc)) + { + size_t cchFile = strlen(pFile->pszFile); + size_t off = strlen(pszDst); + if (cchDst - off >= cchFile + 2) + { + pszDst[off++] = '/'; + memcpy(&pszDst[off], pFile->pszFile, cchFile + 1); + } + else + rc = supR3HardenedError(VERR_BUFFER_OVERFLOW, fFatal, + "supR3HardenedMakeFilePath: pszFile=%s off=%lu\n", + pFile->pszFile, (long)off); + } + return rc; +} + + +/** + * Verifies a directory. + * + * @returns VINF_SUCCESS on success. On failure, an error code is returned if + * fFatal is clear and if it's set the function wont return. + * @param enmDir The directory specifier. + * @param fFatal Whether validation failures should be treated as + * fatal (true) or not (false). + */ +DECLHIDDEN(int) supR3HardenedVerifyDir(SUPINSTDIR enmDir, bool fFatal) +{ + /* + * Validate the index just to be on the safe side... + */ + if (enmDir <= kSupID_Invalid || enmDir >= kSupID_End) + return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedVerifyDir: enmDir=%d\n", enmDir); + + /* + * Already validated? + */ + if (g_aSupVerifiedDirs[enmDir].fValidated) + return VINF_SUCCESS; /** @todo revalidate? */ + + /* initialize the entry. */ + if (g_aSupVerifiedDirs[enmDir].hDir != 0) + supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedVerifyDir: hDir=%p enmDir=%d\n", + (void *)g_aSupVerifiedDirs[enmDir].hDir, enmDir); + g_aSupVerifiedDirs[enmDir].hDir = -1; + g_aSupVerifiedDirs[enmDir].fValidated = false; + + /* + * Make the path and open the directory. + */ + char szPath[RTPATH_MAX]; + int rc = supR3HardenedMakePath(enmDir, szPath, sizeof(szPath), fFatal); + if (RT_SUCCESS(rc)) + { +#if defined(RT_OS_WINDOWS) + HANDLE hDir = CreateFile(szPath, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (hDir != INVALID_HANDLE_VALUE) + { + /** @todo check the type */ + /* That's all on windows, for now at least... */ + g_aSupVerifiedDirs[enmDir].hDir = (intptr_t)hDir; + g_aSupVerifiedDirs[enmDir].fValidated = true; + } + else + { + int err = GetLastError(); + rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal, + "supR3HardenedVerifyDir: Failed to open \"%s\": err=%d\n", + szPath, err); + } +#else /* UNIXY */ + int fd = open(szPath, O_RDONLY, 0); + if (fd >= 0) + { + /* + * On unixy systems we'll make sure the directory is owned by root + * and not writable by the group and user. + */ + struct stat st; + if (!fstat(fd, &st)) + { + + if ( st.st_uid == 0 + && !(st.st_mode & (S_IWGRP | S_IWOTH)) + && S_ISDIR(st.st_mode)) + { + g_aSupVerifiedDirs[enmDir].hDir = fd; + g_aSupVerifiedDirs[enmDir].fValidated = true; + } + else + { + if (!S_ISDIR(st.st_mode)) + rc = supR3HardenedError(VERR_NOT_A_DIRECTORY, fFatal, + "supR3HardenedVerifyDir: \"%s\" is not a directory\n", + szPath, (long)st.st_uid); + else if (st.st_uid) + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": not owned by root (st_uid=%ld)\n", + szPath, (long)st.st_uid); + else + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": group and/or other writable (st_mode=0%lo)\n", + szPath, (long)st.st_mode); + close(fd); + } + } + else + { + int err = errno; + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyDir: Failed to fstat \"%s\": %s (%d)\n", + szPath, strerror(err), err); + close(fd); + } + } + else + { + int err = errno; + rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal, + "supR3HardenedVerifyDir: Failed to open \"%s\": %s (%d)\n", + szPath, strerror(err), err); + } +#endif /* UNIXY */ + } + + return rc; +} + + +/** + * Verifies a file entry. + * + * @returns VINF_SUCCESS on success. On failure, an error code is returned if + * fFatal is clear and if it's set the function wont return. + * + * @param iFile The file table index of the file to be verified. + * @param fFatal Whether validation failures should be treated as + * fatal (true) or not (false). + * @param fLeaveFileOpen Whether the file should be left open. + */ +static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFileOpen) +{ + PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile]; + PSUPVERIFIEDFILE pVerified = &g_aSupVerifiedFiles[iFile]; + + /* + * Already done? + */ + if (pVerified->fValidated) + return VINF_SUCCESS; /** @todo revalidate? */ + + + /* initialize the entry. */ + if (pVerified->hFile != 0) + supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedVerifyFileInternal: hFile=%p (%s)\n", + (void *)pVerified->hFile, pFile->pszFile); + pVerified->hFile = -1; + pVerified->fValidated = false; + + /* + * Verify the directory then proceed to open it. + * (This'll make sure the directory is opened and that we can (later) + * use openat if we wish.) + */ + int rc = supR3HardenedVerifyDir(pFile->enmDir, fFatal); + if (RT_SUCCESS(rc)) + { + char szPath[RTPATH_MAX]; + int rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true, fFatal); + if (RT_SUCCESS(rc)) + { +#if defined(RT_OS_WINDOWS) + HANDLE hFile = CreateFile(szPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + /** @todo Check the type, and verify the signature (separate function so we can skip it). */ + { + /* it's valid. */ + if (fLeaveFileOpen) + pVerified->hFile = (intptr_t)hFile; + else + CloseHandle(hFile); + pVerified->fValidated = true; + } + } + else + { + int err = GetLastError(); + if (!pFile->fOptional || err != ERROR_FILE_NOT_FOUND) + rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal, + "supR3HardenedVerifyFileInternal: Failed to open \"%s\": err=%d\n", + szPath, err); + } +#else /* UNIXY */ + int fd = open(szPath, O_RDONLY, 0); + if (fd >= 0) + { + /* + * On unixy systems we'll make sure the directory is owned by root + * and not writable by the group and user. + */ + struct stat st; + if (!fstat(fd, &st)) + { + if ( st.st_uid == 0 + && !(st.st_mode & (S_IWGRP | S_IWOTH)) + && S_ISREG(st.st_mode)) + { + /* it's valid. */ + if (fLeaveFileOpen) + pVerified->hFile = fd; + else + close(fd); + pVerified->fValidated = true; + } + else + { + if (!S_ISREG(st.st_mode)) + rc = supR3HardenedError(VERR_IS_A_DIRECTORY, fFatal, + "supR3HardenedVerifyFileInternal: \"%s\" is not a regular file\n", + szPath, (long)st.st_uid); + else if (st.st_uid) + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": not owned by root (st_uid=%ld)\n", + szPath, (long)st.st_uid); + else + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": group and/or other writable (st_mode=0%lo)\n", + szPath, (long)st.st_mode); + close(fd); + } + } + else + { + int err = errno; + rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal, + "supR3HardenedVerifyFileInternal: Failed to fstat \"%s\": %s (%d)\n", + szPath, strerror(err), err); + close(fd); + } + } + else + { + int err = errno; + if (!pFile->fOptional || err != ENOENT) + rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal, + "supR3HardenedVerifyFileInternal: Failed to open \"%s\": %s (%d)\n", + szPath, strerror(err), err); + } +#endif /* UNIXY */ + } + } + + return rc; +} + + +/** + * Verifies that the specified table entry matches the given filename. + * + * @returns VINF_SUCCESS if matching. On mismatch fFatal indicates whether an + * error is returned or we terminate the application. + * + * @param iFile The file table index. + * @param pszFilename The filename. + * @param fFatal Whether validation failures should be treated as + * fatal (true) or not (false). + */ +static int supR3HardenedVerifySameFile(int iFile, const char *pszFilename, bool fFatal) +{ + PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile]; + + /* + * Construct the full path for the file table entry + * and compare it with the specified file. + */ + char szName[RTPATH_MAX]; + int rc = supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal); + if (RT_FAILURE(rc)) + return rc; +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + if (stricmp(szName, pszFilename)) +#else + if (strcmp(szName, pszFilename)) +#endif + { + /* + * Normalize the two paths and compare again. + */ + rc = VERR_NOT_SAME_DEVICE; +#if defined(RT_OS_WINDOWS) + LPSTR pszIgnored; + char szName2[RTPATH_MAX]; + if ( GetFullPathName(szName, RT_ELEMENTS(szName2), &szName2[0], &pszIgnored) + && GetFullPathName(pszFilename, RT_ELEMENTS(szName), &szName[0], &pszIgnored)) + if (!stricmp(szName2, szName)) + rc = VINF_SUCCESS; +#else + AssertCompile(RTPATH_MAX >= PATH_MAX); + char szName2[RTPATH_MAX]; + if ( realpath(szName, szName2) != NULL + && realpath(pszFilename, szName) != NULL) + if (!strcmp(szName2, szName)) + rc = VINF_SUCCESS; +#endif + + if (RT_FAILURE(rc)) + { + supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal); + return supR3HardenedError(rc, fFatal, + "supR3HardenedVerifySameFile: \"%s\" isn't the same as \"%s\"\n", + pszFilename, szName); + } + } + + /* + * Check more stuff like the stat info if it's an already open file? + */ + + + + return VINF_SUCCESS; +} + + +/** + * Verifies a file. + * + * @returns VINF_SUCCESS on success. + * VERR_NOT_FOUND if the file isn't in the table, this isn't ever a fatal error. + * On verfication failure, an error code will be returned when fFatal is clear, + * otherwise the program will be termindated. + * + * @param pszFilename The filename. + * @param fFatal Whether validation failures should be treated as + * fatal (true) or not (false). + */ +DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, bool fFatal) +{ + /* + * Lookup the file and check if it's the same file. + */ + const char *pszName = supR3HardenedPathFilename(pszFilename); + for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++) + if (!strcmp(pszName, g_aSupInstallFiles[iFile].pszFile)) + { + int rc = supR3HardenedVerifySameFile(iFile, pszFilename, fFatal); + if (RT_SUCCESS(rc)) + rc = supR3HardenedVerifyFileInternal(iFile, fFatal, false /* fLeaveFileOpen */); + return rc; + } + + return VERR_NOT_FOUND; +} + + +/** + * Verifies a program, worker for supR3HardenedVerifyAll. + * + * @returns See supR3HardenedVerifyAll. + * @param pszProgName See supR3HardenedVerifyAll. + * @param fFatal See supR3HardenedVerifyAll. + */ +static int supR3HardenedVerifyProgram(const char *pszProgName, bool fFatal) +{ + /* + * Search the table looking for the executable and the DLL/DYLIB/SO. + */ + int rc = VINF_SUCCESS; + bool fExe = false; + bool fDll = false; + size_t const cchProgName = strlen(pszProgName); + for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++) + if (!strncmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgName)) + { + if ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Dll + && !strcmp(&g_aSupInstallFiles[iFile].pszFile[cchProgName], SUPLIB_DLL_SUFF)) + { + /* This only has to be found (once). */ + if (fDll) + rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedVerifyProgram: duplicate DLL entry for \"%s\"\n", pszProgName); + fDll = true; + } + else if ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Exe + && !strcmp(&g_aSupInstallFiles[iFile].pszFile[cchProgName], SUPLIB_EXE_SUFF)) + { + /* Here we'll have to check that the specific program is the same as the entry. */ + if (fExe) + rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal, + "supR3HardenedVerifyProgram: duplicate EXE entry for \"%s\"\n", pszProgName); + fExe = true; + + char szFilename[RTPATH_MAX]; + int rc2 = supR3HardenedPathProgram(szFilename, sizeof(szFilename) - cchProgName - sizeof(SUPLIB_EXE_SUFF)); + if (RT_SUCCESS(rc2)) + { + strcat(szFilename, "/"); + strcat(szFilename, g_aSupInstallFiles[iFile].pszFile); + supR3HardenedVerifySameFile(iFile, szFilename, fFatal); + } + else + rc = supR3HardenedError(rc2, fFatal, + "supR3HardenedVerifyProgram: failed to query program path: rc=%d\n", rc2); + } + } + + /* + * Check the findings. + */ + if (!fDll && !fExe) + rc = supR3HardenedError(VERR_NOT_FOUND, fFatal, + "supR3HardenedVerifyProgram: Couldn't find the program \"%s\"\n", pszProgName); + else if (!fExe) + rc = supR3HardenedError(VERR_NOT_FOUND, fFatal, + "supR3HardenedVerifyProgram: Couldn't find the EXE entry for \"%s\"\n", pszProgName); + else if (!fDll) + rc = supR3HardenedError(VERR_NOT_FOUND, fFatal, + "supR3HardenedVerifyProgram: Couldn't find the DLL entry for \"%s\"\n", pszProgName); + return rc; +} + + + +/** + * Verifies all the known files. + * + * @returns VINF_SUCCESS on success. + * On verfication failure, an error code will be returned when fFatal is clear, + * otherwise the program will be termindated. + * + * @param fFatal Whether validation failures should be treated as + * fatal (true) or not (false). + * @param fLeaveFilesOpen If set, all the verfied files are left open. + * @param pszProgName Optional program name. This is used by SUPR3HardenedMain + * to verify that both the executable and corresponding + * DLL/DYLIB/SO are valid. + */ +DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, bool fLeaveFilesOpen, const char *pszProgName) +{ + /* + * The verify all the files. + */ + int rc = VINF_SUCCESS; + for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++) + { + int rc2 = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveFilesOpen); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc = rc2; + } + + /* + * Verify the program name if specified, that is to say, just check that + * it's in the table (=> we've already verified it). + */ + if (pszProgName) + { + int rc2 = supR3HardenedVerifyProgram(pszProgName, fFatal); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc2 = rc; + } + + return rc; +} + + +/** + * Gets the pre-init data for the hand-over to the other version + * of this code. + * + * The reason why we pass this information on is that it contains + * open directories and files. Later it may include even more info + * (int the verified arrays mostly). + * + * The receiver is supR3HardenedRecvPreInitData. + * + * @param pPreInitData Where to store it. + */ +DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData) +{ + pPreInitData->cInstallFiles = RT_ELEMENTS(g_aSupInstallFiles); + pPreInitData->paInstallFiles = &g_aSupInstallFiles[0]; + pPreInitData->paVerifiedFiles = &g_aSupVerifiedFiles[0]; + + pPreInitData->cVerifiedDirs = RT_ELEMENTS(g_aSupVerifiedDirs); + pPreInitData->paVerifiedDirs = &g_aSupVerifiedDirs[0]; +} + + +/** + * Receives the pre-init data from the static executable stub. + * + * @returns VBox status code. Will not bitch on failure since the + * runtime isn't ready for it, so that is left to the exe stub. + * + * @param pPreInitData The hand-over data. + */ +DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData) +{ + /* + * Compare the array lengths and the contents of g_aSupInstallFiles. + */ + if ( pPreInitData->cInstallFiles != RT_ELEMENTS(g_aSupInstallFiles) + || pPreInitData->cVerifiedDirs != RT_ELEMENTS(g_aSupVerifiedDirs)) + return VERR_VERSION_MISMATCH; + SUPINSTFILE const *paInstallFiles = pPreInitData->paInstallFiles; + for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++) + if ( g_aSupInstallFiles[iFile].enmDir != paInstallFiles[iFile].enmDir + || g_aSupInstallFiles[iFile].enmType != paInstallFiles[iFile].enmType + || g_aSupInstallFiles[iFile].fOptional != paInstallFiles[iFile].fOptional + || strcmp(g_aSupInstallFiles[iFile].pszFile, paInstallFiles[iFile].pszFile)) + return VERR_VERSION_MISMATCH; + + /* + * Check that we're not called out of order. + * If dynamic linking it screwed up, we may end up here. + */ + if ( ASMMemIsAll8(&g_aSupVerifiedFiles[0], sizeof(g_aSupVerifiedFiles), 0) != NULL + || ASMMemIsAll8(&g_aSupVerifiedDirs[0], sizeof(g_aSupVerifiedDirs), 0) != NULL) + return VERR_WRONG_ORDER; + + /* + * Copy the verification data over. + */ + memcpy(&g_aSupVerifiedFiles[0], pPreInitData->paVerifiedFiles, sizeof(g_aSupVerifiedFiles)); + memcpy(&g_aSupVerifiedDirs[0], pPreInitData->paVerifiedDirs, sizeof(g_aSupVerifiedDirs)); + return VINF_SUCCESS; +} diff --git a/src/VBox/HostDrivers/Support/SUPSvc.cpp b/src/VBox/HostDrivers/Support/SUPSvc.cpp new file mode 100644 index 000000000..acf7ff293 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPSvc.cpp @@ -0,0 +1,450 @@ +/* $Id: SUPSvc.cpp 11725 2008-08-27 22:21:47Z vboxsync $ */ +/** @file + * VirtualBox Support Service - Common Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#include <VBox/log.h> +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/stream.h> +#include <iprt/getopt.h> + +#include "SUPSvcInternal.h" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Service state. + */ +typedef enum SUPSVCSERVICESTATE +{ + kSupSvcServiceState_Invalid = 0, + kSupSvcServiceState_NotCreated, + kSupSvcServiceState_Paused, + kSupSvcServiceState_Running, + kSupSvcServiceState_End +} SUPSVCSERVICESTATE; + + +/** + * Service descriptor. + */ +typedef struct SUPSVCSERVICE +{ + /** The service name. */ + const char *pszName; + /** The service state. */ + SUPSVCSERVICESTATE enmState; + /** The instance handle returned by pfnCreate. */ + void *pvInstance; + + /** + * Create the service (don't start it). + * + * @returns VBox status code, log entry is written on failure. + * @param ppvInstance Where to store the instance handle. + */ + DECLCALLBACKMEMBER(int, pfnCreate)(void **ppvInstance); + + /** + * Start the service. + * + * @returns VBox status code, log entry is written on failure. + * @param pvInstance The instance handle. + */ + DECLCALLBACKMEMBER(void, pfnStart)(void *pvInstance); + + /** + * Attempt to stop a running service. + * + * This should fail if there are active clients. A stopped service + * can be restarted by calling pfnStart. + * + * @returns VBox status code, log entry is written on failure. + * @param pvInstance The instance handle. + */ + DECLCALLBACKMEMBER(int, pfnTryStop)(void *pvInstance); + + /** + * Destroy the service, stopping first it if necessary. + * + * @param pvInstance The instance handle. + * @param fRunning Whether the service is running or not. + */ + DECLCALLBACKMEMBER(void, pfnStopAndDestroy)(void *pvInstance, bool fRunning); +} SUPSVCSERVICE; +/** Pointer to a service descriptor. */ +typedef SUPSVCSERVICE *PSUPSVCSERVICE; +/** Pointer to a const service descriptor. */ +typedef SUPSVCSERVICE const *PCSUPSVCSERVICE; + + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +static SUPSVCSERVICE g_aServices[] = +{ + { + "Global", + kSupSvcServiceState_NotCreated, + NULL, + supSvcGlobalCreate, + supSvcGlobalStart, + supSvcGlobalTryStop, + supSvcGlobalStopAndDestroy, + } +#ifdef RT_OS_WINDOWS + , + { + "Grant", + kSupSvcServiceState_NotCreated, + NULL, + supSvcGrantCreate, + supSvcGrantStart, + supSvcGrantTryStop, + supSvcGrantStopAndDestroy, + } +#endif +}; + + + +/** + * Instantiates and starts the services. + * + * @returns VBox status code. Done bitching on failure. + */ +int supSvcCreateAndStartServices(void) +{ + LogFlowFuncEnter(); + + /* + * Validate that all services are in the NotCreated state. + */ + unsigned i; + for (i = 0; i < RT_ELEMENTS(g_aServices); i++) + if (g_aServices[i].enmState != kSupSvcServiceState_NotCreated) + { + supSvcLogError("service %s in state %d, expected state %d (NotCreated)", + g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated); + return VERR_WRONG_ORDER; + } + + /* + * Create all the services, then start them. + */ + int rc = VINF_SUCCESS; + for (i = 0; i < RT_ELEMENTS(g_aServices); i++) + { + void *pvInstance = NULL; + int rc = g_aServices[i].pfnCreate(&pvInstance); + if (RT_FAILURE(rc)) + { + Log(("supSvcCreateAndStartServices: %s -> %Rrc\n", g_aServices[i].pszName, rc)); + break; + } + g_aServices[i].pvInstance = pvInstance; + g_aServices[i].enmState = kSupSvcServiceState_Paused; + } + if (RT_SUCCESS(rc)) + { + for (i = 0; i < RT_ELEMENTS(g_aServices); i++) + { + g_aServices[i].pfnStart(g_aServices[i].pvInstance); + g_aServices[i].enmState = kSupSvcServiceState_Running; + } + } + else + { + /* + * Destroy any services we managed to instantiate. + */ + while (i-- > 0) + { + g_aServices[i].pfnStopAndDestroy(g_aServices[i].pvInstance, false /* fRunning */); + g_aServices[i].pvInstance = NULL; + g_aServices[i].enmState = kSupSvcServiceState_NotCreated; + } + } + + LogFlow(("supSvcCreateAndStartServices: returns %Rrc\n", rc)); + return rc; +} + + +/** + * Checks if it's possible to stop the services. + * + * @returns VBox status code, done bitching on failure. + */ +int supSvcTryStopServices(void) +{ + LogFlowFuncEnter(); + + /* + * Check that the services are all created and count the running ones. + */ + unsigned i; + unsigned cRunning = 0; + for (i = 0; i < RT_ELEMENTS(g_aServices); i++) + if (g_aServices[i].enmState == kSupSvcServiceState_Running) + cRunning++; + else if (g_aServices[i].enmState == kSupSvcServiceState_NotCreated) + { + supSvcLogError("service %s in state %d (NotCreated), expected pause or running", + g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated); + return VERR_WRONG_ORDER; + } + if (!cRunning) + return VINF_SUCCESS; /* all stopped, nothing to do. */ + Assert(cRunning == RT_ELEMENTS(g_aServices)); /* all or nothing */ + + /* + * Try stop them in reverse of start order. + */ + int rc = VINF_SUCCESS; + i = RT_ELEMENTS(g_aServices); + while (i-- > 0) + { + rc = g_aServices[i].pfnTryStop(g_aServices[i].pvInstance); + if (RT_FAILURE(rc)) + { + Log(("supSvcTryStopServices: %s -> %Rrc\n", g_aServices[i].pszName, rc)); + break; + } + g_aServices[i].enmState = kSupSvcServiceState_Paused; + } + if (RT_FAILURE(rc)) + { + /* Failed, restart the ones we succeeded in stopping. */ + while (++i < RT_ELEMENTS(g_aServices)) + { + g_aServices[i].pfnStart(g_aServices[i].pvInstance); + g_aServices[i].enmState = kSupSvcServiceState_Running; + } + } + LogFlow(("supSvcTryStopServices: returns %Rrc\n", rc)); + return rc; +} + + +/** + * Stops and destroys the services. + */ +void supSvcStopAndDestroyServices(void) +{ + LogFlowFuncEnter(); + + /* + * Stop and destroy the service in reverse of start order. + */ + unsigned i = RT_ELEMENTS(g_aServices); + while (i-- > 0) + if (g_aServices[i].enmState != kSupSvcServiceState_NotCreated) + { + g_aServices[i].pfnStopAndDestroy(g_aServices[i].pvInstance, + g_aServices[i].enmState == kSupSvcServiceState_Running); + g_aServices[i].pvInstance = NULL; + g_aServices[i].enmState = kSupSvcServiceState_NotCreated; + } + + LogFlowFuncLeave(); +} + + + +/** + * Logs the message to the appropirate system log. + * + * In debug builds this will also put it in the debug log. + * + * @param pszMsg The log string. + * + * @remarks This may later be replaced by the release logger and callback destination(s). + */ +void supSvcLogErrorStr(const char *pszMsg) +{ + supSvcOsLogErrorStr(pszMsg); + LogRel(("%s\n", pszMsg)); +} + + +/** + * Logs the message to the appropirate system log. + * + * In debug builds this will also put it in the debug log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + * + * @todo This should later be replaced by the release logger and callback destination(s). + */ +void supSvcLogErrorV(const char *pszFormat, va_list va) +{ + if (*pszFormat) + { + char *pszMsg = NULL; + if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) + { + supSvcLogErrorStr(pszMsg); + RTStrFree(pszMsg); + } + else + supSvcLogErrorStr(pszFormat); + } +} + + +/** + * Logs the error message to the appropirate system log. + * + * In debug builds this will also put it in the debug log. + * + * @param pszFormat The log string. No trailing newline. + * @param ... Format arguments. + * + * @todo This should later be replaced by the release logger and callback destination(s). + */ +void supSvcLogError(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supSvcLogErrorV(pszFormat, va); + va_end(va); +} + + +/** + * Deals with RTGetOpt failure, bitching in the system log. + * + * @returns 1 + * @param pszAction The action name. + * @param rc The RTGetOpt return value. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + * @param pValue The value returned by RTGetOpt. + */ +int supSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue) +{ + supSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s", + pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); + return 1; +} + + +/** + * Bitch about too many arguments (after RTGetOpt stops) in the system log. + * + * @returns 1 + * @param pszAction The action name. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + */ +int supSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) +{ + Assert(iArg < argc); + supSvcLogError("%s - Too many arguments: %s", pszAction, argv[iArg]); + for ( ; iArg < argc; iArg++) + LogRel(("arg#%i: %s\n", iArg, argv[iArg])); + return 1; +} + + +/** + * Prints an error message to the screen. + * + * @param pszFormat The message format string. + * @param va Format arguments. + */ +void supSvcDisplayErrorV(const char *pszFormat, va_list va) +{ + RTStrmPrintf(g_pStdErr, "VBoxSupSvc error: "); + RTStrmPrintfV(g_pStdErr, pszFormat, va); + Log(("supSvcDisplayErrorV: %s", pszFormat)); /** @todo format it! */ +} + + +/** + * Prints an error message to the screen. + * + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +void supSvcDisplayError(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + supSvcDisplayErrorV(pszFormat, va); + va_end(va); +} + + +/** + * Deals with RTGetOpt failure. + * + * @returns 1 + * @param pszAction The action name. + * @param rc The RTGetOpt return value. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + * @param pValue The value returned by RTGetOpt. + */ +int supSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue) +{ + supSvcDisplayError("%s - RTGetOpt failure, %Rrc (%d): %s\n", + pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); + return 1; +} + + +/** + * Bitch about too many arguments (after RTGetOpt stops). + * + * @returns 1 + * @param pszAction The action name. + * @param argc The argument count. + * @param argv The argument vector. + * @param iArg The argument index. + */ +int supSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) +{ + Assert(iArg < argc); + supSvcDisplayError("%s - Too many arguments: %s\n", pszAction, argv[iArg]); + return 1; +} + diff --git a/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp b/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp new file mode 100644 index 000000000..585a29377 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPSvcGlobal.cpp @@ -0,0 +1,72 @@ +/* $Id: SUPSvcGlobal.cpp 11725 2008-08-27 22:21:47Z vboxsync $ */ +/** @file + * VirtualBox Support Service - The Global Service. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "SUPSvcInternal.h" + +#include <iprt/err.h> +#include <iprt/assert.h> + + +/** @copydoc SUPSVCSERVICE::pfnCreate */ +DECLCALLBACK(int) supSvcGlobalCreate(void **ppvInstance) +{ + *ppvInstance = (void *)1; + return VINF_SUCCESS; +} + + +/** @copydoc SUPSVCSERVICE::pfnStart */ +DECLCALLBACK(void) supSvcGlobalStart(void *pvInstance) +{ + Assert(pvInstance == (void *)1); + NOREF(pvInstance); +} + + +/** @copydoc SUPSVCSERVICE::pfnTryStop */ +DECLCALLBACK(int) supSvcGlobalTryStop(void *pvInstance) +{ + Assert(pvInstance == (void *)1); + NOREF(pvInstance); + return VINF_SUCCESS; +} + + +/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */ +DECLCALLBACK(void) supSvcGlobalStopAndDestroy(void *pvInstance, bool fRunning) +{ + Assert(pvInstance == (void *)1); + NOREF(pvInstance); + NOREF(fRunning); +} + diff --git a/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp b/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp new file mode 100644 index 000000000..ed05d64d8 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPSvcGrant.cpp @@ -0,0 +1,1005 @@ +/* $Id: SUPSvcGrant.cpp 11725 2008-08-27 22:21:47Z vboxsync $ */ +/** @file + * VirtualBox Support Service - The Grant Service. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#include "SUPSvcInternal.h" + +#include <VBox/log.h> +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/mem.h> +#include <iprt/semaphore.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include <iprt/localipc.h> + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** Pointer to a client instance. */ +typedef struct SUPSVCGRANTSESSION *PSUPSVCGRANTSESSION; +/** Pointer to a Grant service instance. */ +typedef struct SUPSVCGRANT *PSUPSVCGRANT; + + +/** + * Grant service session data. + */ +typedef struct SUPSVCGRANTSESSION +{ + /** Pointer to the next client in the list. */ + PSUPSVCGRANTSESSION pNext; + /** Pointer to the previous client in the list. */ + PSUPSVCGRANTSESSION pPrev; + /** Pointer to the parent (the service instance). */ + PSUPSVCGRANT volatile pParent; + /** The local ipc client handle. */ + RTLOCALIPCSESSION volatile hSession; + /** Indicate that the thread should terminate ASAP. */ + bool volatile fTerminate; + /** The thread handle. */ + RTTHREAD hThread; + +} SUPSVCGRANTSESSION; + + +/** + * State grant service machine. + */ +typedef enum SUPSVCGRANTSTATE +{ + /** The invalid zero entry. */ + kSupSvcGrantState_Invalid = 0, + /** Creating - the thread is being started. + * Next: Paused or Butchered. */ + kSupSvcGrantState_Creating, + /** Paused - the thread is blocked on it's user event semaphore. + * Next: Resuming, Terminating or Butchered. + * Prev: Creating, Pausing */ + kSupSvcGrantState_Paused, + /** Resuming - the thread is being unblocked and ushered into RTLocalIpcServiceListen. + * Next: Listen or Butchered. + * Prev: Paused */ + kSupSvcGrantState_Resuming, + /** Listen - the thread is in RTLocalIpcServerListen or setting up an incoming session. + * Next: Pausing or Butchered. + * Prev: Resuming */ + kSupSvcGrantState_Listen, + /** Pausing - Cancelling the listen and dropping any incoming sessions. + * Next: Paused or Butchered. + * Prev: Listen */ + kSupSvcGrantState_Pausing, + /** Butchered - The thread has quit because something when terribly wrong. + * Next: Destroyed + * Prev: Any. */ + kSupSvcGrantState_Butchered, + /** Pausing - Cancelling the listen and dropping any incoming sessions. + * Next: Destroyed + * Prev: Paused */ + kSupSvcGrantState_Terminating, + /** Destroyed - the instance is invalid. + * Prev: Butchered or Terminating */ + kSupSvcGrantState_Destroyed, + /** The end of valid state values. */ + kSupSvcGrantState_End, + /** The usual 32-bit blowup hack. */ + kSupSvcGrantState_32BitHack = 0x7fffffff +} SUPSVCGRANTSTATE; + + +/** + * Grant service instance data. + */ +typedef struct SUPSVCGRANT +{ + /** The local ipc server handle. */ + RTLOCALIPCSERVER hServer; + + /** Critical section serializing access to the session list, the state, + * the response event, the session event, and the thread event. */ + RTCRITSECT CritSect; + /** The service thread will signal this event when it has changed to + * the 'paused' or 'running' state. */ + RTSEMEVENT hResponseEvent; + /** Event that's signaled on session termination. */ + RTSEMEVENT hSessionEvent; + /** The handle to the service thread. */ + RTTHREAD hThread; + /** Head of the session list. */ + PSUPSVCGRANTSESSION volatile pSessionHead; + /** The service state. */ + SUPSVCGRANTSTATE volatile enmState; + + /** Critical section serializing access to the SUPR3HardenedVerify APIs. */ + RTCRITSECT VerifyCritSect; +} SUPSVCGRANT; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState); + + + + +/** + * Services a client session. + * + * @returns VINF_SUCCESS. + * + * @param hThread The thread handle. + * @param pvSession Pointer to the session instance data. + */ +static DECLCALLBACK(int) supSvcGrantSessionThread(RTTHREAD hThread, void *pvSession) +{ + PSUPSVCGRANTSESSION pThis = (PSUPSVCGRANTSESSION)pvSession; + RTLOCALIPCSESSION hSession = pThis->hSession; + Log(("supSvcGrantSessionThread(%p):\n", pThis)); + + /* + * Process client requests untill it quits or we're cancelled on termination. + */ + while (!ASMAtomicUoReadBool(&pThis->fTerminate)) + { + RTThreadSleep(1000); + /** @todo */ + } + + /* + * Clean up the session. + */ + PSUPSVCGRANT pParent = (PSUPSVCGRANT)ASMAtomicReadPtr((void * volatile *)&pThis->pParent); + if (pParent) + RTCritSectEnter(&pParent->CritSect); + else + Log(("supSvcGrantSessionThread(%p): No parent\n", pThis)); + + ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession); + if (hSession != NIL_RTLOCALIPCSESSION) + RTLocalIpcSessionClose(hSession); + else + Log(("supSvcGrantSessionThread(%p): No session handle\n", pThis)); + + if (pParent) + { + RTSemEventSignal(pParent->hSessionEvent); + RTCritSectLeave(&pParent->CritSect); + } + Log(("supSvcGrantSessionThread(%p): exits\n")); + return VINF_SUCCESS; +} + + +/** + * Cleans up a session. + * + * This is called while inside the grant service critical section. + * + * @param pThis The session to destroy. + * @param pParent The parent. + */ +static void supSvcGrantSessionDestroy(PSUPSVCGRANTSESSION pThis, PSUPSVCGRANT pParent) +{ + /* + * Unlink it. + */ + if (pThis->pNext) + { + Assert(pThis->pNext->pPrev == pThis); + pThis->pNext->pPrev = pThis->pPrev; + } + + if (pThis->pPrev) + { + Assert(pThis->pPrev->pNext == pThis); + pThis->pPrev->pNext = pThis->pNext; + } + else if (pParent->pSessionHead == pThis) + pParent->pSessionHead = pThis->pNext; + + /* + * Free the resources associated with it. + */ + pThis->hThread = NIL_RTTHREAD; + pThis->pNext = NULL; + pThis->pPrev = NULL; + + RTLOCALIPCSESSION hSession; + ASMAtomicXchgHandle(&pThis->hSession, NIL_RTLOCALIPCSESSION, &hSession); + if (hSession != NIL_RTLOCALIPCSESSION) + RTLocalIpcSessionClose(hSession); + + RTMemFree(pThis); +} + + +/** + * Cleans up zombie sessions, locked. + * + * @param pThis Pointer to the grant service instance data. + */ +static void supSvcGrantCleanUpSessionsLocked(PSUPSVCGRANT pThis) +{ + /* + * Iterate untill be make it all the way thru the list. + * + * Only use the thread state as and indicator on whether we can destroy + * the session or not. + */ + PSUPSVCGRANTSESSION pCur; + do + { + for (pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + { + int rc = RTThreadWait(pCur->hThread, 0, NULL); + if (RT_SUCCESS(rc)) + { + supSvcGrantSessionDestroy(pCur, pThis); + break; + } + + Assert(rc == VERR_TIMEOUT); + Assert(pCur->hThread != NIL_RTTHREAD); + Assert(pCur->pNext != pThis->pSessionHead); + } + } while (pCur); +} + + +/** + * Cleans up zombie sessions. + * + * @returns VINF_SUCCESS, VBox error code on internal error. + * + * @param pThis Pointer to the grant service instance data. + * @param fOwnCritSect Whether we own the crit sect already. The state is preserved. + */ +static int supSvcGrantCleanUpSessions(PSUPSVCGRANT pThis, bool fOwnCritSect) +{ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + supSvcLogError("supSvcGrantCleanUpSessions: RTCritSectEnter returns %Rrc", rc); + return rc; + } + + supSvcGrantCleanUpSessionsLocked(pThis); + + RTCritSectLeave(&pThis->CritSect); + return VINF_SUCCESS; +} + + +/** + * Gets the state name. + * + * @returns The state name string (read only). + * @param enmState The state. + */ +static const char *supSvcGrantStateName(SUPSVCGRANTSTATE enmState) +{ + switch (enmState) + { + case kSupSvcGrantState_Invalid: return "Invalid"; + case kSupSvcGrantState_Creating: return "Creating"; + case kSupSvcGrantState_Paused: return "Paused"; + case kSupSvcGrantState_Resuming: return "Resuming"; + case kSupSvcGrantState_Listen: return "Listen"; + case kSupSvcGrantState_Pausing: return "Pausing"; + case kSupSvcGrantState_Butchered: return "Butchered"; + case kSupSvcGrantState_Terminating: return "Terminating"; + case kSupSvcGrantState_Destroyed: return "Destroyed"; + default: return "?Unknown?"; + } +} + + +/** + * Attempts to flip into the butchered state. + * + * @returns rc. + * @param pThis The instance data. + * @param fOwnCritSect Whether we own the crit sect already. + * @param pszFailed What failed. + * @param rc What to return (lazy bird). + */ +static int supSvcGrantThreadButchered(PSUPSVCGRANT pThis, bool fOwnCritSect, const char *pszFailed, int rc) +{ + int rc2 = VINF_SUCCESS; + if (!fOwnCritSect) + rc2 = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc2)) + { + supSvcLogError("supSvcGrantThread(%s): Butchered; %Rrc: %s", + supSvcGrantStateName(pThis->enmState), rc, pszFailed); + pThis->enmState = kSupSvcGrantState_Butchered; + + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +/** + * Creates a new session. + * + * @returns VINF_SUCCESS on success, VBox error code on internal error. + * + * @param pThis Pointer to the grant service instance data. + * @param hSession The client session handle. + */ +static int supSvcGrantThreadCreateSession(PSUPSVCGRANT pThis, RTLOCALIPCSESSION hSession) +{ + /* + * Allocate and initialize a new session instance before entering the critsect. + */ + PSUPSVCGRANTSESSION pSession = (PSUPSVCGRANTSESSION)RTMemAlloc(sizeof(*pSession)); + if (!pSession) + { + supSvcLogError("supSvcGrantThreadListen: failed to allocate session"); + return VINF_SUCCESS; /* not fatal? */ + } + pSession->pPrev = NULL; + pSession->pNext = NULL; + pSession->pParent = pThis; + pSession->hSession = hSession; + pSession->fTerminate = false; + pSession->hThread = NIL_RTTHREAD; + + /* + * Enter the critsect, check the state, link it and fire off the session thread. + */ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* check the state */ + SUPSVCGRANTSTATE enmState = pThis->enmState; + if (enmState == kSupSvcGrantState_Listen) + { + /* link it */ + pSession->pNext = pThis->pSessionHead; + if (pThis->pSessionHead) + pThis->pSessionHead->pPrev = pSession; + pThis->pSessionHead = pSession; + + /* fire up the thread */ + Log(("supSvcGrantThreadListen: starting session %p\n", pSession)); + rc = RTThreadCreate(&pSession->hThread, supSvcGrantSessionThread, pSession, 0, + RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "SESSION"); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectLeave(&pThis->CritSect); + if (RT_FAILURE(rc)) + return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectLeave", rc); + + /* + * Successfully handled the client. + */ + return VINF_SUCCESS; + } + + /* bail out */ + supSvcLogError("supSvcGrantThreadListen: RTThreadCreate returns %Rrc", rc); + } + else + Log(("supSvcGrantThreadListen: dropping connection, state %s\n", supSvcGrantStateName(enmState))); + + RTCritSectLeave(&pThis->CritSect); + rc = VINF_SUCCESS; + } + else + supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTCritSectEnter", rc); + RTLocalIpcSessionClose(hSession); + RTMemFree(pSession); + return rc; +} + + +/** + * Listen for a client session and kicks off the service thread for it. + * + * @returns VINF_SUCCESS on normal state change, failure if something gets screwed up. + * + * @param pThis Pointer to the grant service instance data. + */ +static int supSvcGrantThreadListen(PSUPSVCGRANT pThis) +{ + /* + * Wait for a client to connect and create a new session. + */ + RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION; + int rc = RTLocalIpcServerListen(pThis->hServer, &hClientSession); + if (RT_FAILURE(rc)) + { + if (rc == VERR_CANCELLED) + LogFlow(("supSvcGrantThreadListen: cancelled\n")); + else if (rc == VERR_TRY_AGAIN) + /* for testing */; + else + return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "RTLocalIpcServerListen", rc); + return VINF_SUCCESS; + } + + return supSvcGrantThreadCreateSession(pThis, hClientSession); +} + + +/** + * Grant service thread. + * + * This thread is the one listening for clients and kicks off + * the session threads and stuff. + * + * @returns VINF_SUCCESS on normal exit, VBox error status on failure. + * @param hThread The thread handle. + * @param pvThis Pointer to the grant service instance data. + */ +static DECLCALLBACK(int) supSvcGrantThread(RTTHREAD hThread, void *pvThis) +{ + PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvThis; + + /* + * The state loop. + */ + for (;;) + { + /* + * Switch on the current state (requires critsect). + */ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + supSvcLogError("supSvcGrantThread - RTCritSectEnter returns %Rrc", rc); + return rc; + } + + SUPSVCGRANTSTATE enmState = pThis->enmState; + LogFlow(("supSvcGrantThread: switching %s\n", supSvcGrantStateName(enmState))); + switch (enmState) + { + case kSupSvcGrantState_Creating: + case kSupSvcGrantState_Pausing: + pThis->enmState = kSupSvcGrantState_Paused; + rc = RTSemEventSignal(pThis->hResponseEvent); + if (RT_FAILURE(rc)) + return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc); + /* fall thru */ + + case kSupSvcGrantState_Paused: + RTCritSectLeave(&pThis->CritSect); + + rc = RTThreadUserWait(hThread, 60*1000); /* wake up once in a while (paranoia) */ + if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) + return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect*/, "RTThreadUserWait", rc); + break; + + case kSupSvcGrantState_Resuming: + pThis->enmState = kSupSvcGrantState_Listen; + rc = RTSemEventSignal(pThis->hResponseEvent); + if (RT_FAILURE(rc)) + return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "RTSemEventSignal", rc); + /* fall thru */ + + case kSupSvcGrantState_Listen: + RTCritSectLeave(&pThis->CritSect); + rc = supSvcGrantThreadListen(pThis); + if (RT_FAILURE(rc)) + { + Log(("supSvcGrantThread: supSvcGrantDoListening returns %Rrc, exiting\n", rc)); + return rc; + } + break; + + case kSupSvcGrantState_Terminating: + RTCritSectLeave(&pThis->CritSect); + Log(("supSvcGrantThread: Done\n")); + return VINF_SUCCESS; + + case kSupSvcGrantState_Butchered: + default: + return supSvcGrantThreadButchered(pThis, true /* fOwnCritSect*/, "Bad state", VERR_INTERNAL_ERROR); + } + + /* + * Massage the session list between clients and states. + */ + rc = supSvcGrantCleanUpSessions(pThis, false /* fOwnCritSect */); + if (RT_FAILURE(rc)) + return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "supSvcGrantCleanUpSessions", rc); + } +} + + +/** + * Waits for the service thread to respond to a state change. + * + * @returns VINF_SUCCESS on success, VERR_TIMEOUT if it doesn't respond in time, other error code on internal error. + * + * @param pThis Pointer to the grant service instance data. + * @param enmCurState The current state. + * @param enmNewState The new state we're waiting for it to enter. + */ +static int supSvcGrantWait(PSUPSVCGRANT pThis, SUPSVCGRANTSTATE enmCurState, SUPSVCGRANTSTATE enmNewState) +{ + LogFlow(("supSvcGrantWait(,%s,%s): enter\n", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState))); + + /* + * Wait a short while for the response event to be set. + */ + RTSemEventWait(pThis->hResponseEvent, 1000); + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + if (pThis->enmState == enmNewState) + { + RTCritSectLeave(&pThis->CritSect); + rc = VINF_SUCCESS; + } + else if (pThis->enmState == enmCurState) + { + /* + * Wait good while longer. + */ + RTCritSectLeave(&pThis->CritSect); + rc = RTSemEventWait(pThis->hResponseEvent, 59*1000); /* 59 sec */ + if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT) + { + rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* + * Check the state whether we've succeeded. + */ + SUPSVCGRANTSTATE enmState = pThis->enmState; + if (enmState == enmNewState) + rc = VINF_SUCCESS; + else if (enmState == enmCurState) + { + supSvcLogError("supSvcGrantWait(,%s,%s) - the thread doesn't respond in a timely manner, failing.", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)); + rc = VERR_TIMEOUT; + } + else + { + supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState), + supSvcGrantStateName(enmNewState), supSvcGrantStateName(enmState)); + AssertMsgFailed(("%s\n", supSvcGrantStateName(enmState))); + rc = VERR_INTERNAL_ERROR; + } + + RTCritSectLeave(&pThis->CritSect); + } + else + supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)); + } + else + supSvcLogError("supSvcGrantWait(,%s,%s) - RTSemEventWait returns %Rrc", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)); + } + else + { + supSvcLogError("supSvcGrantWait(,%s,%s) - wrong state %s!", supSvcGrantStateName(enmCurState), + supSvcGrantStateName(enmNewState), supSvcGrantStateName(pThis->enmState)); + AssertMsgFailed(("%s\n", supSvcGrantStateName(pThis->enmState))); + RTCritSectLeave(&pThis->CritSect); + rc = VERR_INTERNAL_ERROR; + } + } + else + supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState)); + + Log(("supSvcGrantWait(,%s,%s): returns %Rrc\n", + supSvcGrantStateName(enmCurState), supSvcGrantStateName(enmNewState), rc)); + return rc; +} + + +/** @copydoc SUPSVCSERVICE::pfnCreate */ +DECLCALLBACK(int) supSvcGrantCreate(void **ppvInstance) +{ + LogFlowFuncEnter(); + + /* + * Allocate and initialize the session data. + */ + PSUPSVCGRANT pThis = (PSUPSVCGRANT)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + { + supSvcLogError("supSvcGrantCreate - no memory"); + return VERR_NO_MEMORY; + } + bool fFreeIt = true; + pThis->pSessionHead = NULL; + pThis->enmState = kSupSvcGrantState_Creating; + int rc = RTCritSectInit(&pThis->VerifyCritSect); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pThis->hResponseEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pThis->hSessionEvent); + if (RT_SUCCESS(rc)) + { + /* + * Create the local IPC instance and then finally fire up the thread. + */ + rc = RTLocalIpcServerCreate(&pThis->hServer, SUPSVC_GRANT_SERVICE_NAME, RTLOCALIPC_FLAGS_MULTI_SESSION); + if (RT_SUCCESS(rc)) + { + rc = RTThreadCreate(&pThis->hThread, supSvcGrantThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GRANT"); + if (RT_SUCCESS(rc)) + { + rc = supSvcGrantWait(pThis, kSupSvcGrantState_Creating, kSupSvcGrantState_Paused); + if (RT_SUCCESS(rc)) + { + /* + * Successfully created the grant service! + */ + Log(("supSvcGrantCreate: returns VINF_SUCCESS (pThis=%p)\n", pThis)); + *ppvInstance = pThis; + return VINF_SUCCESS; + } + + /* + * The thread FAILED to start in a timely manner! + */ + RTCritSectEnter(&pThis->CritSect); + pThis->enmState = kSupSvcGrantState_Terminating; + RTCritSectLeave(&pThis->CritSect); + + RTThreadUserSignal(pThis->hThread); + + int cTries = 10; + int rc2 = RTThreadWait(pThis->hThread, 20000, NULL); + if (RT_FAILURE(rc2)) + { + /* poke it a few more times before giving up. */ + while (--cTries > 0) + { + RTThreadUserSignal(pThis->hThread); + RTLocalIpcServerCancel(pThis->hServer); + if (RTThreadWait(pThis->hThread, 1000, NULL) != VERR_TIMEOUT) + break; + } + } + fFreeIt = cTries <= 0; + } + else + supSvcLogError("supSvcGrantCreate - RTThreadCreate returns %Rrc", rc); + RTLocalIpcServerDestroy(pThis->hServer); + pThis->hServer = NIL_RTLOCALIPCSERVER; + } + else + supSvcLogError("supSvcGrantCreate - RTLocalIpcServiceCreate returns %Rrc", rc); + RTSemEventDestroy(pThis->hSessionEvent); + pThis->hSessionEvent = NIL_RTSEMEVENT; + } + else + supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc); + RTSemEventDestroy(pThis->hResponseEvent); + pThis->hResponseEvent = NIL_RTSEMEVENT; + } + else + supSvcLogError("supSvcGrantCreate - RTSemEventCreate returns %Rrc", rc); + RTCritSectDelete(&pThis->CritSect); + } + else + supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc); + RTCritSectDelete(&pThis->VerifyCritSect); + } + else + supSvcLogError("supSvcGrantCreate - RTCritSectInit returns %Rrc", rc); + if (fFreeIt) + RTMemFree(pThis); + Log(("supSvcGrantCreate: returns %Rrc\n", rc)); + return rc; +} + + +/** @copydoc SUPSVCSERVICE::pfnStart */ +DECLCALLBACK(void) supSvcGrantStart(void *pvInstance) +{ + PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance; + + /* + * Change the state and signal the thread. + */ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + bool fInCritSect = true; + SUPSVCGRANTSTATE enmState = pThis->enmState; + if (enmState == kSupSvcGrantState_Paused) + { + pThis->enmState = kSupSvcGrantState_Resuming; + rc = RTThreadUserSignal(pThis->hThread); + if (RT_SUCCESS(rc)) + { + /* + * Wait for the bugger to respond (no need to bitch here). + */ + RTCritSectLeave(&pThis->CritSect); + supSvcGrantWait(pThis, kSupSvcGrantState_Resuming, kSupSvcGrantState_Listen); + fInCritSect = false; + } + } + else + supSvcLogError("supSvcGrantStart - Incorrect state %s!", supSvcGrantStateName(enmState)); + if (fInCritSect) + RTCritSectLeave(&pThis->CritSect); + } + else + { + supSvcLogError("supSvcGrantStart - RTCritSectEnter returns %Rrc!", rc); + AssertRCReturnVoid(rc); + } +} + + +/** @copydoc SUPSVCSERVICE::pfnTryStop */ +DECLCALLBACK(int) supSvcGrantTryStop(void *pvInstance) +{ + PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance; + + /* + * Don't give up immediately. + */ + uint64_t u64StartTS = RTTimeMilliTS(); + int rc; + for (;;) + { + /* + * First check the state to make sure the thing is actually running. + * If the critsect is butchered, just pretend success. + */ + rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + supSvcLogError("supSvcGrantTryStop - RTCritSectEnter returns %Rrc", rc); + AssertRC(rc); + return VINF_SUCCESS; + } + SUPSVCGRANTSTATE enmState = pThis->enmState; + if (enmState != kSupSvcGrantState_Listen) + { + supSvcLogError("supSvcGrantTryStop - Not running, state: %s", supSvcGrantStateName(enmState)); + RTCritSectLeave(&pThis->CritSect); + return VINF_SUCCESS; + } + + /* + * If there are no clients, usher the thread into the paused state. + */ + supSvcGrantCleanUpSessionsLocked(pThis); + if (!pThis->pSessionHead) + { + rc = RTThreadUserReset(pThis->hThread); + pThis->enmState = kSupSvcGrantState_Pausing; + int rc2 = RTLocalIpcServerCancel(pThis->hServer); + int rc3 = RTCritSectLeave(&pThis->CritSect); + if (RT_SUCCESS(rc) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3)) + supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused); + else + { + if (RT_FAILURE(rc)) + supSvcLogError("supSvcGrantTryStop - RTThreadUserReset returns %Rrc", rc); + if (RT_FAILURE(rc2)) + supSvcLogError("supSvcGrantTryStop - RTLocalIpcServerCancel returns %Rrc", rc); + if (RT_FAILURE(rc3)) + supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc); + } + return VINF_SUCCESS; + } + + /* + * Check the time limit, otherwise wait for a client event. + */ + uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS; + if (u64Elapsed >= 60*1000) /* 1 min */ + { + unsigned cSessions = 0; + for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + cSessions++; + RTCritSectLeave(&pThis->CritSect); + + supSvcLogError("supSvcGrantTryStop - %u active sessions after waiting %u ms", cSessions, (unsigned)u64Elapsed); + return VERR_TRY_AGAIN; + } + + rc = RTCritSectLeave(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + supSvcLogError("supSvcGrantTryStop - RTCritSectLeave returns %Rrc", rc); + return VINF_SUCCESS; + } + + rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed); + if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) + { + supSvcLogError("supSvcGrantTryStop - RTSemEventWait returns %Rrc", rc); + return VINF_SUCCESS; + } + } +} + + +/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */ +DECLCALLBACK(void) supSvcGrantStopAndDestroy(void *pvInstance, bool fRunning) +{ + PSUPSVCGRANT pThis = (PSUPSVCGRANT)pvInstance; + int rc; + + /* + * Attempt to stop the service, cancelling blocked server and client calls. + */ + RTCritSectEnter(&pThis->CritSect); + + SUPSVCGRANTSTATE enmState = pThis->enmState; + AssertMsg(fRunning == (pThis->enmState == kSupSvcGrantState_Listen), + ("%RTbool %s\n", fRunning, supSvcGrantStateName(enmState))); + + if (enmState == kSupSvcGrantState_Listen) + { + RTThreadUserReset(pThis->hThread); + pThis->enmState = kSupSvcGrantState_Paused; + for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + ASMAtomicWriteBool(&pCur->fTerminate, true); + + /* try cancel local ipc operations that might be pending */ + RTLocalIpcServerCancel(pThis->hServer); + for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + { + RTLOCALIPCSESSION hSession; + ASMAtomicReadHandle(&pCur->hSession, &hSession); + if (hSession != NIL_RTLOCALIPCSESSION) + RTLocalIpcSessionCancel(hSession); + } + + /* + * Wait for the thread to respond (outside the crit sect). + */ + RTCritSectLeave(&pThis->CritSect); + supSvcGrantWait(pThis, kSupSvcGrantState_Pausing, kSupSvcGrantState_Paused); + RTCritSectEnter(&pThis->CritSect); + + /* + * Wait for any lingering sessions to exit. + */ + supSvcGrantCleanUpSessionsLocked(pThis); + if (pThis->pSessionHead) + { + uint64_t u64StartTS = RTTimeMilliTS(); + do + { + /* Destroy the sessions since cancelling didn't do the trick. */ + for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + { + RTLOCALIPCSESSION hSession; + ASMAtomicXchgHandle(&pCur->hSession, NIL_RTLOCALIPCSESSION, &hSession); + if (hSession != NIL_RTLOCALIPCSESSION) + { + rc = RTLocalIpcSessionClose(hSession); + AssertRC(rc); + if (RT_FAILURE(rc)) + supSvcLogError("supSvcGrantStopAndDestroy: RTLocalIpcSessionClose(%p) returns %Rrc", + (uintptr_t)hSession, rc); + } + } + + /* Check the time. */ + uint64_t u64Elapsed = RTTimeMilliTS() - u64StartTS; + if (u64Elapsed >= 60*1000) /* 1 min */ + break; + + /* wait */ + RTCritSectLeave(&pThis->CritSect); + rc = RTSemEventWait(pThis->hSessionEvent, 60*1000 - u64Elapsed); + RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) + break; + + /* cleanup and check again */ + supSvcGrantCleanUpSessionsLocked(pThis); + } while (pThis->pSessionHead); + } + } + + /* + * Tell the service thread to terminate and wait for it to do so. + */ + pThis->enmState = kSupSvcGrantState_Terminating; + RTLOCALIPCSERVER hServer; + ASMAtomicXchgHandle(&pThis->hServer, NIL_RTLOCALIPCSERVER, &hServer); + RTThreadUserSignal(pThis->hThread); + + RTCritSectLeave(&pThis->CritSect); + + rc = RTThreadWait(pThis->hThread, 20*1000, NULL); + if (RT_FAILURE(rc) && rc == VERR_TIMEOUT) + { + RTThreadUserSignal(pThis->hThread); + RTLocalIpcServerDestroy(hServer); + hServer = NIL_RTLOCALIPCSERVER; + + rc = RTThreadWait(pThis->hThread, 40*1000, NULL); + if (RT_FAILURE(rc)) + supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(40 sec) returns %Rrc", rc); + } + else if (RT_FAILURE(rc)) + supSvcLogError("supSvcGrantStopAndDestroy - RTThreadWait(20 sec) returns %Rrc", rc); + pThis->hThread = NIL_RTTHREAD; + + /* + * Kill the parent pointers of any lingering sessions. + */ + RTCritSectEnter(&pThis->CritSect); + pThis->enmState = kSupSvcGrantState_Destroyed; + + supSvcGrantCleanUpSessionsLocked(pThis); + unsigned cSessions = 0; + for (PSUPSVCGRANTSESSION pCur = pThis->pSessionHead; pCur; pCur = pCur->pNext) + ASMAtomicWritePtr((void * volatile *)&pCur->pParent, NULL); + + RTCritSectLeave(&pThis->CritSect); + if (cSessions) + supSvcLogError("supSvcGrantStopAndDestroy: %d session failed to terminate!", cSessions); + + /* + * Free the resource. + */ + RTLocalIpcServerDestroy(hServer); + + RTSemEventDestroy(pThis->hResponseEvent); + pThis->hResponseEvent = NIL_RTSEMEVENT; + + RTSemEventDestroy(pThis->hSessionEvent); + pThis->hSessionEvent = NIL_RTSEMEVENT; + + RTCritSectDelete(&pThis->VerifyCritSect); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + + Log(("supSvcGrantStopAndDestroy: done (rc=%Rrc)\n", rc)); +} + diff --git a/src/VBox/HostDrivers/Support/SUPSvcInternal.h b/src/VBox/HostDrivers/Support/SUPSvcInternal.h new file mode 100644 index 000000000..05c244d11 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPSvcInternal.h @@ -0,0 +1,95 @@ +/* $Id: SUPSvcInternal.h 11725 2008-08-27 22:21:47Z vboxsync $ */ +/** @file + * VirtualBox Support Service - Internal header. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___SUPSvcInternal_h___ +#define ___SUPSvcInternal_h___ + +#include <VBox/cdefs.h> +#include <VBox/types.h> +#include <iprt/stdarg.h> +#include <iprt/getopt.h> + +__BEGIN_DECLS + +/** @name Common Helpers + * @{ */ +void supSvcLogErrorStr(const char *pszMsg); +void supSvcLogErrorV(const char *pszFormat, va_list va); +void supSvcLogError(const char *pszFormat, ...); +int supSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue); +int supSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg); +void supSvcDisplayErrorV(const char *pszFormat, va_list va); +void supSvcDisplayError(const char *pszFormat, ...); +int supSvcDisplayGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue); +int supSvcDisplayTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg); +/** @} */ + + +/** @name OS Backend + * @{ */ +/** + * Logs the message to the appropirate system log. + * + * @param psMsg The log string. + */ +void supSvcOsLogErrorStr(const char *pszMsg); +/** @} */ + + +/** @name The Service Manager + * @{ */ +void supSvcStopAndDestroyServices(void); +int supSvcTryStopServices(void); +int supSvcCreateAndStartServices(void); +/** @} */ + + +/** @name The Grant Service + * @{ */ +#define SUPSVC_GRANT_SERVICE_NAME "VirtualBoxGrantSvc" +DECLCALLBACK(int) supSvcGrantCreate(void **ppvInstance); +DECLCALLBACK(void) supSvcGrantStart(void *pvInstance); +DECLCALLBACK(int) supSvcGrantTryStop(void *pvInstance); +DECLCALLBACK(void) supSvcGrantStopAndDestroy(void *pvInstance, bool fRunning); +/** @} */ + + +/** @name The Global Service + * @{ */ +DECLCALLBACK(int) supSvcGlobalCreate(void **ppvInstance); +DECLCALLBACK(void) supSvcGlobalStart(void *pvInstance); +DECLCALLBACK(int) supSvcGlobalTryStop(void *pvInstance); +DECLCALLBACK(void) supSvcGlobalStopAndDestroy(void *pvInstance, bool fRunning); +/** @} */ + +__END_DECLS + +#endif + diff --git a/src/VBox/HostDrivers/Support/darwin/Info-Tiger.plist b/src/VBox/HostDrivers/Support/darwin/Info-Tiger.plist new file mode 100644 index 000000000..c3ba34d1e --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/Info-Tiger.plist @@ -0,0 +1,39 @@ + +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> <string>English</string> + <key>CFBundleExecutable</key> <string>VBoxDrvTiger</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxDrvTiger</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>CFBundleGetInfoString</key> <string>VirtualBox @VBOX_VERSION_STRING@, © 2007 Sun Microsystems, Inc.</string> + <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>OSBundleCompatibleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>IOKitPersonalities</key> + <dict> + <key>VBoxDrv</key> + <dict> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>IOClass</key> <string>org_virtualbox_SupDrv</string> + <key>IOMatchCategory</key> <string>org_virtualbox_SupDrv</string> + <key>IOProviderClass</key> <string>IOResources</string> + <key>IOResourceMatch</key> <string>IOKit</string> + <key>IOUserClientClass</key> <string>org_virtualbox_SupDrvClient</string> + </dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.bsd</key> <string>8.8.1</string> + <key>com.apple.kpi.mach</key> <string>8.8.1</string> + <key>com.apple.kpi.libkern</key> <string>8.8.1</string> + <key>com.apple.kpi.unsupported</key> <string>8.8.1</string> + <key>com.apple.kpi.iokit</key> <string>8.8.1</string> + <key>com.apple.kernel.bsd</key> <string>7.9.9</string> + <key>com.apple.kernel.mach</key> <string>7.9.9</string> + </dict> +</dict> +</plist> diff --git a/src/VBox/HostDrivers/Support/darwin/Info.plist b/src/VBox/HostDrivers/Support/darwin/Info.plist new file mode 100644 index 000000000..a577d5866 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/Info.plist @@ -0,0 +1,39 @@ + +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> <string>English</string> + <key>CFBundleExecutable</key> <string>VBoxDrv</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxDrv</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>CFBundleGetInfoString</key> <string>VirtualBox @VBOX_VERSION_STRING@, © 2007 Sun Microsystems, Inc.</string> + <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>OSBundleCompatibleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>IOKitPersonalities</key> + <dict> + <key>VBoxDrv</key> + <dict> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>IOClass</key> <string>org_virtualbox_SupDrv</string> + <key>IOMatchCategory</key> <string>org_virtualbox_SupDrv</string> + <key>IOProviderClass</key> <string>IOResources</string> + <key>IOResourceMatch</key> <string>IOKit</string> + <key>IOUserClientClass</key> <string>org_virtualbox_SupDrvClient</string> + </dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.bsd</key> <string>8.8.1</string> + <key>com.apple.kpi.mach</key> <string>8.8.1</string> + <key>com.apple.kpi.libkern</key> <string>8.8.1</string> + <key>com.apple.kpi.unsupported</key> <string>8.8.1</string> + <key>com.apple.kpi.iokit</key> <string>8.8.1</string> + <key>com.apple.kernel.bsd</key> <string>7.9.9</string> + <key>com.apple.kernel.mach</key> <string>7.9.9</string> + </dict> +</dict> +</plist> diff --git a/src/VBox/HostDrivers/Support/darwin/Makefile.kup b/src/VBox/HostDrivers/Support/darwin/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp new file mode 100644 index 000000000..c47ba6863 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp @@ -0,0 +1,1126 @@ +/* $Id: $ */ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Darwin host: + * Darwin driver C code + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +/* + * Deal with conflicts first. + * PVM - BSD mess, that FreeBSD has correct a long time ago. + * iprt/types.h before sys/param.h - prevents UINT32_C and friends. + */ +#include <iprt/types.h> +#include <sys/param.h> +#undef PVM + +#include <IOKit/IOLib.h> /* Assert as function */ + +#include "../SUPDrvInternal.h" +#include <VBox/version.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> +#include <iprt/alloc.h> +#include <iprt/power.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include <mach/kmod.h> +#include <miscfs/devfs/devfs.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/ioccom.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <IOKit/IOService.h> +#include <IOKit/IOUserclient.h> +#include <IOKit/pwr_mgt/RootDomain.h> + +#ifdef VBOX_WITH_HOST_VMX +__BEGIN_DECLS +# include <i386/vmx.h> +__END_DECLS +#endif + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ + +/** The module name. */ +#define DEVICE_NAME "vboxdrv" + + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +__BEGIN_DECLS +static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData); +static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData); + +static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess); +static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess); + +static int VBoxDrvDarwinErr2DarwinErr(int rc); + +static IOReturn VBoxDrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize); +__END_DECLS + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * The service class. + * This is just a formality really. + */ +class org_virtualbox_SupDrv : public IOService +{ + OSDeclareDefaultStructors(org_virtualbox_SupDrv); + +public: + virtual bool init(OSDictionary *pDictionary = 0); + virtual void free(void); + virtual bool start(IOService *pProvider); + virtual void stop(IOService *pProvider); + virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score); + virtual bool terminate(IOOptionBits fOptions); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_SupDrv, IOService); + + +/** + * An attempt at getting that clientDied() notification. + * I don't think it'll work as I cannot figure out where/what creates the correct + * port right. + */ +class org_virtualbox_SupDrvClient : public IOUserClient +{ + OSDeclareDefaultStructors(org_virtualbox_SupDrvClient); + +private: + PSUPDRVSESSION m_pSession; /**< The session. */ + task_t m_Task; /**< The client task. */ + org_virtualbox_SupDrv *m_pProvider; /**< The service provider. */ + +public: + virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type); + virtual bool start(IOService *pProvider); + static void sessionClose(RTPROCESS Process); + virtual IOReturn clientClose(void); + virtual IOReturn clientDied(void); + virtual bool terminate(IOOptionBits fOptions = 0); + virtual bool finalize(IOOptionBits fOptions); + virtual void stop(IOService *pProvider); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_SupDrvClient, IOUserClient); + + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Declare the module stuff. + */ +__BEGIN_DECLS +extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData); +extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData); + +KMOD_EXPLICIT_DECL(VBoxDrv, VBOX_VERSION_STRING, _start, _stop) +DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxDrvDarwinStart; +DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxDrvDarwinStop; +DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__; +__END_DECLS + + +/** + * Device extention & session data association structure. + */ +static SUPDRVDEVEXT g_DevExt; + +/** + * The character device switch table for the driver. + */ +static struct cdevsw g_DevCW = +{ + /** @todo g++ doesn't like this syntax - it worked with gcc before renaming to .cpp. */ + /*.d_open = */VBoxDrvDarwinOpen, + /*.d_close = */VBoxDrvDarwinClose, + /*.d_read = */eno_rdwrt, + /*.d_write = */eno_rdwrt, + /*.d_ioctl = */VBoxDrvDarwinIOCtl, + /*.d_stop = */eno_stop, + /*.d_reset = */eno_reset, + /*.d_ttys = */NULL, + /*.d_select= */eno_select, + /*.d_mmap = */eno_mmap, + /*.d_strategy = */eno_strat, + /*.d_getc = */eno_getc, + /*.d_putc = */eno_putc, + /*.d_type = */0 +}; + +/** Major device number. */ +static int g_iMajorDeviceNo = -1; +/** Registered devfs device handle. */ +static void *g_hDevFsDevice = NULL; + +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PSUPDRVSESSION g_apSessionHashTab[19]; +/** Calculates the index into g_apSessionHashTab.*/ +#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab)) +/** The number of open sessions. */ +static int32_t volatile g_cSessions = 0; +/** The notifier handle for the sleep callback handler. */ +static IONotifier *g_pSleepNotifier = NULL; + + + +/** + * Start the kernel module. + */ +static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData) +{ + int rc; +#ifdef DEBUG + printf("VBoxDrvDarwinStart\n"); +#endif + + /* + * Initialize IPRT. + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + rc = supdrvInitDevExt(&g_DevExt); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the session hash table. + */ + memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */ + rc = RTSpinlockCreate(&g_Spinlock); + if (RT_SUCCESS(rc)) + { + /* + * Registering ourselves as a character device. + */ + g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW); + if (g_iMajorDeviceNo >= 0) + { +#ifdef VBOX_WITH_HARDENING + g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME); +#else + g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME); +#endif + if (g_hDevFsDevice) + { + LogRel(("VBoxDrv: version " VBOX_VERSION_STRING " r%d; IOCtl version %#x; IDC version %#x; dev major=%d\n", + VBOX_SVN_REV, SUPDRV_IOC_VERSION, SUPDRV_IDC_VERSION, g_iMajorDeviceNo)); + + /* Register a sleep/wakeup notification callback */ + g_pSleepNotifier = registerPrioritySleepWakeInterest(&VBoxDrvDarwinSleepHandler, &g_DevExt, NULL); + if (g_pSleepNotifier == NULL) + LogRel(("VBoxDrv: register for sleep/wakeup events failed\n")); + + return KMOD_RETURN_SUCCESS; + } + + LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME)); + cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + g_iMajorDeviceNo = -1; + } + else + LogRel(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo)); + RTSpinlockDestroy(g_Spinlock); + g_Spinlock = NIL_RTSPINLOCK; + } + else + LogRel(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc)); + supdrvDeleteDevExt(&g_DevExt); + } + else + printf("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc); + RTR0Term(); + } + else + printf("VBoxDrv: failed to initialize IPRT (rc=%d)\n", rc); + + memset(&g_DevExt, 0, sizeof(g_DevExt)); + return KMOD_RETURN_FAILURE; +} + + +/** + * Stop the kernel module. + */ +static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData) +{ + int rc; + LogFlow(("VBoxDrvDarwinStop\n")); + + /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse + * unloading if we're busy. Investigate and implement this! */ + + /* + * Undo the work done during start (in reverse order). + */ + if (g_pSleepNotifier) + { + g_pSleepNotifier->remove(); + g_pSleepNotifier = NULL; + } + + devfs_remove(g_hDevFsDevice); + g_hDevFsDevice = NULL; + + rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + Assert(rc == g_iMajorDeviceNo); + g_iMajorDeviceNo = -1; + + supdrvDeleteDevExt(&g_DevExt); + + rc = RTSpinlockDestroy(g_Spinlock); + AssertRC(rc); + g_Spinlock = NIL_RTSPINLOCK; + + RTR0Term(); + + memset(&g_DevExt, 0, sizeof(g_DevExt)); +#ifdef DEBUG + printf("VBoxDrvDarwinStop - done\n"); +#endif + return KMOD_RETURN_SUCCESS; +} + + +/** + * Device open. Called on open /dev/vboxdrv + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ +#ifdef DEBUG_DARWIN_GIP + char szName[128]; + szName[0] = '\0'; + proc_name(proc_pid(pProcess), szName, sizeof(szName)); + Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName)); +#endif + + /* + * Find the session created by org_virtualbox_SupDrvClient, fail + * if no such session, and mark it as opened. We set the uid & gid + * here too, since that is more straight forward at this point. + */ + int rc = VINF_SUCCESS; + PSUPDRVSESSION pSession = NULL; + struct ucred *pCred = proc_ucred(pProcess); + if (pCred) + { + RTUID Uid = pCred->cr_ruid; + RTGID Gid = pCred->cr_rgid; + RTPROCESS Process = RTProcSelf(); + unsigned iHash = SESSION_HASH(Process); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while (pSession && pSession->Process != Process); + } + if (pSession) + { + if (!pSession->fOpened) + { + pSession->fOpened = true; + pSession->Uid = Uid; + pSession->Gid = Gid; + } + else + rc = VERR_ALREADY_LOADED; + } + else + rc = VERR_GENERAL_FAILURE; + + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + } + else + rc = SUPDRV_ERR_INVALID_PARAM; + +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc)); +#else + Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess))); +#endif + return VBoxDrvDarwinErr2DarwinErr(rc); +} + + +/** + * Close device. + */ +static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + Log(("VBoxDrvDarwinClose: pid=%d\n", (int)RTProcSelf())); + Assert(proc_pid(pProcess) == (int)RTProcSelf()); + + /* + * Hand the session closing to org_virtualbox_SupDrvClient. + */ + org_virtualbox_SupDrvClient::sessionClose(RTProcSelf()); + return 0; +} + + +/** + * Device I/O Control entry point. + * + * @returns Darwin for slow IOCtls and VBox status code for the fast ones. + * @param Dev The device number (major+minor). + * @param iCmd The IOCtl command. + * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)). + * @param fFlags Flag saying we're a character device (like we didn't know already). + * @param pProcess The process issuing this request. + */ +static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) +{ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + const RTPROCESS Process = proc_pid(pProcess); + const unsigned iHash = SESSION_HASH(Process); + PSUPDRVSESSION pSession; + + /* + * Find the session. + */ + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while (pSession && pSession->Process != Process); + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n", + (int)Process, iCmd)); + return EINVAL; + } + + /* + * Deal with the two high-speed IOCtl that takes it's arguments from + * the session and iCmd, and only returns a VBox status code. + */ + if ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || iCmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || iCmd == SUP_IOCTL_FAST_DO_NOP) + return supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession); + return VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); +} + + +/** + * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions. + * + * @returns Darwin errno. + * + * @param pSession The session. + * @param iCmd The IOCtl command. + * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer. + * @param pProcess The calling process. + */ +static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) +{ + LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); + + + /* + * Buffered or unbuffered? + */ + PSUPREQHDR pHdr; + user_addr_t pUser = 0; + void *pvPageBuf = NULL; + uint32_t cbReq = IOCPARM_LEN(iCmd); + if ((IOC_DIRMASK & iCmd) == IOC_INOUT) + { + pHdr = (PSUPREQHDR)pData; + if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd)); + return EINVAL; + } + if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd)); + return EINVAL; + } + if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq + || pHdr->cbIn < sizeof(*pHdr) + || pHdr->cbOut < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd)); + return EINVAL; + } + } + else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) + { + /* + * Get the header and figure out how much we're gonna have to read. + */ + SUPREQHDR Hdr; + pUser = (user_addr_t)*(void **)pData; + int rc = copyin(pUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); + return rc; + } + if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd)); + return EINVAL; + } + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || Hdr.cbOut < sizeof(Hdr) + || cbReq > _1M*16)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd)); + return EINVAL; + } + + /* + * Allocate buffer and copy in the data. + */ + pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); + if (!pHdr) + pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); + if (RT_UNLIKELY(!pHdr)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); + return ENOMEM; + } + rc = copyin(pUser, pHdr, Hdr.cbIn); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", + (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd)); + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pHdr); + return rc; + } + } + else + { + Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr); + if (RT_LIKELY(!rc)) + { + /* + * If not buffered, copy back the buffer before returning. + */ + if (pUser) + { + uint32_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd)); + cbOut = cbReq; + } + rc = copyout(pHdr, pUser, cbOut); + if (RT_UNLIKELY(rc)) + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", + pHdr, (unsigned long long)pUser, cbOut, rc, iCmd)); + + /* cleanup */ + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pHdr); + } + } + else + { + /* + * The request failed, just clean up. + */ + if (pUser) + { + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pHdr); + } + + Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); + rc = EINVAL; + } + + Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc)); + return rc; +} + + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code, see supdrvIDC. + * @param iReq The request code. + * @param pReq The request. + */ +int VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + PSUPDRVSESSION pSession; + + /* + * Some quick validations. + */ + if (RT_UNLIKELY(!VALID_PTR(pReq))) + return VERR_INVALID_POINTER; + + pSession = pReq->pSession; + if (pSession) + { + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return VERR_INVALID_PARAMETER; + if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt)) + return VERR_INVALID_PARAMETER; + } + else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT)) + return VERR_INVALID_PARAMETER; + + /* + * Do the job. + */ + return supdrvIDC(uReq, &g_DevExt, pSession, pReq); +} + + +/** + * Initializes any OS specific object creator fields. + */ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +/** + * Checks if the session can access the object. + * + * @returns true if a decision has been made. + * @returns false if the default access policy should be applied. + * + * @param pObj The object in question. + * @param pSession The session wanting to access the object. + * @param pszObjName The object name, can be NULL. + * @param prc Where to store the result when returning true. + */ +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + +/** + * Callback for blah blah blah. + */ +IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType, IOService * /* pProvider */, void * /* pvMessageArgument */, vm_size_t /* argSize */) +{ + LogFlow(("VBoxDrv: Got sleep/wake notice. Message type was %X\n", (uint)uMessageType)); + + if (uMessageType == kIOMessageSystemWillSleep) + RTPowerSignalEvent(RTPOWEREVENT_SUSPEND); + else if (uMessageType == kIOMessageSystemHasPoweredOn) + RTPowerSignalEvent(RTPOWEREVENT_RESUME); + + acknowledgeSleepWakeNotification(pvRefCon); + + return 0; +} + + +/** + * Enables or disables VT-x using kernel functions. + * + * @returns VBox status code. VERR_NOT_SUPPORTED has a special meaning. + * @param fEnable Whether to enable or disable. + */ +int VBOXCALL supdrvOSEnableVTx(bool fEnable) +{ +/* Zarking amateurish Apple engineering! + host_vmxon is actually buggy and may panic multicore machines. Reason, it + uses a simple lock which will disable preemption of the cpu/thread trying + to acquire it. Then it allocate wired memory in the kernel map for each + of the cpus in the system. If anyone else tries to mess around in the + kernel map on another CPU while this is going on, there is a fair chance + that it might cause the host_vmxon thread to block and hence panic since + preemption is disabled. Arrrg! */ +#if 0 /*def VBOX_WITH_HOST_VMX*/ + int rc; + if (fEnable) + { + rc = host_vmxon(false /* exclusive */); + if (rc == 0 /* all ok */) + rc = VINF_SUCCESS; + else if (rc == 1 /* unsupported */) + rc = VERR_VMX_NO_VMX; + else if (rc == 2 /* exclusive user */) + rc = VERR_VMX_IN_VMX_ROOT_MODE; + else /* shouldn't happen, but just in case. */ + { + LogRel(("host_vmxon returned %d\n", rc)); + rc = VERR_UNRESOLVED_ERROR; + } + } + else + { + host_vmxoff(); + rc = VINF_SUCCESS; + } + return rc; +#else + return VERR_NOT_SUPPORTED; +#endif +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + NOREF(pDevExt); + return false; +} + + +/** + * Converts a supdrv error code to a darwin error code. + * + * @returns corresponding darwin error code. + * @param rc supdrv error code (SUPDRV_ERR_* defines). + */ +static int VBoxDrvDarwinErr2DarwinErr(int rc) +{ + switch (rc) + { + case 0: return 0; + case SUPDRV_ERR_GENERAL_FAILURE: return EACCES; + case SUPDRV_ERR_INVALID_PARAM: return EINVAL; + case SUPDRV_ERR_INVALID_MAGIC: return EILSEQ; + case SUPDRV_ERR_INVALID_HANDLE: return ENXIO; + case SUPDRV_ERR_INVALID_POINTER: return EFAULT; + case SUPDRV_ERR_LOCK_FAILED: return ENOLCK; + case SUPDRV_ERR_ALREADY_LOADED: return EEXIST; + case SUPDRV_ERR_PERMISSION_DENIED: return EPERM; + case SUPDRV_ERR_VERSION_MISMATCH: return ENOSYS; + } + + return EPERM; +} + + +/** @todo move this to assembly where a simple "jmp printf" will to the trick. */ +RTDECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + va_list args; + char szMsg[512]; + + va_start(args, pszFormat); + vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args); + va_end(args); + + szMsg[sizeof(szMsg) - 1] = '\0'; + printf("%s", szMsg); + return 0; +} + + +/* + * + * org_virtualbox_SupDrv + * + */ + + +/** + * Initialize the object. + */ +bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary) +{ + LogFlow(("org_virtualbox_SupDrv::init([%p], %p)\n", this, pDictionary)); + if (IOService::init(pDictionary)) + { + /* init members. */ + return true; + } + return false; +} + + +/** + * Free the object. + */ +void org_virtualbox_SupDrv::free(void) +{ + LogFlow(("IOService::free([%p])\n", this)); + IOService::free(); +} + + +/** + * Check if it's ok to start this service. + * It's always ok by us, so it's up to IOService to decide really. + */ +IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score) +{ + LogFlow(("org_virtualbox_SupDrv::probe([%p])\n", this)); + return IOService::probe(pProvider, pi32Score); +} + + +/** + * Start this service. + */ +bool org_virtualbox_SupDrv::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this)); + + if (IOService::start(pProvider)) + { + /* register the service. */ + registerService(); + return true; + } + return false; +} + + +/** + * Stop this service. + */ +void org_virtualbox_SupDrv::stop(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider)); + IOService::stop(pProvider); +} + + +/** + * Termination request. + * + * @return true if we're ok with shutting down now, false if we're not. + * @param fOptions Flags. + */ +bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions) +{ + bool fRc; + LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n", + KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions)); + if ( KMOD_INFO_NAME.reference_count != 0 + || ASMAtomicUoReadS32(&g_cSessions)) + fRc = false; + else + fRc = IOService::terminate(fOptions); + LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc)); + return fRc; +} + + +/* + * + * org_virtualbox_SupDrvClient + * + */ + + +/** + * Initializer called when the client opens the service. + */ +bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type) +{ + LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n", + this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf())); + + if (!OwningTask) + return false; + if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type)) + { + m_Task = OwningTask; + m_pSession = NULL; + m_pProvider = NULL; + return true; + } + return false; +} + + +/** + * Start the client service. + */ +bool org_virtualbox_SupDrvClient::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n", + this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() )); + AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), + ("%p %p\n", m_Task, RTR0ProcHandleSelf()), + false); + + if (IOUserClient::start(pProvider)) + { + m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider); + if (m_pProvider) + { + Assert(!m_pSession); + + /* + * Create a new session. + */ + int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession); + if (RT_SUCCESS(rc)) + { + m_pSession->fOpened = false; + /* The Uid and Gid fields are set on open. */ + + /* + * Insert it into the hash table, checking that there isn't + * already one for this process first. + */ + unsigned iHash = SESSION_HASH(m_pSession->Process); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + + PSUPDRVSESSION pCur = g_apSessionHashTab[iHash]; + if (pCur && pCur->Process != m_pSession->Process) + { + do pCur = pCur->pNextHash; + while (pCur && pCur->Process != m_pSession->Process); + } + if (!pCur) + { + m_pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = m_pSession; + m_pSession->pvSupDrvClient = this; + ASMAtomicIncS32(&g_cSessions); + rc = VINF_SUCCESS; + } + else + rc = VERR_ALREADY_LOADED; + + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (RT_SUCCESS(rc)) + { + Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf())); + return true; + } + + LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur)); + supdrvCloseSession(&g_DevExt, m_pSession); + } + + m_pSession = NULL; + LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc)); + } + else + LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider)); + } + return false; +} + + +/** + * Common worker for clientClose and VBoxDrvDarwinClose. + * + * It will + */ +/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process) +{ + /* + * Look for the session. + */ + const unsigned iHash = SESSION_HASH(Process); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + PSUPDRVSESSION pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if (pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + } + else + { + PSUPDRVSESSION pPrev = pSession; + pSession = pSession->pNextHash; + while (pSession) + { + if (pSession->Process == Process) + { + pPrev->pNextHash = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + break; + } + + /* next */ + pPrev = pSession; + pSession = pSession->pNextHash; + } + } + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process)); + return; + } + + /* + * Remove it from the client object. + */ + org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient; + pSession->pvSupDrvClient = NULL; + if (pThis) + { + Assert(pThis->m_pSession == pSession); + pThis->m_pSession = NULL; + } + + /* + * Close the session. + */ + supdrvCloseSession(&g_DevExt, pSession); +} + + +/** + * Client exits normally. + */ +IOReturn org_virtualbox_SupDrvClient::clientClose(void) +{ + LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf())); + + /* + * Clean up the session if it's still around. + * + * We cannot rely 100% on close, and in the case of a dead client + * we'll end up hanging inside vm_map_remove() if we postpone it. + */ + if (m_pSession) + { + sessionClose(RTProcSelf()); + Assert(!m_pSession); + } + + m_pProvider = NULL; + terminate(); + + return kIOReturnSuccess; +} + + +/** + * The client exits abnormally / forgets to do cleanups. (logging) + */ +IOReturn org_virtualbox_SupDrvClient::clientDied(void) +{ + LogFlow(("org_virtualbox_SupDrvClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n", + this, m_Task, RTR0ProcHandleSelf(), RTProcSelf())); + + /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */ + return IOUserClient::clientDied(); +} + + +/** + * Terminate the service (initiate the destruction). (logging) + */ +bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions) +{ + LogFlow(("org_virtualbox_SupDrvClient::terminate([%p], %#x)\n", this, fOptions)); + return IOUserClient::terminate(fOptions); +} + + +/** + * The final stage of the client service destruction. (logging) + */ +bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions) +{ + LogFlow(("org_virtualbox_SupDrvClient::finalize([%p], %#x)\n", this, fOptions)); + return IOUserClient::finalize(fOptions); +} + + +/** + * Stop the client service. (logging) + */ +void org_virtualbox_SupDrvClient::stop(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrvClient::stop([%p])\n", this)); + IOUserClient::stop(pProvider); +} + diff --git a/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp new file mode 100644 index 000000000..215fc3129 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp @@ -0,0 +1,304 @@ +/* $Id: $ */ +/** @file + * VirtualBox Support Library - Darwin specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <VBox/types.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <mach/mach_port.h> +#include <IOKit/IOKitLib.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** BSD Device name. */ +#define DEVICE_NAME "/dev/vboxdrv" +/** The IOClass key of the service (see SUPDrv-darwin.cpp / Info.plist). */ +#define IOCLASS_NAME "org_virtualbox_SupDrv" + + + +/** + * Opens the BSD device node. + * + * @returns VBox status code. + */ +static int suplibDarwinOpenDevice(PSUPLIBDATA pThis) +{ + /* + * Open the BSD device. + * This will connect to the session created when the SupDrvClient was + * started, so it has to be done after opening the service (IOC v9.1+). + */ + int hDevice = open(DEVICE_NAME, O_RDWR, 0); + if (hDevice < 0) + { + int rc; + switch (errno) + { + case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break; + case EPERM: + case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break; + case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: rc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("SUP: Failed to open \"%s\", errno=%d, rc=%Rrc\n", DEVICE_NAME, errno, rc)); + return rc; + } + + /* + * Mark the file handle close on exec. + */ + if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0) + { +#ifdef IN_SUP_HARDENED_R3 + int rc = VERR_INTERNAL_ERROR; +#else + int err = errno; + int rc = RTErrConvertFromErrno(err); + LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc)); +#endif + close(hDevice); + return rc; + } + + pThis->hDevice = hDevice; + return VINF_SUCCESS; +} + + +/** + * Opens the IOKit service, instantiating org_virtualbox_SupDrvClient. + * + * @returns VBox status code. + */ +static int suplibDarwinOpenService(PSUPLIBDATA pThis) +{ + /* + * Open the IOKit client first - The first step is finding the service. + */ + mach_port_t MasterPort; + kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort); + if (kr != kIOReturnSuccess) + { + LogRel(("IOMasterPort -> %d\n", kr)); + return VERR_GENERAL_FAILURE; + } + + CFDictionaryRef ClassToMatch = IOServiceMatching(IOCLASS_NAME); + if (!ClassToMatch) + { + LogRel(("IOServiceMatching(\"%s\") failed.\n", IOCLASS_NAME)); + return VERR_GENERAL_FAILURE; + } + + /* Create an io_iterator_t for all instances of our drivers class that exist in the IORegistry. */ + io_iterator_t Iterator; + kr = IOServiceGetMatchingServices(MasterPort, ClassToMatch, &Iterator); + if (kr != kIOReturnSuccess) + { + LogRel(("IOServiceGetMatchingServices returned %d\n", kr)); + return VERR_GENERAL_FAILURE; + } + + /* Get the first item in the iterator and release it. */ + io_service_t ServiceObject = IOIteratorNext(Iterator); + IOObjectRelease(Iterator); + if (!ServiceObject) + { + LogRel(("SUP: Couldn't find any matches. The kernel module is probably not loaded.\n")); + return VERR_VM_DRIVER_NOT_INSTALLED; + } + + /* + * Open the service. + * + * This will cause the user client class in SUPDrv-darwin.cpp to be + * instantiated and create a session for this process. + */ + io_connect_t Connection = NULL; + kr = IOServiceOpen(ServiceObject, mach_task_self(), 0, &Connection); + IOObjectRelease(ServiceObject); + if (kr != kIOReturnSuccess) + { + LogRel(("SUP: IOServiceOpen returned %d. Driver open failed.\n", kr)); + pThis->pvConnection = NULL; + return VERR_VM_DRIVER_OPEN_ERROR; + } + + AssertCompile(sizeof(void *) == sizeof(Connection)); + pThis->pvConnection = (void *)Connection; + return VINF_SUCCESS; +} + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + /* + * Do the job. + */ + Assert(pThis->hDevice == NIL_RTFILE); + int rc = suplibDarwinOpenService(pThis); + if (RT_SUCCESS(rc)) + { + rc = suplibDarwinOpenDevice(pThis); + if (RT_FAILURE(rc)) + { + kern_return_t kr = IOServiceClose((io_connect_t)pThis->pvConnection); + if (kr != kIOReturnSuccess) + { + LogRel(("Warning: IOServiceClose(%p) returned %d\n", pThis->pvConnection, kr)); + AssertFailed(); + } + pThis->pvConnection = NULL; + } + } + + return rc; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Close the connection to the IOService. + * This will cause the SUPDRVSESSION to be closed (starting IOC 9.1). + */ + if (pThis->pvConnection) + { + kern_return_t kr = IOServiceClose((io_connect_t)pThis->pvConnection); + if (kr != kIOReturnSuccess) + { + LogRel(("Warning: IOServiceClose(%p) returned %d\n", pThis->pvConnection, kr)); + AssertFailed(); + } + pThis->pvConnection = NULL; + } + + /* + * Check if we're initited at all. + */ + if (pThis->hDevice != NIL_RTFILE) + { + if (close(pThis->hDevice)) + AssertFailed(); + pThis->hDevice = NIL_RTFILE; + } + + return VINF_SUCCESS; +} + + +int suplibOsInstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsUninstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + int rc = ioctl(pThis->hDevice, uFunction, NULL); + if (rc == -1) + rc = errno; + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = valloc(cPages << PAGE_SHIFT); + if (*ppvPages) + { + memset(*ppvPages, 0, cPages << PAGE_SHIFT); + return VINF_SUCCESS; + } + return RTErrConvertFromErrno(errno); +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + free(pvPages); + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c new file mode 100644 index 000000000..1351f1cb2 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c @@ -0,0 +1,59 @@ +/* $Id: SUPR0IdcClient-darwin.c 10262 2008-07-04 23:42:33Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Darwin Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq); +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + int rc = SUPDrvDarwinIDC(iReq, pReq); + if (RT_SUCCESS(rc)) + rc = pReq->rc; + + NOREF(pHandle); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/freebsd/Makefile.kup b/src/VBox/HostDrivers/Support/freebsd/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/freebsd/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c new file mode 100644 index 000000000..8201b475b --- /dev/null +++ b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c @@ -0,0 +1,586 @@ +/* $Id: SUPDrv-freebsd.c 16030 2009-01-19 05:24:30Z vboxsync $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - FreeBSD specifics. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +/* Deal with conflicts first. */ +#include <sys/param.h> +#undef PVM +#include <sys/types.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/fcntl.h> +#include <sys/conf.h> +#include <sys/uio.h> + +#include "../SUPDrvInternal.h" +#include <VBox/version.h> +#include <iprt/initterm.h> +#include <iprt/string.h> +#include <iprt/spinlock.h> +#include <iprt/process.h> +#include <iprt/assert.h> +#include <VBox/log.h> +#include <iprt/alloc.h> +#include <iprt/err.h> + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg); +static int VBoxDrvFreeBSDLoad(void); +static int VBoxDrvFreeBSDUnload(void); +static void VBoxDrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pachName, int cchName, struct cdev **ppDev); + +static d_fdopen_t VBoxDrvFreeBSDOpen; +static d_close_t VBoxDrvFreeBSDClose; +static d_ioctl_t VBoxDrvFreeBSDIOCtl; +static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Module info structure used by the kernel. + */ +static moduledata_t g_VBoxDrvFreeBSDModule = +{ + "vboxdrv", + VBoxDrvFreeBSDModuleEvent, + NULL +}; + +/** Declare the module as a pseudo device. */ +DECLARE_MODULE(vboxdrv, g_VBoxDrvFreeBSDModule, SI_SUB_PSEUDO, SI_ORDER_ANY); + +/** + * The /dev/vboxdrv character device entry points. + */ +static struct cdevsw g_VBoxDrvFreeBSDChrDevSW = +{ + .d_version = D_VERSION, + .d_flags = D_PSEUDO | D_TRACKCLOSE, + .d_fdopen = VBoxDrvFreeBSDOpen, + .d_close = VBoxDrvFreeBSDClose, + .d_ioctl = VBoxDrvFreeBSDIOCtl, + .d_name = "vboxdrv" +}; + +/** List of cloned device. Managed by the kernel. */ +static struct clonedevs *g_pVBoxDrvFreeBSDClones; +/** The dev_clone event handler tag. */ +static eventhandler_tag g_VBoxDrvFreeBSDEHTag; + +/** The device extention. */ +static SUPDRVDEVEXT g_VBoxDrvFreeBSDDevExt; + + + + +/** + * Module event handler. + * + * @param pMod The module structure. + * @param enmEventType The event type (modeventtype_t). + * @param pvArg Module argument. NULL. + * + * @return 0 on success, errno.h status code on failure. + */ +static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg) +{ + int rc; + switch (enmEventType) + { + case MOD_LOAD: + rc = VBoxDrvFreeBSDLoad(); + break; + + case MOD_UNLOAD: + rc = VBoxDrvFreeBSDUnload(); + break; + + case MOD_SHUTDOWN: + case MOD_QUIESCE: + default: + return EOPNOTSUPP; + } + + if (RT_SUCCESS(rc)) + return 0; + return RTErrConvertToErrno(rc); +} + + +static int VBoxDrvFreeBSDLoad(void) +{ + dprintf(("VBoxDrvFreeBSDLoad:\n")); + + /* + * Initialize the runtime. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + rc = supdrvInitDevExt(&g_VBoxDrvFreeBSDDevExt); + if (RT_SUCCESS(rc)) + { + /* + * Configure device cloning. + */ + clone_setup(&g_pVBoxDrvFreeBSDClones); + g_VBoxDrvFreeBSDEHTag = EVENTHANDLER_REGISTER(dev_clone, VBoxDrvFreeBSDClone, 0, 1000); + if (g_VBoxDrvFreeBSDEHTag) + { + dprintf(("VBoxDrvFreeBSDLoad: returns successfully\n")); + return VINF_SUCCESS; + } + + printf("vboxdrv: EVENTHANDLER_REGISTER(dev_clone,,,) failed\n"); + clone_cleanup(&g_pVBoxDrvFreeBSDClones); + rc = SUPDRV_ERR_ALREADY_LOADED; + supdrvDeleteDevExt(&g_VBoxDrvFreeBSDDevExt); + } + else + printf("vboxdrv: supdrvInitDevExt failed, rc=%d\n", rc); + RTR0Term(); + } + else + printf("vboxdrv: RTR0Init failed, rc=%d\n", rc); + return rc; +} + +static int VBoxDrvFreeBSDUnload(void) +{ + dprintf(("VBoxDrvFreeBSDUnload:\n")); + + /** @todo verify that FreeBSD does reference counting. */ + + /* + * Reserve what we did in VBoxDrvFreeBSDInit. + */ + clone_cleanup(&g_pVBoxDrvFreeBSDClones); + + supdrvDeleteDevExt(&g_VBoxDrvFreeBSDDevExt); + + RTR0Term(); + + memset(&g_VBoxDrvFreeBSDDevExt, 0, sizeof(g_VBoxDrvFreeBSDDevExt)); + + dprintf(("VBoxDrvFreeBSDUnload: returns\n")); + return VINF_SUCCESS; +} + + +/** + * DEVFS event handler. + */ +static void VBoxDrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev) +{ + int iUnit; + int rc; + + dprintf(("VBoxDrvFreeBSDClone: pszName=%s ppDev=%p\n", pszName, ppDev)); + + /* + * One device node per user, si_drv1 points to the session. + * /dev/vboxdrv<N> where N = {0...255}. + */ + if (!ppDev) + return; + if (dev_stdclone(pszName, NULL, "vboxdrv", &iUnit) != 1) + return; + if (iUnit >= 256 || iUnit < 0) + { + dprintf(("VBoxDrvFreeBSDClone: iUnit=%d >= 256 - rejected\n", iUnit)); + return; + } + + dprintf(("VBoxDrvFreeBSDClone: pszName=%s iUnit=%d\n", pszName, iUnit)); + + rc = clone_create(&g_pVBoxDrvFreeBSDClones, &g_VBoxDrvFreeBSDChrDevSW, &iUnit, ppDev, 0); + dprintf(("VBoxDrvFreeBSDClone: clone_create -> %d; iUnit=%d\n", rc, iUnit)); + if (rc) + { +#ifdef VBOX_WITH_HARDENING + *ppDev = make_dev(&g_VBoxDrvFreeBSDChrDevSW, unit2minor(iUnit), UID_ROOT, GID_WHEEL, 0600, "vboxdrv%d", iUnit); +#else + *ppDev = make_dev(&g_VBoxDrvFreeBSDChrDevSW, unit2minor(iUnit), UID_ROOT, GID_WHEEL, 0666, "vboxdrv%d", iUnit); +#endif + if (*ppDev) + { + dev_ref(*ppDev); + (*ppDev)->si_flags |= SI_CHEAPCLONE; + dprintf(("VBoxDrvFreeBSDClone: Created *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); + (*ppDev)->si_drv1 = (*ppDev)->si_drv2 = NULL; + } + else + OSDBGPRINT(("VBoxDrvFreeBSDClone: make_dev iUnit=%d failed\n", iUnit)); + } + else + dprintf(("VBoxDrvFreeBSDClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); +} + + + +/** + * + * @returns 0 on success, errno on failure. + * EBUSY if the device is used by someone else. + * @param pDev The device node. + * @param fOpen The open flags. + * @param pTd The thread. + * @param pFd The file descriptor. FreeBSD 7.0 and later. + * @param iFd The file descriptor index(?). Pre FreeBSD 7.0. + */ +#if __FreeBSD__ >= 7 +static int VBoxDrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, struct file *pFd) +#else +static int VBoxDrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, int iFd) +#endif +{ + PSUPDRVSESSION pSession; + int rc; + + dprintf(("VBoxDrvFreeBSDOpen: fOpen=%#x iUnit=%d\n", fOpen, minor2unit(minor(pDev)))); + + /* + * Let's be a bit picky about the flags... + */ + if (fOpen != (FREAD|FWRITE /*=O_RDWR*/)) + { + dprintf(("VBoxDrvFreeBSDOpen: fOpen=%#x expected %#x\n", fOpen, O_RDWR)); + return EINVAL; + } + + /* + * Try grab it (we don't grab the giant, remember). + */ + if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, (void *)0x42, NULL)) + return EBUSY; + + /* + * Create a new session. + */ + rc = supdrvCreateSession(&g_VBoxDrvFreeBSDDevExt, true /* fUser */, &pSession); + if (RT_SUCCESS(rc)) + { + /** @todo get (r)uid and (r)gid. + pSession->Uid = stuff; + pSession->Gid = stuff; */ + if (ASMAtomicCmpXchgPtr(&pDev->si_drv1, pSession, (void *)0x42)) + return 0; + + OSDBGPRINT(("VBoxDrvFreeBSDOpen: si_drv1=%p, expected 0x42!\n", pDev->si_drv1)); + supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession); + } + + return RTErrConvertToErrno(rc); +} + + +/** + * Close a file device previously opened by VBoxDrvFreeBSDOpen + * + * @returns 0 on success. + * @param pDev The device. + * @param fFile The file descriptor flags. + * @param DevType The device type (CHR. + * @param pTd The calling thread. + */ +static int VBoxDrvFreeBSDClose(struct cdev *pDev, int fFile, int DevType, struct thread *pTd) +{ + PSUPDRVSESSION pSession = (PSUPDRVSESSION)pDev->si_drv1; + dprintf(("VBoxDrvFreeBSDClose: fFile=%#x iUnit=%d pSession=%p\n", fFile, minor2unit(minor(pDev)), pSession)); + + /* + * Close the session if it's still hanging on to the device... + */ + if (VALID_PTR(pSession)) + { + supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession); + if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, NULL, pSession)) + OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p expected %p!\n", pDev->si_drv1, pSession)); + } + else + OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p!\n", pSession)); + return 0; +} + + +/** + * I/O control request. + * + * @returns depends... + * @param pDev The device. + * @param ulCmd The command. + * @param pvData Pointer to the data. + * @param fFile The file descriptor flags. + * @param pTd The calling thread. + */ +static int VBoxDrvFreeBSDIOCtl(struct cdev *pDev, u_long ulCmd, caddr_t pvData, int fFile, struct thread *pTd) +{ + /* + * Validate the input. + */ + PSUPDRVSESSION pSession = (PSUPDRVSESSION)pDev->si_drv1; + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return EINVAL; + + /* + * Deal with the fast ioctl path first. + */ + if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || ulCmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || ulCmd == SUP_IOCTL_FAST_DO_NOP) + return supdrvIOCtlFast(ulCmd, *(uint32_t *)pvData, &g_VBoxDrvFreeBSDDevExt, pSession); + + return VBoxDrvFreeBSDIOCtlSlow(pSession, ulCmd, pvData, pTd); +} + + +/** + * Deal with the 'slow' I/O control requests. + * + * @returns 0 on success, appropriate errno on failure. + * @param pSession The session. + * @param ulCmd The command. + * @param pvData The request data. + * @param pTd The calling thread. + */ +static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd) +{ + PSUPREQHDR pHdr; + uint32_t cbReq = IOCPARM_LEN(ulCmd); + void *pvUser = NULL; + + /* + * Buffered request? + */ + if ((IOC_DIRMASK & ulCmd) == IOC_INOUT) + { + pHdr = (PSUPREQHDR)pvData; + if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd)); + return EINVAL; + } + if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", pHdr->fFlags, ulCmd)); + return EINVAL; + } + if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq + || pHdr->cbIn < sizeof(*pHdr) + || pHdr->cbOut < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd)); + return EINVAL; + } + } + /* + * Big unbuffered request? + */ + else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq) + { + /* + * Read the header, validate it and figure out how much that needs to be buffered. + */ + SUPREQHDR Hdr; + pvUser = *(void **)pvData; + int rc = copyin(pvUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd)); + return rc; + } + if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", Hdr.fFlags, ulCmd)); + return EINVAL; + } + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || Hdr.cbOut < sizeof(Hdr) + || cbReq > _1M*16)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd)); + return EINVAL; + } + + /* + * Allocate buffer and copy in the data. + */ + pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); + if (RT_UNLIKELY(!pHdr)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd)); + return ENOMEM; + } + rc = copyin(pvUser, pHdr, Hdr.cbIn); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n", + pvUser, pHdr, Hdr.cbIn, rc, ulCmd)); + RTMemTmpFree(pHdr); + return rc; + } + } + else + { + dprintf(("VBoxDrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + int rc = supdrvIOCtl(ulCmd, &g_VBoxDrvFreeBSDDevExt, pSession, pHdr); + if (RT_LIKELY(!rc)) + { + /* + * If unbuffered, copy back the result before returning. + */ + if (pvUser) + { + uint32_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd)); + cbOut = cbReq; + } + rc = copyout(pHdr, pvUser, cbOut); + if (RT_UNLIKELY(rc)) + OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd)); + + /* cleanup */ + RTMemTmpFree(pHdr); + } + dprintf(("VBoxDrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd)); + } + else + { + /* + * The request failed, just clean up. + */ + if (pvUser) + RTMemTmpFree(pHdr); + + dprintf(("VBoxDrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc)); + rc = EINVAL; + } + + return rc; +} + + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code, see supdrvIDC. + * @param iReq The request code. + * @param pReq The request. + */ +int VBOXCALL SUPDrvFreeBSDIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + PSUPDRVSESSION pSession; + + /* + * Some quick validations. + */ + if (RT_UNLIKELY(!VALID_PTR(pReq))) + return VERR_INVALID_POINTER; + + pSession = pReq->pSession; + if (pSession) + { + if (RT_UNLIKELY(!VALID_PTR(pReq->pSession))) + return VERR_INVALID_PARAMETER; + if (RT_UNLIKELY(pSession->pDevExt != &g_VBoxDrvFreeBSDModule)) + return VERR_INVALID_PARAMETER; + } + else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT)) + return VERR_INVALID_PARAMETER; + + /* + * Do the job. + */ + return supdrvIDC(uReq, &g_VBoxDrvFreeBSDModule, pSession, pReq); +} + + +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + return false; +} + + +SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + va_list va; + char szMsg[256]; + int cch; + + va_start(va, pszFormat); + cch = RTStrPrintfV(szMsg, sizeof(szMsg), pszFormat, va); + va_end(va); + + printf("%s", szMsg); + + return cch; +} + diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp b/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp new file mode 100644 index 000000000..6443b4619 --- /dev/null +++ b/src/VBox/HostDrivers/Support/freebsd/SUPLib-freebsd.cpp @@ -0,0 +1,196 @@ +/* $Id: $ */ +/** @file + * VirtualBox Support Library - FreeBSD specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <VBox/types.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** FreeBSD base device name. */ +#define DEVICE_NAME "/dev/vboxdrv" + + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + /* + * Try open the BSD device. + */ + int hDevice = -1; + char szDevice[sizeof(DEVICE_NAME) + 16]; + for (unsigned iUnit = 0; iUnit < 1024; iUnit++) + { + errno = 0; + RTStrPrintf(szDevice, sizeof(szDevice), DEVICE_NAME "%d", iUnit); + hDevice = open(szDevice, O_RDWR, 0); + if (hDevice >= 0 || errno != EBUSY) + break; + } + if (hDevice < 0) + { + int rc; + switch (errno) + { + case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break; + case EPERM: + case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break; + case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: rc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", szDevice, errno, rc)); + return rc; + } + + /* + * Mark the file handle close on exec. + */ + if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0) + { +#ifdef IN_SUP_HARDENED_R3 + int rc = VERR_INTERNAL_ERROR; +#else + int err = errno; + int rc = RTErrConvertFromErrno(err); + LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc)); +#endif + close(hDevice); + return rc; + } + + /* + * We're done. + */ + pThis->hDevice = hDevice; + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Check if we're initited at all. + */ + if (pThis->hDevice != NIL_RTFILE) + { + if (close(pThis->hDevice)) + AssertFailed(); + pThis->hDevice = NIL_RTFILE; + } + return VINF_SUCCESS; +} + + +int suplibOsInstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsUninstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + int rc = ioctl(pThis->hDevice, uFunction, idCpu); + if (rc == -1) + rc = errno; + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = RTMemPageAllocZ(cPages << PAGE_SHIFT); + if (*ppvPages) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + RTMemPageFree(pvPages); + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c b/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c new file mode 100644 index 000000000..f9d0000eb --- /dev/null +++ b/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c @@ -0,0 +1,59 @@ +/* $Id: SUPR0IdcClient-freebsd.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, FreeBSD Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq); +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + int rc = SUPDrvFreeBSDIDC(iReq, pReq); + if (RT_SUCCESS(rc)) + rc = pReq->rc; + + NOREF(pHandle); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/linux/Makefile b/src/VBox/HostDrivers/Support/linux/Makefile new file mode 100644 index 000000000..d58b9160f --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/Makefile @@ -0,0 +1,282 @@ +# +# Makefile for the VirtualBox Linux Host Driver. +# (For 2.6.x this file must be called 'Makefile'!) +# + +# +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +# +# First, figure out which architecture we're targeting and the build type. +# (We have to support basic cross building (ARCH=i386|x86_64).) +# While at it, warn about BUILD_* vars found to help with user problems. +# +ifneq ($(filter-out amd64 x86,$(BUILD_TARGET_ARCH)),) + $(warning Ignoring unknown BUILD_TARGET_ARCH value '$(BUILD_TARGET_ARCH)'.) + BUILD_TARGET_ARCH := +endif +ifeq ($(BUILD_TARGET_ARCH),) + ifeq ($(ARCH),x86_64) + BUILD_TARGET_ARCH := amd64 + else + ifeq ($(ARCH),i386) + BUILD_TARGET_ARCH := x86 + else + ifeq ($(filter-out x86_64 amd64 AMD64,$(shell uname -m)),) + BUILD_TARGET_ARCH := amd64 + else + BUILD_TARGET_ARCH := x86 + endif + endif + endif +else + $(warning Using BUILD_TARGET_ARCH='$(BUILD_TARGET_ARCH)' from the $(origin BUILD_TARGET_ARCH).) +endif + +ifneq ($(filter-out release profile debug strict,$(BUILD_TYPE)),) + $(warning Ignoring unknown BUILD_TYPE value '$(BUILD_TYPE)'.) + BUILD_TYPE := +endif +ifeq ($(BUILD_TYPE),) + BUILD_TYPE := release +else + $(warning Using BUILD_TYPE='$(BUILD_TYPE)' from the $(origin BUILD_TYPE).) +endif + +# use vm_insert_page() API (if available) to map kernel pages to userland - better debugging +# VBOX_USE_INSERT_PAGE = 1 + +# override is required by the Debian guys +override MODULE = vboxdrv +OBJS = \ + linux/SUPDrv-linux.o \ + SUPDrv.o \ + r0drv/alloc-r0drv.o \ + r0drv/initterm-r0drv.o \ + r0drv/memobj-r0drv.o \ + r0drv/mpnotification-r0drv.o \ + r0drv/powernotification-r0drv.o \ + r0drv/linux/assert-r0drv-linux.o \ + r0drv/linux/alloc-r0drv-linux.o \ + r0drv/linux/initterm-r0drv-linux.o \ + r0drv/linux/memobj-r0drv-linux.o \ + r0drv/linux/mp-r0drv-linux.o \ + r0drv/linux/mpnotification-r0drv-linux.o \ + r0drv/linux/process-r0drv-linux.o \ + r0drv/linux/semevent-r0drv-linux.o \ + r0drv/linux/semeventmulti-r0drv-linux.o \ + r0drv/linux/semfastmutex-r0drv-linux.o \ + r0drv/linux/spinlock-r0drv-linux.o \ + r0drv/linux/thread-r0drv-linux.o \ + r0drv/linux/thread2-r0drv-linux.o \ + r0drv/linux/time-r0drv-linux.o \ + r0drv/linux/timer-r0drv-linux.o \ + common/err/RTErrConvertFromErrno.o \ + common/log/log.o \ + common/log/logellipsis.o \ + common/log/logrel.o \ + common/log/logrelellipsis.o \ + common/log/logcom.o \ + common/log/logformat.o \ + common/string/strformat.o \ + common/string/strformatrt.o \ + common/string/strformattype.o \ + common/string/strprintf.o \ + common/string/strtonum.o \ + r0drv/linux/RTLogWriteDebugger-r0drv-linux.o \ + generic/RTAssertShouldPanic-generic.o \ + generic/RTLogWriteStdErr-stub-generic.o \ + generic/RTLogWriteStdOut-stub-generic.o \ + generic/RTLogWriteUser-generic.o \ + VBox/log-vbox.o \ + VBox/strformat-vbox.o +ifeq ($(BUILD_TARGET_ARCH),x86) +OBJS += math/gcc/divdi3.o \ + math/gcc/moddi3.o \ + math/gcc/qdivrem.o \ + math/gcc/udivdi3.o \ + math/gcc/divdi3.o \ + math/gcc/umoddi3.o +endif +ifeq ($(BUILD_TARGET_ARCH),amd64) +OBJS += alloc/heapsimple.o +endif + +ifneq ($(MAKECMDGOALS),clean) + +ifeq ($(KERNELRELEASE),) + + # + # building from this directory + # + + # kernel base directory + ifndef KERN_DIR + # build for the current kernel, version check + KERN_DIR := /lib/modules/$(shell uname -r)/build + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + KERN_DIR := /usr/src/linux + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + $(error Error: unable to find the sources of your current Linux kernel. \ + Specify KERN_DIR=<directory> and run Make again) + endif + $(warning Warning: using /usr/src/linux as the source directory of your \ + Linux kernel. If this is not correct, specify \ + KERN_DIR=<directory> and run Make again.) + endif + # check if versions match -- works only for later 2.6 kernels + VBOX_KERN_VER := $(shell $(MAKE) -sC $(KERN_DIR) kernelrelease 2> /dev/null || true) + ifneq ($(VBOX_KERN_VER),) + ifneq ($(VBOX_KERN_VER),$(shell uname -r)) + $(error Error: /usr/src/linux (version $(VBOX_KERN_VER)) does not match \ + the current kernel (version $(shell uname -r))) + endif + endif + else + # build for a dedicated kernel, no version check + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + $(error Error: KERN_DIR does not point to a directory) + endif + endif + + # includes + ifndef KERN_INCL + KERN_INCL = $(KERN_DIR)/include + endif + ifneq ($(shell if test -d $(KERN_INCL); then echo yes; fi),yes) + $(error Error: unable to find the include directory for your current Linux \ + kernel. Specify KERN_INCL=<directory> and run Make again) + endif + + # module install dir, only for current kernel + ifneq ($(filter install install_rpm,$(MAKECMDGOALS)),) + ifndef MODULE_DIR + MODULE_DIR_TST := /lib/modules/$(shell uname -r) + ifeq ($(shell if test -d $(MODULE_DIR_TST); then echo yes; fi),yes) + MODULE_DIR := $(MODULE_DIR_TST)/misc + else + $(error Unable to find the folder to install the support driver to) + endif + endif # MODULE_DIR unspecified + endif + + # guess kernel version (24 or 26) + KERN_VERSION := $(if $(wildcard $(KERN_DIR)/Rules.make),24,26) + +else # neq($(KERNELRELEASE),) + + # + # building from kbuild (make -C <kernel_directory> M=`pwd`) + # + + # guess kernel version (24 or 26) + KERN_VERSION := $(if $(wildcard $(PWD)/Rules.make),24,26) + +endif # neq($(KERNELRELEASE),) + +# debug - show guesses. +ifdef DEBUG +$(warning dbg: KERN_DIR = $(KERN_DIR)) +$(warning dbg: KERN_INCL = $(KERN_INCL)) +$(warning dbg: MODULE_DIR = $(MODULE_DIR)) +$(warning dbg: KERN_VERSION = $(KERN_VERSION)) +endif + +KBUILD_VERBOSE ?= 1 + +# +# Compiler options +# +ifndef INCL + INCL := $(addprefix -I,$(KERN_INCL) $(EXTRA_INCL)) + ifndef KBUILD_EXTMOD + KBUILD_EXTMOD := $(shell pwd) + endif + INCL += $(addprefix -I$(KBUILD_EXTMOD),/ /include /r0drv/linux) + export INCL +endif +KFLAGS := -D__KERNEL__ -DMODULE -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -DVBOX_WITH_HARDENING +ifdef VBOX_REDHAT_KABI + KFLAGS += -DVBOX_REDHAT_KABI +endif +ifndef CONFIG_VBOXDRV_FIXEDMAJOR + KFLAGS += -DCONFIG_VBOXDRV_AS_MISC +endif +ifeq ($(BUILD_TARGET_ARCH),amd64) + KFLAGS += -DRT_ARCH_AMD64 +else + KFLAGS += -DRT_ARCH_X86 +endif +# must be consistent with Config.kmk! +KFLAGS += -DVBOX_WITH_64_BITS_GUESTS +ifeq ($(BUILD_TYPE),debug) + KFLAGS += -DDEBUG -DDEBUG_$(USER) -g + # IPRT_DEBUG_SEMS indicates thread wrt sems state via the comm field. + KFLAGS += -DIPRT_DEBUG_SEMS +endif + +# By default we use remap_pfn_range() kernel API to make kernel pages +# visible for userland. Unfortuately, it leads to situation that +# during debug session all structures on that page (such as PVM pointer) +# are not accessible to the debugger (see #3214). +# This code enables experimental support +# for vm_insert_page() kernel API, allowing to export kernel pages +# to the userland in more debugger-friendly way. Due to stability +# concerns, not enabled by default yet. +ifdef VBOX_USE_INSERT_PAGE + KFLAGS += -DVBOX_USE_INSERT_PAGE +endif + +# 2.6 and later +MODULE_EXT := ko +$(MODULE)-y := $(OBJS) + +# build defs +EXTRA_CFLAGS += $(INCL) $(KFLAGS) $(KDEBUG) + +all: $(MODULE) + +obj-m += $(MODULE).o + +$(MODULE): + $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C $(KERN_DIR) SUBDIRS=$(CURDIR) SRCROOT=$(CURDIR) modules + +install: $(MODULE) + @mkdir -p $(MODULE_DIR); \ + install -m 0664 -o root -g root $(MODULE).$(MODULE_EXT) $(MODULE_DIR); \ + PATH="$(PATH):/bin:/sbin" depmod -ae; \ + rm -f /etc/vbox/module_not_compiled + +install_rpm: $(MODULE) + @mkdir -p $(MODULE_DIR); \ + install -m 0664 $(MODULE).$(MODULE_EXT) $(MODULE_DIR) + +endif # eq($(MAKECMDGOALS),clean) + +clean: + for f in . linux r0drv r0drv/linux VBox common/err common/string common/log generic math/gcc; \ + do rm -f $$f/*.o $$f/.*.cmd $$f/.*.flags; done + rm -rf .vboxdrv* .tmp_ver* vboxdrv.* Module.symvers Modules.symvers modules.order diff --git a/src/VBox/HostDrivers/Support/linux/Makefile.kup b/src/VBox/HostDrivers/Support/linux/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c new file mode 100644 index 000000000..7fa74a1b6 --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c @@ -0,0 +1,1089 @@ +/* $Rev: 15999 $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - Linux specifics. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + * Some lines of code to disable the local APIC on x86_64 machines taken + * from a Mandriva patch by Gwenole Beauchesne <gbeauchesne@mandriva.com>. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include "../SUPDrvInternal.h" +#include "the-linux-kernel.h" +#include "version-generated.h" + +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <VBox/log.h> +#include <iprt/mp.h> + +/** @todo figure out the exact version number */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) +# include <iprt/power.h> +# define VBOX_WITH_SUSPEND_NOTIFICATION +#endif + +#include <linux/sched.h> +#ifdef CONFIG_DEVFS_FS +# include <linux/devfs_fs_kernel.h> +#endif +#ifdef CONFIG_VBOXDRV_AS_MISC +# include <linux/miscdevice.h> +#endif +#ifdef CONFIG_X86_LOCAL_APIC +# include <asm/apic.h> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +# include <asm/nmi.h> +# endif +#endif +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION +# include <linux/platform_device.h> +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) +# include <asm/pgtable.h> +# define global_flush_tlb __flush_tlb_global +#endif + +#include <iprt/mem.h> + + +/* devfs defines */ +#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC) +# ifdef VBOX_WITH_HARDENING +# define VBOX_DEV_FMASK (S_IWUSR | S_IRUSR) +# else +# define VBOX_DEV_FMASK (S_IRUGO | S_IWUGO) +# endif + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + +# define VBOX_REGISTER_DEVFS() \ +({ \ + void *rc = NULL; \ + if (devfs_mk_cdev(MKDEV(DEVICE_MAJOR, 0), \ + S_IFCHR | VBOX_DEV_FMASK, \ + DEVICE_NAME) == 0) \ + rc = (void *)' '; /* return not NULL */ \ + rc; \ + }) + +# define VBOX_UNREGISTER_DEVFS(handle) \ + devfs_remove(DEVICE_NAME); + +# else /* < 2.6.0 */ + +# define VBOX_REGISTER_DEVFS() \ + devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, \ + DEVICE_MAJOR, 0, \ + S_IFCHR | VBOX_DEV_FMASK, \ + &gFileOpsVBoxDrv, NULL) + +# define VBOX_UNREGISTER_DEVFS(handle) \ + if (handle != NULL) \ + devfs_unregister(handle) + +# endif /* < 2.6.0 */ +#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */ + +#ifndef CONFIG_VBOXDRV_AS_MISC +# if defined(CONFIG_DEVFS_FS) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 0) +# define VBOX_REGISTER_DEVICE(a,b,c) devfs_register_chrdev(a,b,c) +# define VBOX_UNREGISTER_DEVICE(a,b) devfs_unregister_chrdev(a,b) +# else +# define VBOX_REGISTER_DEVICE(a,b,c) register_chrdev(a,b,c) +# define VBOX_UNREGISTER_DEVICE(a,b) unregister_chrdev(a,b) +# endif +#endif /* !CONFIG_VBOXDRV_AS_MISC */ + + +#ifdef CONFIG_X86_HIGH_ENTRY +# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time." +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + +/* If an NMI occurs while we are inside the world switcher the machine will + * crash. The Linux NMI watchdog generates periodic NMIs increasing a counter + * which is compared with another counter increased in the timer interrupt + * handler. We disable the NMI watchdog. + * + * - Linux >= 2.6.21: The watchdog is disabled by default on i386 and x86_64. + * - Linux < 2.6.21: The watchdog is normally enabled by default on x86_64 + * and disabled on i386. + */ +# if defined(RT_ARCH_AMD64) +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) && !defined(VBOX_REDHAT_KABI) +# define DO_DISABLE_NMI 1 +# endif +# endif + +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +extern int nmi_active; +# define nmi_atomic_read(P) *(P) +# define nmi_atomic_set(P, V) *(P) = (V) +# define nmi_atomic_dec(P) nmi_atomic_set(P, 0) +# else +# define nmi_atomic_read(P) atomic_read(P) +# define nmi_atomic_set(P, V) atomic_set(P, V) +# define nmi_atomic_dec(P) atomic_dec(P) +# endif + +# ifndef X86_FEATURE_ARCH_PERFMON +# define X86_FEATURE_ARCH_PERFMON (3*32+9) /* Intel Architectural PerfMon */ +# endif +# ifndef MSR_ARCH_PERFMON_EVENTSEL0 +# define MSR_ARCH_PERFMON_EVENTSEL0 0x186 +# endif +# ifndef ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT +# define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT (1 << 0) +# endif + +#endif /* CONFIG_X86_LOCAL_APIC */ + +#define xstr(s) str(s) +#define str(s) #s + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Device extention & session data association structure. + */ +static SUPDRVDEVEXT g_DevExt; + +/** Registered devfs device handle. */ +#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +static void *g_hDevFsVBoxDrv = NULL; +# else +static devfs_handle_t g_hDevFsVBoxDrv = NULL; +# endif +#endif + +#ifndef CONFIG_VBOXDRV_AS_MISC +/** Module major number */ +#define DEVICE_MAJOR 234 +/** Saved major device number */ +static int g_iModuleMajor; +#endif /* !CONFIG_VBOXDRV_AS_MISC */ + +/** Module parameter. + * Not prefixed because the name is used by macros and the end of this file. */ +static int force_async_tsc = 0; + +/** The module name. */ +#define DEVICE_NAME "vboxdrv" + +#ifdef RT_ARCH_AMD64 +/** + * Memory for the executable memory heap (in IPRT). + */ +extern uint8_t g_abExecMemory[1572864]; /* 1.5 MB */ +__asm__(".section execmemory, \"awx\", @progbits\n\t" + ".align 32\n\t" + ".globl g_abExecMemory\n" + "g_abExecMemory:\n\t" + ".zero 1572864\n\t" + ".type g_abExecMemory, @object\n\t" + ".size g_abExecMemory, 1572864\n\t" + ".text\n\t"); +#endif + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int VBoxDrvLinuxInit(void); +static void VBoxDrvLinuxUnload(void); +static int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp); +static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp); +#ifdef HAVE_UNLOCKED_IOCTL +static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg); +#else +static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg); +#endif +static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg); +static int VBoxDrvLinuxErr2LinuxErr(int); +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION +static int VBoxDrvProbe(struct platform_device *pDev); +static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State); +static int VBoxDrvResume(struct platform_device *pDev); +static void VBoxDevRelease(struct device *pDev); +#endif + +/** The file_operations structure. */ +static struct file_operations gFileOpsVBoxDrv = +{ + owner: THIS_MODULE, + open: VBoxDrvLinuxCreate, + release: VBoxDrvLinuxClose, +#ifdef HAVE_UNLOCKED_IOCTL + unlocked_ioctl: VBoxDrvLinuxIOCtl, +#else + ioctl: VBoxDrvLinuxIOCtl, +#endif +}; + +#ifdef CONFIG_VBOXDRV_AS_MISC +/** The miscdevice structure. */ +static struct miscdevice gMiscDevice = +{ + minor: MISC_DYNAMIC_MINOR, + name: DEVICE_NAME, + fops: &gFileOpsVBoxDrv, +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && \ + LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17) + devfs_name: DEVICE_NAME, +# endif +}; +#endif + + +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION +static struct platform_driver gPlatformDriver = +{ + .probe = VBoxDrvProbe, + .suspend = VBoxDrvSuspend, + .resume = VBoxDrvResume, + /** @todo .shutdown? */ + .driver = + { + .name = "vboxdrv" + } +}; + +static struct platform_device gPlatformDevice = +{ + .name = "vboxdrv", + .dev = + { + .release = VBoxDevRelease + } +}; +#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */ + + +#ifdef CONFIG_X86_LOCAL_APIC +# ifdef DO_DISABLE_NMI +/** Stop AMD NMI watchdog (x86_64 only). */ +static int vboxdrvStopK7Watchdog(void) +{ + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + return 1; +} + +/** Stop Intel P4 NMI watchdog (x86_64 only). */ +static int vboxdrvStopP4Watchdog(void) +{ + wrmsr(MSR_P4_IQ_CCCR0, 0, 0); + wrmsr(MSR_P4_IQ_CCCR1, 0, 0); + wrmsr(MSR_P4_CRU_ESCR0, 0, 0); + return 1; +} + +/** The new method of detecting the event counter */ +static int vboxdrvStopIntelArchWatchdog(void) +{ + unsigned ebx; + + ebx = cpuid_ebx(10); + if (!(ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT)) + wrmsr(MSR_ARCH_PERFMON_EVENTSEL0, 0, 0); + return 1; +} + +/** Stop NMI watchdog. */ +static void vboxdrvStopApicNmiWatchdog(void *unused) +{ + int stopped = 0; + + /* only support LOCAL and IO APICs for now */ + if ((nmi_watchdog != NMI_LOCAL_APIC) && + (nmi_watchdog != NMI_IO_APIC)) + return; + + if (nmi_watchdog == NMI_LOCAL_APIC) + { + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_AMD: + if (strstr(boot_cpu_data.x86_model_id, "Screwdriver")) + return; + stopped = vboxdrvStopK7Watchdog(); + break; + case X86_VENDOR_INTEL: + if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) + { + stopped = vboxdrvStopIntelArchWatchdog(); + break; + } + stopped = vboxdrvStopP4Watchdog(); + break; + default: + return; + } + } + + if (stopped) + nmi_atomic_dec(&nmi_active); +} + +/** Disable LAPIC NMI watchdog. */ +static void DisableLapicNmiWatchdog(void) +{ + BUG_ON(nmi_watchdog != NMI_LOCAL_APIC); + + if (nmi_atomic_read(&nmi_active) <= 0) + return; + + on_each_cpu(vboxdrvStopApicNmiWatchdog, NULL, 1, 1); + + BUG_ON(nmi_atomic_read(&nmi_active) != 0); + + /* tell do_nmi() and others that we're not active any more */ + nmi_watchdog = NMI_NONE; +} + +/** Shutdown NMI. */ +static void vboxdrvNmiCpuShutdown(void * dummy) +{ + unsigned int vERR, vPC; + + vPC = apic_read(APIC_LVTPC); + + if ((GET_APIC_DELIVERY_MODE(vPC) == APIC_MODE_NMI) && !(vPC & APIC_LVT_MASKED)) + { + vERR = apic_read(APIC_LVTERR); + apic_write(APIC_LVTERR, vERR | APIC_LVT_MASKED); + apic_write(APIC_LVTPC, vPC | APIC_LVT_MASKED); + apic_write(APIC_LVTERR, vERR); + } +} + +static void vboxdrvNmiShutdown(void) +{ + on_each_cpu(vboxdrvNmiCpuShutdown, NULL, 0, 1); +} +# endif /* DO_DISABLE_NMI */ +#endif /* CONFIG_X86_LOCAL_APIC */ + + +DECLINLINE(RTUID) vboxdrvLinuxUid(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + return current->cred->uid; +#else + return current->uid; +#endif +} + +DECLINLINE(RTGID) vboxdrvLinuxGid(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + return current->cred->gid; +#else + return current->gid; +#endif +} + +DECLINLINE(RTUID) vboxdrvLinuxEuid(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + return current->cred->euid; +#else + return current->euid; +#endif +} + +/** + * Initialize module. + * + * @returns appropriate status code. + */ +static int __init VBoxDrvLinuxInit(void) +{ + int rc; + + dprintf(("VBoxDrv::ModuleInit\n")); + +#ifdef CONFIG_X86_LOCAL_APIC + /* + * If an NMI occurs while we are inside the world switcher the macine will crash. + * The Linux NMI watchdog generates periodic NMIs increasing a counter which is + * compared with another counter increased in the timer interrupt handler. Therefore + * we don't allow to setup an NMI watchdog. + */ +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && !defined(VBOX_REDHAT_KABI) + /* + * First test: NMI actiated? Works only works with Linux 2.6 -- 2.4 does not export + * the nmi_watchdog variable. + */ +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || \ + (defined CONFIG_X86_64 && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +# ifdef DO_DISABLE_NMI + if (nmi_atomic_read(&nmi_active) > 0) + { + printk(KERN_DEBUG DEVICE_NAME ": Trying to deactivate the NMI watchdog...\n"); + + switch (nmi_watchdog) + { + case NMI_LOCAL_APIC: + DisableLapicNmiWatchdog(); + break; + case NMI_NONE: + nmi_atomic_dec(&nmi_active); + break; + } + + if (nmi_atomic_read(&nmi_active) == 0) + { + vboxdrvNmiShutdown(); + printk(KERN_DEBUG DEVICE_NAME ": Successfully done.\n"); + } + else + printk(KERN_DEBUG DEVICE_NAME ": Failed!\n"); + } +# endif /* DO_DISABLE_NMI */ + + /* + * Permanent IO_APIC mode active? No way to handle this! + */ + if (nmi_watchdog == NMI_IO_APIC) + { + printk(KERN_ERR DEVICE_NAME + ": NMI watchdog in IO_APIC mode active -- refused to load the kernel module!\n" + DEVICE_NAME + ": Please disable the NMI watchdog by specifying 'nmi_watchdog=0' at kernel\n" + DEVICE_NAME + ": command line.\n"); + return -EINVAL; + } + + /* + * See arch/i386/kernel/nmi.c on >= 2.6.19: -1 means it can never enabled again + */ + nmi_atomic_set(&nmi_active, -1); + printk(KERN_DEBUG DEVICE_NAME ": Trying to deactivate the NMI watchdog permanently...\n"); + + /* + * Now fall through and see if it actually was enabled before. If so, fail + * as we cannot deactivate it cleanly from here. + */ +# else /* < 2.6.19 */ + /* + * Older 2.6 kernels: nmi_watchdog is not initalized by default + */ + if (nmi_watchdog != NMI_NONE) + goto nmi_activated; +# endif +# endif /* >= 2.6.0 && !defined(VBOX_REDHAT_KABI) */ + + /* + * Second test: Interrupt generated by performance counter not masked and can + * generate an NMI. Works also with Linux 2.4. + */ + { + unsigned int v, ver, maxlvt; + + v = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(v); + /* 82489DXs do not report # of LVT entries. */ + maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2; + if (maxlvt >= 4) + { + /* Read status of performance counter IRQ vector */ + v = apic_read(APIC_LVTPC); + + /* performance counter generates NMI and is not masked? */ + if ((GET_APIC_DELIVERY_MODE(v) == APIC_MODE_NMI) && !(v & APIC_LVT_MASKED)) + { +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || \ + (defined CONFIG_X86_64 && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + printk(KERN_ERR DEVICE_NAME + ": NMI watchdog either active or at least initialized. Please disable the NMI\n" + DEVICE_NAME + ": watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n"); + return -EINVAL; +# else /* < 2.6.19 */ +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && !defined(VBOX_REDHAT_KABI) +nmi_activated: +# endif + printk(KERN_ERR DEVICE_NAME + ": NMI watchdog active -- refused to load the kernel module! Please disable\n" + DEVICE_NAME + ": the NMI watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n"); + return -EINVAL; +# endif /* >= 2.6.19 */ + } + } + } +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + printk(KERN_DEBUG DEVICE_NAME ": Successfully done.\n"); +# endif /* >= 2.6.19 */ +#endif /* CONFIG_X86_LOCAL_APIC */ + + /* + * Check for synchronous/asynchronous TSC mode. + */ + printk(KERN_DEBUG DEVICE_NAME ": Found %u processor cores.\n", (unsigned)RTMpGetOnlineCount()); +#ifdef CONFIG_VBOXDRV_AS_MISC + rc = misc_register(&gMiscDevice); + if (rc) + { + printk(KERN_ERR DEVICE_NAME ": Can't register misc device! rc=%d\n", rc); + return rc; + } +#else /* !CONFIG_VBOXDRV_AS_MISC */ + /* + * Register character device. + */ + g_iModuleMajor = DEVICE_MAJOR; + rc = VBOX_REGISTER_DEVICE((dev_t)g_iModuleMajor, DEVICE_NAME, &gFileOpsVBoxDrv); + if (rc < 0) + { + dprintf(("VBOX_REGISTER_DEVICE failed with rc=%#x!\n", rc)); + return rc; + } + + /* + * Save returned module major number + */ + if (DEVICE_MAJOR != 0) + g_iModuleMajor = DEVICE_MAJOR; + else + g_iModuleMajor = rc; + rc = 0; + +#ifdef CONFIG_DEVFS_FS + /* + * Register a device entry + */ + g_hDevFsVBoxDrv = VBOX_REGISTER_DEVFS(); + if (g_hDevFsVBoxDrv == NULL) + { + dprintf(("devfs_register failed!\n")); + rc = -EINVAL; + } +#endif +#endif /* !CONFIG_VBOXDRV_AS_MISC */ + if (!rc) + { + /* + * Initialize the runtime. + * On AMD64 we'll have to donate the high rwx memory block to the exec allocator. + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { +#ifdef RT_ARCH_AMD64 + rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory)); + printk("VBoxDrv: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]); +#endif + /* + * Initialize the device extension. + */ + if (RT_SUCCESS(rc)) + rc = supdrvInitDevExt(&g_DevExt); + if (RT_SUCCESS(rc)) + { +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION + rc = platform_driver_register(&gPlatformDriver); + if (rc == 0) + { + rc = platform_device_register(&gPlatformDevice); + if (rc == 0) +#endif + { + printk(KERN_INFO DEVICE_NAME ": TSC mode is %s, kernel timer mode is " +#ifdef VBOX_HRTIMER + "'high-res'" +#else + "'normal'" +#endif + ".\n", + g_DevExt.pGip->u32Mode == SUPGIPMODE_SYNC_TSC ? "'synchronous'" : "'asynchronous'"); + LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc)); + printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version " + VBOX_VERSION_STRING " (interface " xstr(SUPDRV_IOC_VERSION) ").\n"); + return rc; + } +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION + else + platform_driver_unregister(&gPlatformDriver); + } +#endif + } + + rc = -EINVAL; + RTR0Term(); + } + else + rc = -EINVAL; + + /* + * Failed, cleanup and return the error code. + */ +#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC) + VBOX_UNREGISTER_DEVFS(g_hDevFsVBoxDrv); +#endif + } +#ifdef CONFIG_VBOXDRV_AS_MISC + misc_deregister(&gMiscDevice); + dprintf(("VBoxDrv::ModuleInit returning %#x (minor:%d)\n", rc, gMiscDevice.minor)); +#else + VBOX_UNREGISTER_DEVICE(g_iModuleMajor, DEVICE_NAME); + dprintf(("VBoxDrv::ModuleInit returning %#x (major:%d)\n", rc, g_iModuleMajor)); +#endif + return rc; +} + + +/** + * Unload the module. + */ +static void __exit VBoxDrvLinuxUnload(void) +{ + int rc; + dprintf(("VBoxDrvLinuxUnload\n")); + NOREF(rc); + +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION + platform_device_unregister(&gPlatformDevice); + platform_driver_unregister(&gPlatformDriver); +#endif + + /* + * I Don't think it's possible to unload a driver which processes have + * opened, at least we'll blindly assume that here. + */ +#ifdef CONFIG_VBOXDRV_AS_MISC + rc = misc_deregister(&gMiscDevice); + if (rc < 0) + { + dprintf(("misc_deregister failed with rc=%#x\n", rc)); + } +#else /* !CONFIG_VBOXDRV_AS_MISC */ +# ifdef CONFIG_DEVFS_FS + /* + * Unregister a device entry + */ + VBOX_UNREGISTER_DEVFS(g_hDevFsVBoxDrv); +# endif /* devfs */ + VBOX_UNREGISTER_DEVICE(g_iModuleMajor, DEVICE_NAME); +#endif /* !CONFIG_VBOXDRV_AS_MISC */ + + /* + * Destroy GIP, delete the device extension and terminate IPRT. + */ + supdrvDeleteDevExt(&g_DevExt); + RTR0Term(); +} + + +/** + * Device open. Called on open /dev/vboxdrv + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp) +{ + int rc; + PSUPDRVSESSION pSession; + Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm)); + +#ifdef VBOX_WITH_HARDENING + /* + * Only root is allowed to access the device, enforce it! + */ + if (vboxdrvLinuxEuid() != 0 /* root */ ) + { + Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuid())); + return -EPERM; + } +#endif /* VBOX_WITH_HARDENING */ + + /* + * Call common code for the rest. + */ + rc = supdrvCreateSession(&g_DevExt, true /* fUser */, (PSUPDRVSESSION *)&pSession); + if (!rc) + { + pSession->Uid = vboxdrvLinuxUid(); + pSession->Gid = vboxdrvLinuxGid(); + } + + pFilp->private_data = pSession; + + Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n", + &g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc), + RTProcSelf(), current->pid, current->comm)); + return VBoxDrvLinuxErr2LinuxErr(rc); +} + + +/** + * Close device. + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp) +{ + Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n", + pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm)); + supdrvCloseSession(&g_DevExt, (PSUPDRVSESSION)pFilp->private_data); + pFilp->private_data = NULL; + return 0; +} + + +#ifdef VBOX_WITH_SUSPEND_NOTIFICATION +/** + * Dummy device release function. We have to provide this function, + * otherwise the kernel will complain. + * + * @param pDev Pointer to the platform device. + */ +static void VBoxDevRelease(struct device *pDev) +{ +} + +/** + * Dummy probe function. + * + * @param pDev Pointer to the platform device. + */ +static int VBoxDrvProbe(struct platform_device *pDev) +{ + return 0; +} + +/** + * Suspend callback. + * @param pDev Pointer to the platform device. + * @param State message type, see Documentation/power/devices.txt. + */ +static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State) +{ + RTPowerSignalEvent(RTPOWEREVENT_SUSPEND); + return 0; +} + +/** + * Resume callback. + * + * @param pDev Pointer to the platform device. + */ +static int VBoxDrvResume(struct platform_device *pDev) +{ + RTPowerSignalEvent(RTPOWEREVENT_RESUME); + return 0; +} +#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */ + + +/** + * Device I/O Control entry point. + * + * @param pFilp Associated file pointer. + * @param uCmd The function specified to ioctl(). + * @param ulArg The argument specified to ioctl(). + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg) +#else +static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg) +#endif +{ + /* + * Deal with the two high-speed IOCtl that takes it's arguments from + * the session and iCmd, and only returns a VBox status code. + */ +#ifdef HAVE_UNLOCKED_IOCTL + if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || uCmd == SUP_IOCTL_FAST_DO_NOP)) + return supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data); + return VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg); + +#else /* !HAVE_UNLOCKED_IOCTL */ + + int rc; + unlock_kernel(); + if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || uCmd == SUP_IOCTL_FAST_DO_NOP)) + rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data); + else + rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg); + lock_kernel(); + return rc; +#endif /* !HAVE_UNLOCKED_IOCTL */ +} + + +/** + * Device I/O Control entry point. + * + * @param pFilp Associated file pointer. + * @param uCmd The function specified to ioctl(). + * @param ulArg The argument specified to ioctl(). + */ +static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg) +{ + int rc; + SUPREQHDR Hdr; + PSUPREQHDR pHdr; + uint32_t cbBuf; + + Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid)); + + /* + * Read the header. + */ + if (RT_UNLIKELY(copy_from_user(&Hdr, (void *)ulArg, sizeof(Hdr)))) + { + Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd)); + return -EFAULT; + } + if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd)); + return -EINVAL; + } + + /* + * Buffer the request. + */ + cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY(cbBuf > _1M*16)) + { + Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd)); + return -E2BIG; + } + if (RT_UNLIKELY(cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd))) + { + Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", cbBuf, _IOC_SIZE(uCmd), uCmd)); + return -EINVAL; + } + pHdr = RTMemAlloc(cbBuf); + if (RT_UNLIKELY(!pHdr)) + { + OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x.\n", cbBuf, uCmd)); + return -ENOMEM; + } + if (RT_UNLIKELY(copy_from_user(pHdr, (void *)ulArg, Hdr.cbIn))) + { + Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, Hdr.cbIn, uCmd)); + RTMemFree(pHdr); + return -EFAULT; + } + + /* + * Process the IOCtl. + */ + rc = supdrvIOCtl(uCmd, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data, pHdr); + + /* + * Copy ioctl data and output buffer back to user space. + */ + if (RT_LIKELY(!rc)) + { + uint32_t cbOut = pHdr->cbOut; + if (RT_UNLIKELY(cbOut > cbBuf)) + { + OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd)); + cbOut = cbBuf; + } + if (RT_UNLIKELY(copy_to_user((void *)ulArg, pHdr, cbOut))) + { + /* this is really bad! */ + OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd)); + rc = -EFAULT; + } + } + else + { + Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc)); + rc = -EINVAL; + } + RTMemFree(pHdr); + + Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid)); + return rc; +} + + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code, see supdrvIDC. + * @param iReq The request code. + * @param pReq The request. + */ +int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + PSUPDRVSESSION pSession; + + /* + * Some quick validations. + */ + if (RT_UNLIKELY(!VALID_PTR(pReq))) + return VERR_INVALID_POINTER; + + pSession = pReq->pSession; + if (pSession) + { + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return VERR_INVALID_PARAMETER; + if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt)) + return VERR_INVALID_PARAMETER; + } + else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT)) + return VERR_INVALID_PARAMETER; + + /* + * Do the job. + */ + return supdrvIDC(uReq, &g_DevExt, pSession, pReq); +} + +EXPORT_SYMBOL(SUPDrvLinuxIDC); + + +/** + * Initializes any OS specific object creator fields. + */ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +/** + * Checks if the session can access the object. + * + * @returns true if a decision has been made. + * @returns false if the default access policy should be applied. + * + * @param pObj The object in question. + * @param pSession The session wanting to access the object. + * @param pszObjName The object name, can be NULL. + * @param prc Where to store the result when returning true. + */ +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + return force_async_tsc != 0; +} + + +/** + * Converts a supdrv error code to an linux error code. + * + * @returns corresponding linux error code. + * @param rc supdrv error code (SUPDRV_ERR_* defines). + */ +static int VBoxDrvLinuxErr2LinuxErr(int rc) +{ + switch (rc) + { + case 0: return 0; + case SUPDRV_ERR_GENERAL_FAILURE: return -EACCES; + case SUPDRV_ERR_INVALID_PARAM: return -EINVAL; + case SUPDRV_ERR_INVALID_MAGIC: return -EILSEQ; + case SUPDRV_ERR_INVALID_HANDLE: return -ENXIO; + case SUPDRV_ERR_INVALID_POINTER: return -EFAULT; + case SUPDRV_ERR_LOCK_FAILED: return -ENOLCK; + case SUPDRV_ERR_ALREADY_LOADED: return -EEXIST; + case SUPDRV_ERR_PERMISSION_DENIED: return -EPERM; + case SUPDRV_ERR_VERSION_MISMATCH: return -ENOSYS; + case SUPDRV_ERR_IDT_FAILED: return -1000; + } + + return -EPERM; +} + + +RTDECL(int) SUPR0Printf(const char *pszFormat, ...) +{ +#if 1 + va_list args; + char szMsg[512]; + + va_start(args, pszFormat); + vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args); + szMsg[sizeof(szMsg) - 1] = '\0'; + printk("%s", szMsg); + va_end(args); +#else + /* forward to printf - needs some more GCC hacking to fix ebp... */ + __asm__ __volatile__ ("mov %0, %esp\n\t" + "jmp %1\n\t", + :: "r" ((uintptr_t)&pszFormat - 4), + "m" (printk)); +#endif + return 0; +} + +module_init(VBoxDrvLinuxInit); +module_exit(VBoxDrvLinuxUnload); + +MODULE_AUTHOR("Sun Microsystems, Inc."); +MODULE_DESCRIPTION("VirtualBox Support Driver"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +MODULE_VERSION(VBOX_VERSION_STRING " (" xstr(SUPDRV_IOC_VERSION) ")"); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +module_param(force_async_tsc, int, 0444); +#else +MODULE_PARM(force_async_tsc, "i"); +#endif +MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode"); + diff --git a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c new file mode 100644 index 000000000..40a54909a --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.mod.c @@ -0,0 +1,94 @@ +/* $Id: SUPDrv-linux.mod.c 10662 2008-07-15 14:36:00Z vboxsync $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - Autogenerated Linux code. + * This is checked in to assist syntax checking the module. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#include "SUPDrvInternal.h" /* for KBUILD_STR */ +#include "the-linux-kernel.h" +#include <linux/vermagic.h> + +MODULE_INFO(vermagic, VERMAGIC_STRING); + +#undef unix +struct module __this_module +__attribute__((section(".gnu.linkonce.this_module"))) = { + .name = __stringify(KBUILD_MODNAME), + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif +}; + +static const struct modversion_info ____versions[] +__attribute_used__ +__attribute__((section("__versions"))) = { + { 0, "cleanup_module" }, + { 0, "init_module" }, + { 0, "struct_module" }, + { 0, "devfs_remove" }, + { 0, "strpbrk" }, + { 0, "__kmalloc" }, + { 0, "mem_map" }, + { 0, "vmalloc" }, + { 0, "malloc_sizes" }, + { 0, "vfree" }, + { 0, "change_page_attr" }, + { 0, "__might_sleep" }, + { 0, "remap_page_range" }, + { 0, "__alloc_pages" }, + { 0, "printk" }, + { 0, "__PAGE_KERNEL" }, + { 0, "rwsem_wake" }, + { 0, "copy_to_user" }, + { 0, "devfs_mk_cdev" }, + { 0, "preempt_schedule" }, + { 0, "contig_page_data" }, + { 0, "do_mmap_pgoff" }, + { 0, "find_vma" }, + { 0, "kmem_cache_alloc" }, + { 0, "__free_pages" }, + { 0, "do_munmap" }, + { 0, "get_user_pages" }, + { 0, "register_chrdev" }, + { 0, "vsnprintf" }, + { 0, "kfree" }, + { 0, "memcpy" }, + { 0, "unregister_chrdev" }, + { 0, "put_page" }, + { 0, "__up_wakeup" }, + { 0, "__down_failed" }, + { 0, "copy_from_user" }, + { 0, "rwsem_down_read_failed" }, +}; + +static const char __module_depends[] +__attribute_used__ +__attribute__((section(".modinfo"))) = +"depends="; + diff --git a/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp new file mode 100644 index 000000000..c02beae38 --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp @@ -0,0 +1,255 @@ +/* $Id: SUPLib-linux.cpp 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VirtualBox Support Library - GNU/Linux specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <malloc.h> + +#include <VBox/log.h> +#include <VBox/sup.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <VBox/types.h> +#include <iprt/string.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Unix Device name. */ +#define DEVICE_NAME "/dev/vboxdrv" + +/* define MADV_DONTFORK if it's missing from the system headers. */ +#ifndef MADV_DONTFORK +# define MADV_DONTFORK 10 +#endif + + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + Assert(pThis->hDevice == NIL_RTFILE); + + /* + * Check if madvise works. + */ + void *pv = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (pv == MAP_FAILED) + return VERR_NO_MEMORY; + pThis->fSysMadviseWorks = (0 == madvise(pv, PAGE_SIZE, MADV_DONTFORK)); + munmap(pv, PAGE_SIZE); + + /* + * Try open the device. + */ + int hDevice = open(DEVICE_NAME, O_RDWR, 0); + if (hDevice < 0) + { + /* + * Try load the device. + */ + hDevice = open(DEVICE_NAME, O_RDWR, 0); + if (hDevice < 0) + { + int rc; + switch (errno) + { + case ENXIO: /* see man 2 open, ENODEV is actually a kernel bug */ + case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break; + case EPERM: + case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break; + case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: rc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", DEVICE_NAME, errno, rc)); + return rc; + } + } + + /* + * Mark the file handle close on exec. + */ + if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) == -1) + { + close(hDevice); +#ifdef IN_SUP_HARDENED_R3 + return VERR_INTERNAL_ERROR; +#else + return RTErrConvertFromErrno(errno); +#endif + } + + /* + * We're done. + */ + pThis->hDevice = hDevice; + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Close the device if it's actually open. + */ + if (pThis->hDevice != NIL_RTFILE) + { + if (close(pThis->hDevice)) + AssertFailed(); + pThis->hDevice = NIL_RTFILE; + } + + return 0; +} + + +int suplibOsInstall(void) +{ + // nothing to do on Linux + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsUninstall(void) +{ + // nothing to do on Linux + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + AssertMsg(pThis->hDevice != NIL_RTFILE, ("SUPLIB not initiated successfully!\n")); + + /* + * Issue device iocontrol. + */ + if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0)) + return VINF_SUCCESS; + + /* This is the reverse operation of the one found in SUPDrv-linux.c */ + switch (errno) + { + case EACCES: return VERR_GENERAL_FAILURE; + case EINVAL: return VERR_INVALID_PARAMETER; + case EILSEQ: return VERR_INVALID_MAGIC; + case ENXIO: return VERR_INVALID_HANDLE; + case EFAULT: return VERR_INVALID_POINTER; + case ENOLCK: return VERR_LOCK_FAILED; + case EEXIST: return VERR_ALREADY_LOADED; + case EPERM: return VERR_PERMISSION_DENIED; + case ENOSYS: return VERR_VERSION_MISMATCH; + case 1000: return VERR_IDT_FAILED; + } + + return RTErrConvertFromErrno(errno); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + int rc = ioctl(pThis->hDevice, uFunction, idCpu); + if (rc == -1) + rc = -errno; + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + size_t cbMmap = (pThis->fSysMadviseWorks ? cPages : cPages + 2) << PAGE_SHIFT; + char *pvPages = (char *)mmap(NULL, cbMmap, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (pvPages == MAP_FAILED) + return VERR_NO_MEMORY; + + if (pThis->fSysMadviseWorks) + { + /* + * It is not fatal if we fail here but a forked child (e.g. the ALSA sound server) + * could crash. Linux < 2.6.16 does not implement madvise(MADV_DONTFORK) but the + * kernel seems to split bigger VMAs and that is all that we want -- later we set the + * VM_DONTCOPY attribute in supdrvOSLockMemOne(). + */ + if (madvise (pvPages, cbMmap, MADV_DONTFORK)) + LogRel(("SUPLib: madvise %p-%p failed\n", pvPages, cbMmap)); + *ppvPages = pvPages; + } + else + { + /* + * madvise(MADV_DONTFORK) is not available (most probably Linux 2.4). Enclose any + * mmapped region by two unmapped pages to guarantee that there is exactly one VM + * area struct of the very same size as the mmap area. + */ + mprotect(pvPages, PAGE_SIZE, PROT_NONE); + mprotect(pvPages + cbMmap - PAGE_SIZE, PAGE_SIZE, PROT_NONE); + *ppvPages = pvPages + PAGE_SIZE; + } + memset(*ppvPages, 0, cPages << PAGE_SHIFT); + return VINF_SUCCESS; +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages) +{ + munmap(pvPages, cPages << PAGE_SHIFT); + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c b/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c new file mode 100644 index 000000000..90abb0a92 --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c @@ -0,0 +1,59 @@ +/* $Id: SUPR0IdcClient-linux.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Linux Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq); +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + int rc = SUPDrvLinuxIDC(iReq, pReq); + if (RT_SUCCESS(rc)) + rc = pReq->rc; + + NOREF(pHandle); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/linux/dkms.conf b/src/VBox/HostDrivers/Support/linux/dkms.conf new file mode 100644 index 000000000..5adc5d03f --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/dkms.conf @@ -0,0 +1,6 @@ +BUILT_MODULE_NAME=vboxdrv +DEST_MODULE_LOCATION=/kernel/misc +PACKAGE_NAME=vboxdrv +PACKAGE_VERSION=_VERSION_ +AUTOINSTALL=yes +POST_BUILD="do_Module.symvers vboxdrv save $dkms_tree/$module/$module_version/build/Module.symvers" diff --git a/src/VBox/HostDrivers/Support/linux/files_vboxdrv b/src/VBox/HostDrivers/Support/linux/files_vboxdrv new file mode 100644 index 000000000..bca75a850 --- /dev/null +++ b/src/VBox/HostDrivers/Support/linux/files_vboxdrv @@ -0,0 +1,129 @@ +#!/bin/sh +# +# Shared file between Makefile.kmk and export_modules +# +# Copyright (C) 2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +FILES_VBOXDRV_NOBIN=" \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \ + ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \ + ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \ + ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \ + ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \ + ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \ + ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \ + ${PATH_ROOT}/include/iprt/process.h=>include/iprt/process.h \ + ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \ + ${PATH_ROOT}/include/iprt/spinlock.h=>include/iprt/spinlock.h \ + ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \ + ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \ + ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \ + ${PATH_ROOT}/include/iprt/thread.h=>include/iprt/thread.h \ + ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \ + ${PATH_ROOT}/include/iprt/timer.h=>include/iprt/timer.h \ + ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \ + ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \ + ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \ + ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \ + ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \ + ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c=>linux/SUPDrv-linux.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrv.c=>SUPDrv.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIOC.h=>SUPDrvIOC.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvInternal.h=>SUPDrvInternal.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/alloc/heapsimple.cpp=>alloc/heapsimple.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertFromErrno.cpp=>common/err/RTErrConvertFromErrno.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/log.cpp=>common/log/log.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logellipsis.cpp=>common/log/logellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logrel.cpp=>common/log/logrel.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logrelellipsis.cpp=>common/log/logrelellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logformat.cpp=>common/log/logformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/initterm.h=>include/internal/initterm.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/magics.h=>include/internal/magics.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/memobj.h=>include/internal/memobj.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/string.h=>include/internal/string.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/thread.h=>include/internal/thread.h \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp=>generic/RTAssertShouldPanic-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp=>generic/RTLogWriteStdErr-stub-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp=>generic/RTLogWriteStdOut-stub-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp=>generic/RTLogWriteUser-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.cpp=>r0drv/alloc-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.h=>r0drv/alloc-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/initterm-r0drv.cpp=>r0drv/initterm-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/mp-r0drv.h=>r0drv/mp-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/mpnotification-r0drv.c=>r0drv/mpnotification-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/power-r0drv.h=>r0drv/power-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/powernotification-r0drv.c=>r0drv/powernotification-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c=>r0drv/linux/RTLogWriteDebugger-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c=>r0drv/linux/assert-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c=>r0drv/linux/alloc-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c=>r0drv/linux/initterm-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c=>r0drv/linux/memobj-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c=>r0drv/linux/mp-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c=>r0drv/linux/mpnotification-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c=>r0drv/linux/process-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c=>r0drv/linux/semevent-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c=>r0drv/linux/semeventmulti-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c=>r0drv/linux/semfastmutex-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c=>r0drv/linux/spinlock-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/string.h=>r0drv/linux/string.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c=>r0drv/linux/thread-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c=>r0drv/linux/thread2-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c=>r0drv/linux/time-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/timer-r0drv-linux.c=>r0drv/linux/timer-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/memobj-r0drv.cpp=>r0drv/memobj-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/log-vbox.cpp=>VBox/log-vbox.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/strformat-vbox.cpp=>VBox/strformat-vbox.c \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ +" + +FILES_VBOXDRV_BIN=" \ +" diff --git a/src/VBox/HostDrivers/Support/os2/Makefile.kup b/src/VBox/HostDrivers/Support/os2/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp new file mode 100644 index 000000000..86fdd350c --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp @@ -0,0 +1,425 @@ +/* $Id: SUPDrv-os2.cpp 13998 2008-11-10 12:08:56Z vboxsync $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - OS/2 specifics. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#define __STDC_CONSTANT_MACROS +#define __STDC_LIMIT_MACROS + +#include <os2ddk/bsekee.h> +#undef RT_MAX + +#include "SUPDrvInternal.h" +#include <VBox/version.h> +#include <iprt/initterm.h> +#include <iprt/string.h> +#include <iprt/spinlock.h> +#include <iprt/process.h> +#include <iprt/assert.h> +#include <VBox/log.h> +#include <iprt/param.h> + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Device extention & session data association structure. + */ +static SUPDRVDEVEXT g_DevExt; +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PSUPDRVSESSION g_apSessionHashTab[19]; +/** Calculates the index into g_apSessionHashTab.*/ +#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab)) + +__BEGIN_DECLS +/* Defined in SUPDrvA-os2.asm */ +extern uint16_t g_offLogHead; +extern uint16_t volatile g_offLogTail; +extern uint16_t const g_cchLogMax; +extern char g_szLog[]; +/* (init only:) */ +extern char g_szInitText[]; +extern uint16_t g_cchInitText; +extern uint16_t g_cchInitTextMax; +__END_DECLS + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ + + + +/** + * 32-bit Ring-0 initialization. + * + * @returns 0 on success, non-zero on failure. + * @param pszArgs Pointer to the device arguments. + */ +DECLASM(int) VBoxDrvInit(const char *pszArgs) +{ + dprintf(("VBoxDrvInit: pszArgs=%s\n", pszArgs)); + + /* + * Initialize the runtime. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + rc = supdrvInitDevExt(&g_DevExt); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the session hash table. + */ + rc = RTSpinlockCreate(&g_Spinlock); + if (RT_SUCCESS(rc)) + { + /* + * Process the commandline. Later. + */ + bool fVerbose = true; + + /* + * Success + */ + if (fVerbose) + { + strcpy(&g_szInitText[0], + "\r\n" + "VirtualBox.org Support Driver for OS/2 version " VBOX_VERSION_STRING "\r\n" + "Copyright (C) 2007 Knut St. Osmundsen\r\n" + "Copyright (C) 2007 Sun Microsystems, Inc.\r\n"); + g_cchInitText = strlen(&g_szInitText[0]); + } + return VINF_SUCCESS; + } + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTSpinlockCreate failed, rc=%Rrc\n", rc); + supdrvDeleteDevExt(&g_DevExt); + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: supdrvInitDevExt failed, rc=%Rrc\n", rc); + RTR0Term(); + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTR0Init failed, rc=%Rrc\n", rc); + return rc; +} + + +DECLASM(int) VBoxDrvOpen(uint16_t sfn) +{ + int rc; + PSUPDRVSESSION pSession; + + /* + * Create a new session. + */ + rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &pSession); + if (RT_SUCCESS(rc)) + { + pSession->sfn = sfn; + + /* + * Insert it into the hash table. + */ + unsigned iHash = SESSION_HASH(sfn); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = pSession; + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + } + + dprintf(("VBoxDrvOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf())); + return rc; +} + + +DECLASM(int) VBoxDrvClose(uint16_t sfn) +{ + dprintf(("VBoxDrvClose: pid=%d sfn=%d\n", (int)RTProcSelf(), sfn)); + + /* + * Remove from the hash table. + */ + PSUPDRVSESSION pSession; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + + pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if ( pSession->sfn == sfn + && pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + } + else + { + PSUPDRVSESSION pPrev = pSession; + pSession = pSession->pNextHash; + while (pSession) + { + if ( pSession->sfn == sfn + && pSession->Process == Process) + { + pPrev->pNextHash = pSession->pNextHash; + pSession->pNextHash = NULL; + break; + } + + /* next */ + pPrev = pSession; + pSession = pSession->pNextHash; + } + } + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d sfn=%d\n", (int)Process, sfn)); + return VERR_INVALID_PARAMETER; + } + + /* + * Close the session. + */ + supdrvCloseSession(&g_DevExt, pSession); + return 0; +} + + +DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction) +{ + /* + * Find the session. + */ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + PSUPDRVSESSION pSession; + + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while ( pSession + && ( pSession->sfn != sfn + || pSession->Process != Process)); + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (RT_UNLIKELY(!pSession)) + { + OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); + return VERR_INVALID_PARAMETER; + } + + /* + * Dispatch the fast IOCtl. + */ + supdrvIOCtlFast(iFunction, 0, &g_DevExt, pSession); + return 0; +} + + +DECLASM(int) VBoxDrvIOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *pvParm, void *pvData, uint16_t *pcbParm, uint16_t *pcbData) +{ + /* + * Find the session. + */ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + PSUPDRVSESSION pSession; + + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while ( pSession + && ( pSession->sfn != sfn + || pSession->Process != Process)); + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); + return VERR_INVALID_PARAMETER; + } + + /* + * Verify the category and dispatch the IOCtl. + */ + if (RT_LIKELY(iCat == SUP_CTL_CATEGORY)) + { + dprintf(("VBoxDrvIOCtl: pSession=%p iFunction=%#x pvParm=%p pvData=%p *pcbParm=%d *pcbData=%d\n", pSession, iFunction, pvParm, pvData, *pcbParm, *pcbData)); + Assert(pvParm); + Assert(!pvData); + + /* + * Lock the header. + */ + PSUPREQHDR pHdr = (PSUPREQHDR)pvParm; + AssertReturn(*pcbParm == sizeof(*pHdr), VERR_INVALID_PARAMETER); + KernVMLock_t Lock; + int rc = KernVMLock(VMDHL_WRITE, pHdr, *pcbParm, &Lock, (KernPageList_t *)-1, NULL); + AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, *pcbParm, &Lock, rc), VERR_LOCK_FAILED); + + /* + * Validate the header. + */ + if (RT_LIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) == SUPREQHDR_FLAGS_MAGIC)) + { + uint32_t cbReq = RT_MAX(pHdr->cbIn, pHdr->cbOut); + if (RT_LIKELY( pHdr->cbIn >= sizeof(*pHdr) + && pHdr->cbOut >= sizeof(*pHdr) + && cbReq <= _1M*16)) + { + /* + * Lock the rest of the buffer if necessary. + */ + if (((uintptr_t)pHdr & PAGE_OFFSET_MASK) + cbReq > PAGE_SIZE) + { + rc = KernVMUnlock(&Lock); + AssertMsgReturn(!rc, ("KernVMUnlock(Lock) -> %#x\n", rc), VERR_LOCK_FAILED); + + rc = KernVMLock(VMDHL_WRITE, pHdr, cbReq, &Lock, (KernPageList_t *)-1, NULL); + AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, cbReq, &Lock, rc), VERR_LOCK_FAILED); + } + + /* + * Process the IOCtl. + */ + rc = supdrvIOCtl(iFunction, &g_DevExt, pSession, pHdr); + } + else + { + OSDBGPRINT(("VBoxDrvIOCtl: max(%#x,%#x); iCmd=%#x\n", pHdr->cbIn, pHdr->cbOut, iFunction)); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + OSDBGPRINT(("VBoxDrvIOCtl: bad magic fFlags=%#x; iCmd=%#x\n", pHdr->fFlags, iFunction)); + rc = VERR_INVALID_PARAMETER; + } + + /* + * Unlock and return. + */ + int rc2 = KernVMUnlock(&Lock); + AssertMsg(!rc2, ("rc2=%d\n", rc2)); NOREF(rc2); + + dprintf2(("VBoxDrvIOCtl: returns %d\n", rc)); + return rc; + } + return VERR_NOT_SUPPORTED; +} + + +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + NOREF(pDevExt); + return false; +} + + +/** + * Callback for writing to the log buffer. + * + * @returns number of bytes written. + * @param pvArg Unused. + * @param pachChars Pointer to an array of utf-8 characters. + * @param cbChars Number of bytes in the character array pointed to by pachChars. + */ +static DECLCALLBACK(size_t) VBoxDrvLogOutput(void *pvArg, const char *pachChars, size_t cbChars) +{ + size_t cchWritten = 0; + while (cbChars-- > 0) + { + const uint16_t offLogHead = g_offLogHead; + const uint16_t offLogHeadNext = (offLogHead + 1) & (g_cchLogMax - 1); + if (offLogHeadNext == g_offLogTail) + break; /* no */ + g_szLog[offLogHead] = *pachChars++; + g_offLogHead = offLogHeadNext; + cchWritten++; + } + return cchWritten; +} + + +SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + va_list va; + +#if 0 //def DEBUG_bird + va_start(va, pszFormat); + RTLogComPrintfV(pszFormat, va); + va_end(va); +#endif + + va_start(va, pszFormat); + int cch = RTLogFormatV(VBoxDrvLogOutput, NULL, pszFormat, va); + va_end(va); + + return cch; +} diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def new file mode 100644 index 000000000..6e71395cb --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def @@ -0,0 +1,48 @@ +;; @file +; VBoxDrv - OS/2 definition file. +; + +; +; Copyright (C) 2007 Sun Microsystems, Inc. +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +; Clara, CA 95054 USA or visit http://www.sun.com if you need +; additional information or have any questions. +; + + +PHYSICAL DEVICE VBoxDrv +DESCRIPTION 'VirtualBox.org Support Driver for OS/2.' +CODE PRELOAD EXECUTEREAD +DATA PRELOAD +; We're using wlink.exe, so this doesn't work. +;SEGMENTS +; DATA16 class 'FAR_DATA' +; DATA16_INIT class 'FAR_DATA' +; +; CODE16 class 'CODE' +; CODE16_INIT class 'CODE' +; +; CODE32 class 'CODE' +; TEXT32 class 'CODE' +; +; DATA32 class 'DATA' +; BSS32 class 'BSS' + diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm b/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm new file mode 100644 index 000000000..4498a58df --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm @@ -0,0 +1,974 @@ +; $Id: SUPDrvA-os2.asm 10729 2008-07-17 15:05:47Z vboxsync $ +;; @file +; VBoxDrv - OS/2 assembly file, the first file in the link. +; + +; +; Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> +; +; Permission is hereby granted, free of charge, to any person +; obtaining a copy of this software and associated documentation +; files (the "Software"), to deal in the Software without +; restriction, including without limitation the rights to use, +; copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the +; Software is furnished to do so, subject to the following +; conditions: +; +; The above copyright notice and this permission notice shall be +; included in all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +; OTHER DEALINGS IN THE SOFTWARE. +; + + +;******************************************************************************* +;* Header Files * +;******************************************************************************* +%define RT_INCL_16BIT_SEGMENTS +%include "iprt/asmdefs.mac" + + +;******************************************************************************* +;* Structures and Typedefs * +;******************************************************************************* +;; +; Request packet header. +struc PKTHDR + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 +endstruc + + +;; +; Init request packet - input. +struc PKTINITIN + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + + .data_1 resb 1 + .fpfnDevHlp resd 1 + .fpszArgs resd 1 + .data_2 resb 1 +endstruc + +;; +; Init request packet - output. +struc PKTINITOUT + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + + .cUnits resb 1 ; block devs only. + .cbCode16 resw 1 + .cbData16 resw 1 + .fpaBPBs resd 1 ; block devs only. + .data_2 resb 1 +endstruc + +;; +; Open request packet. +struc PKTOPEN + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + .sfn resw 1 +endstruc + +;; +; Close request packet. +struc PKTCLOSE + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + .sfn resw 1 +endstruc + +;; +; IOCtl request packet. +struc PKTIOCTL + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + + .cat resb 1 + .fun resb 1 + .pParm resd 1 + .pData resd 1 + .sfn resw 1 + .cbParm resw 1 + .cbData resw 1 +endstruc + +;; +; Read/Write request packet +struc PKTRW + .cb resb 1 + .unit resb 1 + .cmd resb 1 + .status resw 1 + .res1 resd 1 + .link resd 1 + + .media resb 1 + .PhysTrans resd 1 + .cbTrans resw 1 + .start resd 1 + .sfn resw 1 +endstruc + + + +;; +; The two device headers. +segment DATA16 + +; Some devhdr.inc stuff. +%define DEVLEV_3 0180h +%define DEV_30 0800h +%define DEV_CHAR_DEV 8000h +%define DEV_16MB 0002h +%define DEV_IOCTL2 0001h + +; Some dhcalls.h stuff. +%define DevHlp_VirtToLin 05bh +%define DevHlp_SAVE_MESSAGE 03dh +%define DevHlp_PhysToVirt 015h + +; Fast IOCtl category, also defined in SUPDrvIOC.h +%define SUP_CTL_CATEGORY_FAST 0c1h + + +;******************************************************************************* +;* External Symbols * +;******************************************************************************* +extern KernThunkStackTo32 +extern KernThunkStackTo16 +extern DOS16OPEN +extern DOS16CLOSE +extern DOS16WRITE +extern NAME(VBoxDrvInit) +extern NAME(VBoxDrvOpen) +extern NAME(VBoxDrvClose) +extern NAME(VBoxDrvIOCtl) +extern NAME(VBoxDrvIOCtlFast) + + +;; +; Device headers. The first one is the one we'll be opening and the +; latter is only used for 32-bit initialization. +GLOBALNAME g_VBoxDrvHdr1 + dw NAME(g_VBoxDrvHdr2) wrt DATA16 ; NextHeader.off + dw DATA16 ; NextHeader.sel + dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt + dw NAME(VBoxDrvEP) wrt CODE16 ; StrategyEP + dw 0 ; InterruptEP + db 'vboxdrv$' ; DevName + dw 0 ; SDevProtCS + dw 0 ; SDevProtDS + dw 0 ; SDevRealCS + dw 0 ; SDevRealDS + dd DEV_16MB | DEV_IOCTL2 ; SDevCaps + +align 4 +GLOBALNAME g_VBoxDrvHdr2 + dd 0ffffffffh ; NextHeader (NIL) + dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt + dw NAME(VBoxDrvInitEP) wrt CODE16 ; StrategyEP + dw 0 ; InterruptEP + db 'vboxdr1$' ; DevName + dw 0 ; SDevProtCS + dw 0 ; SDevProtDS + dw 0 ; SDevRealCS + dw 0 ; SDevRealDS + dd DEV_16MB | DEV_IOCTL2 ; SDevCaps + + +;; Tristate 32-bit initialization indicator [0 = need init, -1 = init failed, 1 init succeeded]. +; Check in the open path of the primary driver. The secondary driver will +; open the primary one during it's init and thereby trigger the 32-bit init. +GLOBALNAME g_fInitialized + db 0 + +align 4 +;; Pointer to the device helper service routine +; This is set during the initialization of the 2nd device driver. +GLOBALNAME g_fpfnDevHlp + dd 0 + + +;; Where we write to the log. +GLOBALNAME g_offLogHead + dw 0 +;; Where we read from the log. +GLOBALNAME g_offLogTail + dw 0 +;; The size of the log. (power of two!) +%define LOG_SIZE 16384 +GLOBALNAME g_cchLogMax + dw LOG_SIZE +;; The log buffer. +GLOBALNAME g_szLog + times LOG_SIZE db 0 + + +; +; The init data. +; +segment DATA16_INIT +GLOBALNAME g_InitDataStart + +;; Far pointer to the device argument. +g_fpszArgs: + dd 0 + +%if 0 +;; Message table for the Save_Message device helper. +GLOBALNAME g_MsgTab + dw 1178 ; MsgId - 'MSG_REPLACEMENT_STRING'. + dw 1 ; cMsgStrings + dw NAME(g_szInitText) ; MsgStrings[0] + dw seg NAME(g_szInitText) +%else +;; Far pointer to DOS16WRITE (corrected set before called). +; Just a temporary hack to work around a wlink issue. +GLOBALNAME g_fpfnDos16Write + dw DOS16WRITE + dw seg DOS16WRITE +%endif + +;; Size of the text currently in the g_szInitText buffer. +GLOBALNAME g_cchInitText + dw 0 +;; The max size of text that can fit into the g_szInitText buffer. +GLOBALNAME g_cchInitTextMax + dw 512 +;; The init text buffer. +GLOBALNAME g_szInitText + times 512 db 0 + +; +; The 16-bit code segment. +; +segment CODE16 + + +;; +; The strategy entry point (vboxdrv$). +; +; ss:bx -> request packet +; ds:si -> device header +; +; Can clobber any registers it likes except SP. +; +BEGINPROC VBoxDrvEP + push ebp + mov ebp, esp + push es ; bp - 2 + push bx ; bp - 4 + and sp, 0fffch + + ; + ; Check for the most frequent first. + ; + cmp byte [es:bx + PKTHDR.cmd], 10h ; Generic IOCtl + jne near VBoxDrvEP_NotGenIOCtl + + + ; + ; Generic I/O Control Request. + ; +VBoxDrvEP_GenIOCtl: + + ; Fast IOCtl? + cmp byte [es:bx + PKTIOCTL.cat], SUP_CTL_CATEGORY_FAST + jne VBoxDrvEP_GenIOCtl_Other + + ; + ; Fast IOCtl. + ; DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction) + ; +VBoxDrvEP_GenIOCtl_Fast: + ; function. + movzx edx, byte [es:bx + PKTIOCTL.fun] + push edx ; 04h + + ; system file number. + movzx eax, word [es:bx + PKTIOCTL.sfn] + push eax ; 00h + + ; go to the 32-bit code + ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_32) wrt FLAT + db 066h + db 0eah + dd NAME(VBoxDrvEP_GenIOCtl_Fast_32) ;wrt FLAT + dw TEXT32 wrt FLAT +segment TEXT32 +GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_32 + + ; switch stack to 32-bit. + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + call KernThunkStackTo32 + + ; call the C code (don't cleanup the stack). + call NAME(VBoxDrvIOCtlFast) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + ; jump back to the 16-bit code. + ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16 + db 066h + db 0eah + dw NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16 + dw CODE16 +segment CODE16 +GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_16 + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near VBoxDrvEP_GeneralFailure + + ; setup output stuff. + xor eax, eax + mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData. + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + + mov sp, bp + pop ebp + retf + + ; + ; Other IOCtl (slow) + ; +VBoxDrvEP_GenIOCtl_Other: + mov eax, [es:bx + PKTIOCTL.cbParm] ; Load cbParm and cbData + push eax ; 1eh - in/out data size. + ; 1ch - in/out parameter size. + push edx ; 18h - pointer to data size (filled in later). + push ecx ; 14h - pointer to param size (filled in later). + + ; pData (convert to flat 32-bit) + mov ax, word [es:bx + PKTIOCTL.pData + 2] ; selector + cmp ax, 3 ; <= 3 -> nil selector... + jbe .no_data + movzx esi, word [es:bx + PKTIOCTL.pData] ; offset + mov dl, DevHlp_VirtToLin + call far [NAME(g_fpfnDevHlp)] + jc near VBoxDrvEP_GeneralFailure + jmp .finish_data +.no_data: + xor eax, eax +.finish_data: + push eax ; 10h + + ; pParm (convert to flat 32-bit) + mov ax, word [es:bx + PKTIOCTL.pParm + 2] ; selector + cmp ax, 3 ; <= 3 -> nil selector... + jbe .no_parm + movzx esi, word [es:bx + PKTIOCTL.pParm] ; offset + mov dl, DevHlp_VirtToLin + call far [NAME(g_fpfnDevHlp)] + jc near VBoxDrvEP_GeneralFailure + jmp .finish_parm +.no_parm: + xor eax, eax +.finish_parm: + push eax ; 0ch + + ; function. + movzx edx, byte [es:bx + PKTIOCTL.fun] + push edx ; 08h + + ; category. + movzx ecx, byte [es:bx + PKTIOCTL.cat] + push ecx ; 04h + + ; system file number. + movzx eax, word [es:bx + PKTIOCTL.sfn] + push eax ; 00h + + ; go to the 32-bit code + ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_32) wrt FLAT + db 066h + db 0eah + dd NAME(VBoxDrvEP_GenIOCtl_Other_32) ;wrt FLAT + dw TEXT32 wrt FLAT +segment TEXT32 +GLOBALNAME VBoxDrvEP_GenIOCtl_Other_32 + + ; switch stack to 32-bit. + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + call KernThunkStackTo32 + + ; update in/out parameter pointers + lea eax, [esp + 1ch] + mov [esp + 14h], eax + lea edx, [esp + 1eh] + mov [esp + 18h], edx + + ; call the C code (don't cleanup the stack). + call NAME(VBoxDrvIOCtl) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + ; jump back to the 16-bit code. + ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16 + db 066h + db 0eah + dw NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16 + dw CODE16 +segment CODE16 +GLOBALNAME VBoxDrvEP_GenIOCtl_Other_16 + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near VBoxDrvEP_GeneralFailure + + ; setup output stuff. + mov edx, esp + mov eax, [ss:edx + 1ch] ; output sizes. + mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData. + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + + mov sp, bp + pop ebp + retf + + + ; + ; Less Performance Critical Requests. + ; +VBoxDrvEP_NotGenIOCtl: + cmp byte [es:bx + PKTHDR.cmd], 0dh ; Open + je VBoxDrvEP_Open + cmp byte [es:bx + PKTHDR.cmd], 0eh ; Close + je VBoxDrvEP_Close + cmp byte [es:bx + PKTHDR.cmd], 00h ; Init + je VBoxDrvEP_Init + cmp byte [es:bx + PKTHDR.cmd], 04h ; Read + je near VBoxDrvEP_Read + jmp near VBoxDrvEP_NotSupported + + + ; + ; Open Request. w/ ring-0 init. + ; +VBoxDrvEP_Open: + cmp byte [NAME(g_fInitialized)], 1 + jne VBoxDrvEP_OpenOther + + ; First argument, the system file number. + movzx eax, word [es:bx + PKTOPEN.sfn] + push eax + + ; go to the 32-bit code + ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt FLAT + db 066h + db 0eah + dd NAME(VBoxDrvEP_Open_32) ;wrt FLAT + dw TEXT32 wrt FLAT +segment TEXT32 +GLOBALNAME VBoxDrvEP_Open_32 + + ; switch stack to 32-bit. + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + call KernThunkStackTo32 + + ; call the C code. + call NAME(VBoxDrvOpen) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + ; jump back to the 16-bit code. + ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt CODE16 + db 066h + db 0eah + dw NAME(VBoxDrvEP_Open_16) wrt CODE16 + dw CODE16 +segment CODE16 +GLOBALNAME VBoxDrvEP_Open_16 + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near VBoxDrvEP_GeneralFailure + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + jmp near VBoxDrvEP_Done + + ; Initializing or failed init? +VBoxDrvEP_OpenOther: + cmp byte [NAME(g_fInitialized)], 0 + jne VBoxDrvEP_OpenFailed + + mov byte [NAME(g_fInitialized)], -1 + call NAME(VBoxDrvRing0Init) + cmp byte [NAME(g_fInitialized)], 1 + je VBoxDrvEP_Open + +VBoxDrvEP_OpenFailed: + mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. + jmp near VBoxDrvEP_Done + + + ; + ; Close Request. + ; +VBoxDrvEP_Close: + ; First argument, the system file number. + movzx eax, word [es:bx + PKTOPEN.sfn] + push eax + + ; go to the 32-bit code + ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt FLAT + db 066h + db 0eah + dd NAME(VBoxDrvEP_Close_32) ;wrt FLAT + dw TEXT32 wrt FLAT +segment TEXT32 +GLOBALNAME VBoxDrvEP_Close_32 + + ; switch stack to 32-bit. + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + call KernThunkStackTo32 + + ; call the C code. + call NAME(VBoxDrvClose) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + ; jump back to the 16-bit code. + ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt CODE16 + db 066h + db 0eah + dw NAME(VBoxDrvEP_Close_16) wrt CODE16 + dw CODE16 +segment CODE16 +GLOBALNAME VBoxDrvEP_Close_16 + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near VBoxDrvEP_GeneralFailure + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + jmp near VBoxDrvEP_Done + + + ; + ; Init Request. + ; The other driver header will do this. + ; +VBoxDrvEP_Init: + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + mov byte [es:bx + PKTINITOUT.cUnits], 0 + mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16 + mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16 + mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 + jmp near VBoxDrvEP_Done + + + ; + ; Read Request. + ; Return log data. + ; +VBoxDrvEP_Read: + ; Any log data available? + xor dx, dx + mov ax, [NAME(g_offLogTail)] + cmp ax, [NAME(g_offLogHead)] + jz near .log_done + + ; create a temporary mapping of the physical buffer. Docs claims it trashes nearly everything... + push ebp + mov cx, [es:bx + PKTRW.cbTrans] + push cx + mov ax, [es:bx + PKTRW.PhysTrans + 2] + mov bx, [es:bx + PKTRW.PhysTrans] + mov dh, 1 + mov dl, DevHlp_PhysToVirt + call far [NAME(g_fpfnDevHlp)] + pop bx ; bx = cbTrans + pop ebp + jc near .log_phystovirt_failed + ; es:di -> the output buffer. + + ; setup the copy operation. + mov ax, [NAME(g_offLogTail)] + xor dx, dx ; dx tracks the number of bytes copied. +.log_loop: + mov cx, [NAME(g_offLogHead)] + cmp ax, cx + je .log_done + jb .log_loop_before + mov cx, LOG_SIZE +.log_loop_before: ; cx = end offset + sub cx, ax ; cx = sequential bytes to copy. + cmp cx, bx + jbe .log_loop_min + mov cx, bx ; output buffer is smaller than available data. +.log_loop_min: + mov si, NAME(g_szLog) + add si, ax ; ds:si -> the log buffer. + add dx, cx ; update output counter + add ax, cx ; calc new offLogTail + and ax, LOG_SIZE - 1 + rep movsb ; do the copy + mov [NAME(g_offLogTail)], ax ; commit the read. + jmp .log_loop + +.log_done: + les bx, [bp - 4] ; Reload the packet pointer. + mov word [es:bx + PKTRW.cbTrans], dx + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + jmp near VBoxDrvEP_Done + +.log_phystovirt_failed: + les bx, [bp - 4] ; Reload the packet pointer. + jmp VBoxDrvEP_GeneralFailure + + + ; + ; Return 'unknown command' error. + ; +VBoxDrvEP_NotSupported: + mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. + jmp VBoxDrvEP_Done + + ; + ; Return 'general failure' error. + ; +VBoxDrvEP_GeneralFailure: + mov word [es:bx + PKTHDR.status], 0810ch ; error, done, general failure. + jmp VBoxDrvEP_Done + + ; + ; Non-optimized return path. + ; +VBoxDrvEP_Done: + mov sp, bp + pop ebp + retf +ENDPROC VBoxDrvEP + + +;; +; The helper device entry point. +; +; This is only used to do the DosOpen on the main driver so we can +; do ring-3 init and report failures. +; +GLOBALNAME VBoxDrvInitEP + ; The only request we're servicing is the 'init' one. + cmp word [es:bx + PKTHDR.cmd], 0 + je near NAME(VBoxDrvInitEPServiceInitReq) + + ; Ok, it's not the init request, just fail it. + mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. + retf + + +; +; The 16-bit init code. +; +segment CODE16_INIT +GLOBALNAME g_InitCodeStart + +;; The device name for DosOpen. +g_szDeviceName: + db '\DEV\vboxdrv$', 0 + +; icsdebug can't see where stuff starts otherwise. (kDevTest) +int3 +int3 +int3 +int3 +int3 +int3 + +;; +; The Ring-3 init code. +; +BEGINPROC VBoxDrvInitEPServiceInitReq + push ebp + mov ebp, esp + push es ; bp - 2 + push sp ; bp - 4 + push -1 ; bp - 6: hfOpen + push 0 ; bp - 8: usAction + and sp, 0fffch + + ; check for the init package. + cmp word [es:bx + PKTHDR.cmd], 0 + jne near .not_init + + ; + ; Copy the data out of the init packet. + ; + mov eax, [es:bx + PKTINITIN.fpfnDevHlp] + mov [NAME(g_fpfnDevHlp)], eax + mov edx, [es:bx + PKTINITIN.fpszArgs] + mov [g_fpszArgs], edx + + ; + ; Open the first driver, close it, and check status. + ; + + ; APIRET _Pascal DosOpen(PSZ pszFname, PHFILE phfOpen, PUSHORT pusAction, + ; ULONG ulFSize, USHORT usAttr, USHORT fsOpenFlags, + ; USHORT fsOpenMode, ULONG ulReserved); + push seg g_szDeviceName ; pszFname + push g_szDeviceName + push ss ; phfOpen + lea dx, [bp - 6] + push dx + push ss ; pusAction + lea dx, [bp - 8] + push dx + push dword 0 ; ulFSize + push 0 ; usAttr = FILE_NORMAL + push 1 ; fsOpenFlags = FILE_OPEN + push 00040h ; fsOpenMode = OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY + push dword 0 ; ulReserved + call far DOS16OPEN + + push ax ; Quickly flush any text. + call NAME(VBoxDrvInitFlushText) + pop ax + + or ax, ax + jnz .done_err + + ; APIRET APIENTRY DosClose(HFILE hf); + mov cx, [bp - 6] + push cx + call far DOS16CLOSE + or ax, ax + jnz .done_err ; This can't happen (I hope). + + ; + ; Ok, we're good. + ; + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + mov byte [es:bx + PKTINITOUT.cUnits], 0 + mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16 + mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16 + mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 + jmp .done + + ; + ; Init failure. + ; +.done_err: + mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. + mov byte [es:bx + PKTINITOUT.cUnits], 0 + mov word [es:bx + PKTINITOUT.cbCode16], 0 + mov word [es:bx + PKTINITOUT.cbData16], 0 + mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 + jmp .done + + ; + ; Not init, return 'unknown command'. + ; +.not_init: + mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. + jmp .done + + ; + ; Request done. + ; +.done: + mov sp, bp + pop ebp + retf +ENDPROC VBoxDrvInitEPServiceInitReq + + +;; +; The Ring-0 init code. +; +BEGINPROC VBoxDrvRing0Init + push es + push esi + push ebp + mov ebp, esp + and sp, 0fffch + + ; + ; Thunk the argument string pointer first. + ; + movzx esi, word [g_fpszArgs] ; offset + mov ax, [g_fpszArgs + 2] ; selector + mov dl, DevHlp_VirtToLin + call far [NAME(g_fpfnDevHlp)] + jc near VBoxDrvRing0Init_done ; eax is non-zero on failure (can't happen) + push eax ; 00h - pszArgs (for VBoxDrvInit). + + ; + ; Do 16-bit init? + ; + + + ; + ; Do 32-bit init + ; + ;jmp far dword NAME(VBoxDrvRing0Init_32) wrt FLAT + db 066h + db 0eah + dd NAME(VBoxDrvRing0Init_32) ;wrt FLAT + dw TEXT32 wrt FLAT +segment TEXT32 +GLOBALNAME VBoxDrvRing0Init_32 + + ; switch stack to 32-bit. + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + call KernThunkStackTo32 + + ; call the C code. + call NAME(VBoxDrvInit) + + ; switch back the stack and reload ds. + push eax + call KernThunkStackTo16 + pop eax + + mov dx, seg NAME(g_fInitialized) + mov ds, dx + + ; jump back to the 16-bit code. + ;jmp far dword NAME(VBoxDrvRing0Init_16) wrt CODE16 + db 066h + db 0eah + dw NAME(VBoxDrvRing0Init_16) wrt CODE16 + dw CODE16_INIT +segment CODE16_INIT +GLOBALNAME VBoxDrvRing0Init_16 + + ; check the result and set g_fInitialized on success. + or eax, eax + jnz VBoxDrvRing0Init_done + mov byte [NAME(g_fInitialized)], 1 + +VBoxDrvRing0Init_done: + mov sp, bp + pop ebp + pop esi + pop es + ret +ENDPROC VBoxDrvRing0Init + + +;; +; Flush any text in the text buffer. +; +BEGINPROC VBoxDrvInitFlushText + push bp + mov bp, sp + + ; Anything in the buffer? + mov ax, [NAME(g_cchInitText)] + or ax, ax + jz .done + +%if 1 + ; Write it to STDOUT. + ; APIRET _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten); + push ax ; bp - 2 : cbBytesWritten + mov cx, sp + push 1 ; STDOUT + push seg NAME(g_szInitText) ; pvBuf + push NAME(g_szInitText) + push ax ; cbBuf + push ss ; pcbBytesWritten + push cx +%if 0 ; wlink generates a non-aliased fixup here which results in 16-bit offset with the flat 32-bit selector. + call far DOS16WRITE +%else + ; convert flat pointer to a far pointer using the tiled algorithm. + push ds + mov ax, DATA32 wrt FLAT + mov ds, ax + mov eax, g_pfnDos16Write wrt FLAT + movzx eax, word [eax + 2] ; High word of the flat address (in DATA32). + shl ax, 3 + or ax, 0007h + pop ds + mov [NAME(g_fpfnDos16Write) + 2], ax ; Update the selector (in DATA16_INIT). + ; do the call + call far [NAME(g_fpfnDos16Write)] +%endif + +%else ; alternative workaround for the wlink issue. + ; Use the save message devhlp. + push esi + push ebx + xor bx, bx + mov si, NAME(g_MsgTab) + mov dx, seg NAME(g_MsgTab) + mov ds, dx + mov dl, DevHlp_SAVE_MESSAGE + call far [NAME(g_fpfnDevHlp)] + pop ebx + pop esi +%endif + + ; Empty the buffer. + mov word [NAME(g_cchInitText)], 0 + mov byte [NAME(g_szInitText)], 0 + +.done: + mov sp, bp + pop bp + ret +ENDPROC VBoxDrvInitFlushText + + + +;; +; This must be present +segment DATA32 +g_pfnDos16Write: + dd DOS16WRITE ; flat + + diff --git a/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp b/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp new file mode 100644 index 000000000..fee169bfc --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/SUPLib-os2.cpp @@ -0,0 +1,197 @@ +/* $Id: SUPLib-os2.cpp 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VirtualBox Support Library - OS/2 specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define INCL_BASE +#define INCL_ERRORS +#include <os2.h> +#undef RT_MAX + +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <VBox/types.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** OS/2 Device name. */ +#define DEVICE_NAME "/dev/vboxdrv$" + + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + /* + * Try open the device. + */ + ULONG ulAction = 0; + HFILE hDevice = (HFILE)-1; + APIRET rc = DosOpen((PCSZ)DEVICE_NAME, + &hDevice, + &ulAction, + 0, + FILE_NORMAL, + OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS, + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, + NULL); + if (rc) + { + int vrc; + switch (rc) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: vrc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: vrc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("Failed to open \"%s\", rc=%d, vrc=%Rrc\n", DEVICE_NAME, rc, vrc)); + return vrc; + } + + pThis->hDevice = (RTFILE)hDevice; + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Check if we're initited at all. + */ + if (pThis->hDevice != NIL_RTFILE) + { + APIRET rc = DosClose((HFILE)pThis->hDevice); + AssertMsg(rc == NO_ERROR, ("%d\n", rc)); NOREF(rc); + pThis->hDevice = NIL_RTFILE; + } + + return 0; +} + + +int suplibOsInstall(void) +{ + /** @remark OS/2: Not supported */ + return VERR_NOT_SUPPORTED; +} + + +int suplibOsUninstall(void) +{ + /** @remark OS/2: Not supported */ + return VERR_NOT_SUPPORTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + ULONG cbReturned = sizeof(SUPREQHDR); + int rc = DosDevIOCtl((HFILE)pThis->hDevice, SUP_CTL_CATEGORY, uFunction, + pvReq, cbReturned, &cbReturned, + NULL, 0, NULL); + if (RT_LIKELY(rc == NO_ERROR)) + return VINF_SUCCESS; + return RTErrConvertFromOS2(rc); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + NOREF(idCpu); + int32_t rcRet = VERR_INTERNAL_ERROR; + int rc = DosDevIOCtl((HFILE)pThis->hDevice, SUP_CTL_CATEGORY_FAST, uFunction, + NULL, 0, NULL, + NULL, 0, NULL); + if (RT_LIKELY(rc == NO_ERROR)) + rc = rcRet; + else + rc = RTErrConvertFromOS2(rc); + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = NULL; + int rc = DosAllocMem(ppvPages, cPages << PAGE_SHIFT, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_ANY); + if (rc == ERROR_INVALID_PARAMETER) + rc = DosAllocMem(ppvPages, cPages << PAGE_SHIFT, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_ANY); + if (!rc) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromOS2(rc); + return rc; +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + if (pvPages) + { + int rc = DosFreeMem(pvPages); + Assert(!rc); NOREF(rc); + } + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c b/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c new file mode 100644 index 000000000..ef05452ec --- /dev/null +++ b/src/VBox/HostDrivers/Support/os2/SUPR0IdcClient-os2.c @@ -0,0 +1,54 @@ +/* $Id: SUPR0IdcClient-os2.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, OS/2 Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return VERR_NOT_SUPPORTED; +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return VERR_NOT_SUPPORTED; +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + return VERR_NOT_SUPPORTED; +} + diff --git a/src/VBox/HostDrivers/Support/solaris/Makefile.kup b/src/VBox/HostDrivers/Support/solaris/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c new file mode 100644 index 000000000..fc3e580fc --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c @@ -0,0 +1,889 @@ +/* $Id: SUPDrv-solaris.c 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - Solaris specifics. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/uio.h> +#include <sys/buf.h> +#include <sys/modctl.h> +#include <sys/open.h> +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/file.h> +#include <sys/priv_names.h> +#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */ + +#include "../SUPDrvInternal.h" +#include <VBox/log.h> +#include <VBox/version.h> +#include <iprt/semaphore.h> +#include <iprt/spinlock.h> +#include <iprt/mp.h> +#include <iprt/process.h> +#include <iprt/thread.h> +#include <iprt/initterm.h> +#include <iprt/alloc.h> +#include <iprt/string.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** @todo this quoting macros probably should be moved to a common place. + * The indirection is for expanding macros passed to the first macro. */ +#define VBOXSOLQUOTE2(x) #x +#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x) +/** The module name. */ +#define DEVICE_NAME "vboxdrv" +/** The module description as seen in 'modinfo'. */ +#define DEVICE_DESC "VirtualBox HostDrv" +/** Maximum number of driver instances. */ +#define DEVICE_MAXINSTANCES 16 + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int VBoxDrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred); +static int VBoxDrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred); +static int VBoxDrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int VBoxDrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int VBoxDrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArgs, int mode, cred_t *pCred, int *pVal); + +static int VBoxDrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t Cmd); +static int VBoxDrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t Cmd); + +static int VBoxSupDrvErr2SolarisErr(int rc); +static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int Cmd, int Mode, intptr_t pArgs); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * cb_ops: for drivers that support char/block entry points + */ +static struct cb_ops g_VBoxDrvSolarisCbOps = +{ + VBoxDrvSolarisOpen, + VBoxDrvSolarisClose, + nodev, /* b strategy */ + nodev, /* b dump */ + nodev, /* b print */ + VBoxDrvSolarisRead, + VBoxDrvSolarisWrite, + VBoxDrvSolarisIOCtl, + nodev, /* c devmap */ + nodev, /* c mmap */ + nodev, /* c segmap */ + nochpoll, /* c poll */ + ddi_prop_op, /* property ops */ + NULL, /* streamtab */ + D_NEW | D_MP, /* compat. flag */ + CB_REV /* revision */ +}; + +/** + * dev_ops: for driver device operations + */ +static struct dev_ops g_VBoxDrvSolarisDevOps = +{ + DEVO_REV, /* driver build revision */ + 0, /* ref count */ + nulldev, /* get info */ + nulldev, /* identify */ + nulldev, /* probe */ + VBoxDrvSolarisAttach, + VBoxDrvSolarisDetach, + nodev, /* reset */ + &g_VBoxDrvSolarisCbOps, + (struct bus_ops *)0, + nodev /* power */ +}; + +/** + * modldrv: export driver specifics to the kernel + */ +static struct modldrv g_VBoxDrvSolarisModule = +{ + &mod_driverops, /* extern from kernel */ + DEVICE_DESC " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV), + &g_VBoxDrvSolarisDevOps +}; + +/** + * modlinkage: export install/remove/info to the kernel + */ +static struct modlinkage g_VBoxDrvSolarisModLinkage = +{ + MODREV_1, /* loadable module system revision */ + &g_VBoxDrvSolarisModule, + NULL /* terminate array of linkage structures */ +}; + +#ifndef USE_SESSION_HASH +/** + * State info for each open file handle. + */ +typedef struct +{ + /**< Pointer to the session data. */ + PSUPDRVSESSION pSession; +} vbox_devstate_t; +#else +/** State info. for each driver instance. */ +typedef struct +{ + dev_info_t *pDip; /* Device handle */ +} vbox_devstate_t; +#endif + +/** Opaque pointer to list of state */ +static void *g_pVBoxDrvSolarisState; + +/** Device extention & session data association structure */ +static SUPDRVDEVEXT g_DevExt; + +/* GCC C++ hack. */ +unsigned __gxx_personality_v0 = 0xcccccccc; + +/** Hash table */ +static PSUPDRVSESSION g_apSessionHashTab[19]; +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Calculates bucket index into g_apSessionHashTab.*/ +#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab)) + +/** + * Kernel entry points + */ +int _init(void) +{ + LogFlow((DEVICE_NAME ":_init\n")); + + /* + * Prevent module autounloading. + */ + modctl_t *pModCtl = mod_getctl(&g_VBoxDrvSolarisModLinkage); + if (pModCtl) + pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD; + else + LogRel((DEVICE_NAME ":failed to disable autounloading!\n")); + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension + */ + rc = supdrvInitDevExt(&g_DevExt); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the session hash table. + */ + memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); + rc = RTSpinlockCreate(&g_Spinlock); + if (RT_SUCCESS(rc)) + { + int rc = ddi_soft_state_init(&g_pVBoxDrvSolarisState, sizeof(vbox_devstate_t), 8); + if (!rc) + { + rc = mod_install(&g_VBoxDrvSolarisModLinkage); + if (!rc) + return 0; /* success */ + + ddi_soft_state_fini(&g_pVBoxDrvSolarisState); + LogRel((DEVICE_NAME ":mod_install failed! rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ":failed to initialize soft state.\n")); + + RTSpinlockDestroy(g_Spinlock); + g_Spinlock = NIL_RTSPINLOCK; + } + else + LogRel((DEVICE_NAME ":VBoxDrvSolarisAttach: RTSpinlockCreate failed\n")); + supdrvDeleteDevExt(&g_DevExt); + } + else + LogRel((DEVICE_NAME ":VBoxDrvSolarisAttach: supdrvInitDevExt failed\n")); + RTR0Term(); + } + else + LogRel((DEVICE_NAME ":VBoxDrvSolarisAttach: failed to init R0Drv\n")); + memset(&g_DevExt, 0, sizeof(g_DevExt)); + + return -1; +} + + +int _fini(void) +{ + LogFlow((DEVICE_NAME ":_fini\n")); + + /* + * Undo the work we did at start (in the reverse order). + */ + int rc = mod_remove(&g_VBoxDrvSolarisModLinkage); + if (rc != 0) + return rc; + + supdrvDeleteDevExt(&g_DevExt); + + rc = RTSpinlockDestroy(g_Spinlock); + AssertRC(rc); + g_Spinlock = NIL_RTSPINLOCK; + + RTR0Term(); + + memset(&g_DevExt, 0, sizeof(g_DevExt)); + + ddi_soft_state_fini(&g_pVBoxDrvSolarisState); + return 0; +} + + +int _info(struct modinfo *pModInfo) +{ + LogFlow((DEVICE_NAME ":_info\n")); + int e = mod_info(&g_VBoxDrvSolarisModLinkage, pModInfo); + return e; +} + + +/** + * Attach entry point, to attach a device to the system or resume it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (attach/resume). + * + * @return corresponding solaris error code. + */ +static int VBoxDrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd) +{ + LogFlow((DEVICE_NAME ":VBoxDrvSolarisAttach\n")); + + switch (enmCmd) + { + case DDI_ATTACH: + { + int rc; + int instance = ddi_get_instance(pDip); +#ifdef USE_SESSION_HASH + vbox_devstate_t *pState; + + if (ddi_soft_state_zalloc(g_pVBoxDrvSolarisState, instance) != DDI_SUCCESS) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisAttach: state alloc failed\n")); + return DDI_FAILURE; + } + + pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance); +#endif + + /* + * Register ourselves as a character device, pseudo-driver + */ +#ifdef VBOX_WITH_HARDENING + rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, + 0, NULL, NULL, 0600); +#else + rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, + 0, "none", "none", 0666); +#endif + if (rc == DDI_SUCCESS) + { +#ifdef USE_SESSION_HASH + pState->pDip = pDip; +#endif + ddi_report_dev(pDip); + return DDI_SUCCESS; + } + + return DDI_FAILURE; + } + + case DDI_RESUME: + { + RTSemFastMutexRequest(g_DevExt.mtxGip); + if (g_DevExt.pGipTimer) + RTTimerStart(g_DevExt.pGipTimer, 0); + + RTSemFastMutexRelease(g_DevExt.mtxGip); + return DDI_SUCCESS; + } + + default: + return DDI_FAILURE; + } + + return DDI_FAILURE; +} + + +/** + * Detach entry point, to detach a device to the system or suspend it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (detach/suspend). + * + * @return corresponding solaris error code. + */ +static int VBoxDrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd) +{ + int rc = VINF_SUCCESS; + + LogFlow((DEVICE_NAME ":VBoxDrvSolarisDetach\n")); + switch (enmCmd) + { + case DDI_DETACH: + { + int instance = ddi_get_instance(pDip); +#ifndef USE_SESSION_HASH + ddi_remove_minor_node(pDip, NULL); +#else + vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance); + ddi_remove_minor_node(pDip, NULL); + ddi_soft_state_free(g_pVBoxDrvSolarisState, instance); +#endif + return DDI_SUCCESS; + } + + case DDI_SUSPEND: + { + RTSemFastMutexRequest(g_DevExt.mtxGip); + if (g_DevExt.pGipTimer && g_DevExt.cGipUsers > 0) + RTTimerStop(g_DevExt.pGipTimer); + + RTSemFastMutexRelease(g_DevExt.mtxGip); + return DDI_SUCCESS; + } + + default: + return DDI_FAILURE; + } +} + + + +/** + * User context entry points + */ +static int VBoxDrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred) +{ + int rc; + PSUPDRVSESSION pSession; + LogFlow((DEVICE_NAME ":VBoxDrvSolarisOpen: pDev=%p:%#x\n", pDev, *pDev)); + +#ifndef USE_SESSION_HASH + /* + * Locate a new device open instance. + * + * For each open call we'll allocate an item in the soft state of the device. + * The item index is stored in the dev_t. I hope this is ok... + */ + vbox_devstate_t *pState = NULL; + unsigned iOpenInstance; + for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++) + { + if ( !ddi_get_soft_state(g_pVBoxDrvSolarisState, iOpenInstance) /* faster */ + && ddi_soft_state_zalloc(g_pVBoxDrvSolarisState, iOpenInstance) == DDI_SUCCESS) + { + pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, iOpenInstance); + break; + } + } + if (!pState) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisOpen: too many open instances.\n")); + return ENXIO; + } + + /* + * Create a new session. + */ + rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &pSession); + if (RT_SUCCESS(rc)) + { + pSession->Uid = crgetruid(pCred); + pSession->Gid = crgetrgid(pCred); + + pState->pSession = pSession; + *pDev = makedevice(getmajor(*pDev), iOpenInstance); + LogFlow((DEVICE_NAME ":VBoxDrvSolarisOpen: Dev=%#x pSession=%p pid=%d r0proc=%p thread=%p\n", + *pDev, pSession, RTProcSelf(), RTR0ProcHandleSelf(), RTThreadNativeSelf() )); + return 0; + } + + /* failed - clean up */ + ddi_soft_state_free(g_pVBoxDrvSolarisState, iOpenInstance); + +#else + /* + * Create a new session. + * Sessions in Solaris driver are mostly useless. It's however needed + * in VBoxDrvSolarisIOCtlSlow() while calling supdrvIOCtl() + */ + rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &pSession); + if (RT_SUCCESS(rc)) + { + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + unsigned iHash; + + pSession->Uid = crgetruid(pCred); + pSession->Gid = crgetrgid(pCred); + + /* + * Insert it into the hash table. + */ + iHash = SESSION_HASH(pSession->Process); + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = pSession; + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + LogFlow((DEVICE_NAME ":VBoxDrvSolarisOpen success\n")); + } + + int instance; + for (instance = 0; instance < DEVICE_MAXINSTANCES; instance++) + { + vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance); + if (pState) + break; + } + + if (instance >= DEVICE_MAXINSTANCES) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisOpen: All instances exhausted\n")); + return ENXIO; + } + + *pDev = makedevice(getmajor(*pDev), instance); + + return VBoxSupDrvErr2SolarisErr(rc); +#endif +} + + +static int VBoxDrvSolarisClose(dev_t Dev, int flag, int otyp, cred_t *cred) +{ + LogFlow((DEVICE_NAME ":VBoxDrvSolarisClose: Dev=%#x\n", Dev)); + +#ifndef USE_SESSION_HASH + /* + * Get the session and free the soft state item. + */ + vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, getminor(Dev)); + if (!pState) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisClose: no state data for %#x (%d)\n", Dev, getminor(Dev))); + return EFAULT; + } + + PSUPDRVSESSION pSession = pState->pSession; + pState->pSession = NULL; + ddi_soft_state_free(g_pVBoxDrvSolarisState, getminor(Dev)); + + if (!pSession) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisClose: no session in state data for %#x (%d)\n", Dev, getminor(Dev))); + return EFAULT; + } + LogFlow((DEVICE_NAME ":VBoxDrvSolarisClose: Dev=%#x pSession=%p pid=%d r0proc=%p thread=%p\n", + Dev, pSession, RTProcSelf(), RTR0ProcHandleSelf(), RTThreadNativeSelf() )); + +#else + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(Process); + PSUPDRVSESSION pSession; + + /* + * Remove from the hash table. + */ + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if (pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + } + else + { + PSUPDRVSESSION pPrev = pSession; + pSession = pSession->pNextHash; + while (pSession) + { + if (pSession->Process == Process) + { + pPrev->pNextHash = pSession->pNextHash; + pSession->pNextHash = NULL; + break; + } + + /* next */ + pPrev = pSession; + pSession = pSession->pNextHash; + } + } + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisClose: WHAT?!? pSession == NULL! This must be a mistake... pid=%d (close)\n", + (int)Process)); + return EFAULT; + } +#endif + + /* + * Close the session. + */ + supdrvCloseSession(&g_DevExt, pSession); + return 0; +} + + +static int VBoxDrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFlow((DEVICE_NAME ":VBoxDrvSolarisRead")); + return 0; +} + + +static int VBoxDrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFlow((DEVICE_NAME ":VBoxDrvSolarisWrite")); + return 0; +} + + +/** + * Driver ioctl, an alternate entry point for this character driver. + * + * @param Dev Device number + * @param Cmd Operation identifier + * @param pArg Arguments from user to driver + * @param Mode Information bitfield (read/write, address space etc.) + * @param pCred User credentials + * @param pVal Return value for calling process. + * + * @return corresponding solaris error code. + */ +static int VBoxDrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArgs, int Mode, cred_t *pCred, int *pVal) +{ +#ifndef USE_SESSION_HASH + /* + * Get the session from the soft state item. + */ + vbox_devstate_t *pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, getminor(Dev)); + if (!pState) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev))); + return EINVAL; + } + + PSUPDRVSESSION pSession = pState->pSession; + if (!pSession) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev))); + return DDI_SUCCESS; + } +#else + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(Process); + PSUPDRVSESSION pSession; + + /* + * Find the session. + */ + RTSpinlockAcquireNoInts(g_Spinlock, &Tmp); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while (pSession && pSession->Process != Process); + } + RTSpinlockReleaseNoInts(g_Spinlock, &Tmp); + if (!pSession) + { + LogRel((DEVICE_NAME ":VBoxSupDrvIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n", + (int)Process, Cmd)); + return EINVAL; + } +#endif + + /* + * Deal with the two high-speed IOCtl that takes it's arguments from + * the session and iCmd, and only returns a VBox status code. + */ + if ( Cmd == SUP_IOCTL_FAST_DO_RAW_RUN + || Cmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || Cmd == SUP_IOCTL_FAST_DO_NOP) + { + *pVal = supdrvIOCtlFast(Cmd, pArgs, &g_DevExt, pSession); + return 0; + } + + return VBoxDrvSolarisIOCtlSlow(pSession, Cmd, Mode, pArgs); +} + + +/** @def IOCPARM_LEN + * Gets the length from the ioctl number. + * This is normally defined by sys/ioccom.h on BSD systems... + */ +#ifndef IOCPARM_LEN +# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK ) +#endif + + +/** + * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions. + * + * @returns Solaris errno. + * + * @param pSession The session. + * @param Cmd The IOCtl command. + * @param Mode Information bitfield (for specifying ownership of data) + * @param iArg User space address of the request buffer. + */ +static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int iCmd, int Mode, intptr_t iArg) +{ + int rc; + uint32_t cbBuf = 0; + SUPREQHDR Hdr; + PSUPREQHDR pHdr; + + + /* + * Read the header. + */ + if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(Hdr))) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(Hdr))); + return EINVAL; + } + rc = ddi_copyin((void *)iArg, &Hdr, sizeof(Hdr), Mode); + if (RT_UNLIKELY(rc)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc)); + return EFAULT; + } + if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: bad header magic %#x; iCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, iCmd)); + return EINVAL; + } + cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || Hdr.cbOut < sizeof(Hdr) + || cbBuf > _1M*16)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", Hdr.cbIn, Hdr.cbOut, iCmd)); + return EINVAL; + } + + /* + * Buffer the request. + */ + pHdr = RTMemTmpAlloc(cbBuf); + if (RT_UNLIKELY(!pHdr)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd)); + return ENOMEM; + } + rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode); + if (RT_UNLIKELY(rc)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, Hdr.cbIn, iCmd, rc)); + RTMemFree(pHdr); + return EFAULT; + } + + /* + * Process the IOCtl. + */ + rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr); + + /* + * Copy ioctl data and output buffer back to user space. + */ + if (RT_LIKELY(!rc)) + { + uint32_t cbOut = pHdr->cbOut; + if (RT_UNLIKELY(cbOut > cbBuf)) + { + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd)); + cbOut = cbBuf; + } + rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode); + if (RT_UNLIKELY(rc != 0)) + { + /* this is really bad */ + LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc)); + rc = EFAULT; + } + } + else + rc = EINVAL; + + RTMemTmpFree(pHdr); + return rc; +} + + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code, see supdrvIDC. + * @param iReq The request code. + * @param pReq The request. + */ +int VBOXCALL SUPDrvSolarisIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + PSUPDRVSESSION pSession; + + /* + * Some quick validations. + */ + if (RT_UNLIKELY(!VALID_PTR(pReq))) + return VERR_INVALID_POINTER; + + pSession = pReq->pSession; + if (pSession) + { + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return VERR_INVALID_PARAMETER; + if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt)) + return VERR_INVALID_PARAMETER; + } + else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT)) + return VERR_INVALID_PARAMETER; + + /* + * Do the job. + */ + return supdrvIDC(uReq, &g_DevExt, pSession, pReq); +} + + +/** + * Converts an supdrv error code to a solaris error code. + * + * @returns corresponding solaris error code. + * @param rc supdrv error code (SUPDRV_ERR_* defines). + */ +static int VBoxSupDrvErr2SolarisErr(int rc) +{ + switch (rc) + { + case 0: return 0; + case SUPDRV_ERR_GENERAL_FAILURE: return EACCES; + case SUPDRV_ERR_INVALID_PARAM: return EINVAL; + case SUPDRV_ERR_INVALID_MAGIC: return EILSEQ; + case SUPDRV_ERR_INVALID_HANDLE: return ENXIO; + case SUPDRV_ERR_INVALID_POINTER: return EFAULT; + case SUPDRV_ERR_LOCK_FAILED: return ENOLCK; + case SUPDRV_ERR_ALREADY_LOADED: return EEXIST; + case SUPDRV_ERR_PERMISSION_DENIED: return EPERM; + case SUPDRV_ERR_VERSION_MISMATCH: return ENOSYS; + } + + return EPERM; +} + + +/** + * Initializes any OS specific object creator fields. + */ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +/** + * Checks if the session can access the object. + * + * @returns true if a decision has been made. + * @returns false if the default access policy should be applied. + * + * @param pObj The object in question. + * @param pSession The session wanting to access the object. + * @param pszObjName The object name, can be NULL. + * @param prc Where to store the result when returning true. + */ +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + return false; +} + + +RTDECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + va_list args; + char szMsg[512]; + + va_start(args, pszFormat); + RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, args); + va_end(args); + + szMsg[sizeof(szMsg) - 1] = '\0'; + cmn_err(CE_CONT, "%s", szMsg); + return 0; +} + diff --git a/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp b/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp new file mode 100644 index 000000000..b4dbdd82f --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/SUPLib-solaris.cpp @@ -0,0 +1,190 @@ +/* $Id: SUPLib-solaris.cpp 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Solaris specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <VBox/types.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + +#include <sys/fcntl.h> +#include <sys/ioctl.h> + +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <stdio.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Solaris device link. */ +#define DEVICE_NAME "/dev/vboxdrv" + + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + + /* + * Try to open the device. + */ + int hDevice = open(DEVICE_NAME, O_RDWR, 0); + if (hDevice < 0) + { + int rc; + switch (errno) + { + case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break; + case EPERM: + case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break; + case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: rc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("Failed to open \"%s\", errno=%d, rc=%Rrc\n", DEVICE_NAME, errno, rc)); + return rc; + } + + /* + * Mark the file handle close on exec. + */ + if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0) + { +#ifdef IN_SUP_HARDENED_R3 + int rc = VERR_INTERNAL_ERROR; +#else + int err = errno; + int rc = RTErrConvertFromErrno(err); + LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc)); +#endif + close(hDevice); + return rc; + } + + pThis->hDevice = hDevice; + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Check if we're initialized + */ + if (pThis->hDevice != NIL_RTFILE) + { + if (close(pThis->hDevice)) + AssertFailed(); + pThis->hDevice = NIL_RTFILE; + } + + return VINF_SUCCESS; +} + + +int suplibOsInstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + +int suplibOsUninstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + int rc = ioctl(pThis->hDevice, uFunction, idCpu); + if (rc == -1) + rc = errno; + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = mmap(NULL, cPages * PAGE_SIZE, PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (*ppvPages != (void *)-1) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t cPages) +{ + NOREF(pThis); + munmap(pvPages, cPages * PAGE_SIZE); + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c b/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c new file mode 100644 index 000000000..f6dd151e0 --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/SUPR0IdcClient-solaris.c @@ -0,0 +1,59 @@ +/* $Id: SUPR0IdcClient-solaris.c 10258 2008-07-04 23:31:26Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Solaris Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq); +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + int rc = SUPDrvSolarisIDC(iReq, pReq); + if (RT_SUCCESS(rc)) + rc = pReq->rc; + + NOREF(pHandle); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/solaris/mod.sh b/src/VBox/HostDrivers/Support/solaris/mod.sh new file mode 100755 index 000000000..182185a29 --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/mod.sh @@ -0,0 +1,76 @@ +#!/bin/sh +set -x +# +# Figure out the environment and locations. +# + +# Sudo isn't native solaris, but it's very convenient... +if test -z "$SUDO" && test "`whoami`" != "root"; then + SUDO=sudo +fi + +script_dir=`dirname "$0"` +# src/VBox/HostDrivers/solaris/ residence: +script_dir=`cd "$script_dir/../../../../.." ; /bin/pwd` +## root residence: +#script_dir=`cd "$script_dir" ; /bin/pwd` + +set -e +if test -z "$BUILD_TARGET"; then + export BUILD_TARGET=solaris +fi +if test -z "$BUILD_TARGET_ARCH"; then + export BUILD_TARGET_ARCH=x86 +fi +if test -z "$BUILD_TYPE"; then + export BUILD_TYPE=debug +fi + +DIR=$script_dir/out/$BUILD_TARGET.$BUILD_TARGET_ARCH/$BUILD_TYPE/bin/ + +VBOXDRV_CONF_DIR=/platform/i86pc/kernel/drv +if test "$BUILD_TARGET_ARCH" = "amd64"; then + VBOXDRV_DIR=$VBOXDRV_CONF_DIR/amd64 +else + VBOXDRV_DIR=$VBOXDRV_CONF_DIR +fi + +# +# Do the job. +# +$SUDO cp $DIR/vboxdrv $VBOXDRV_DIR/vboxdrv +$SUDO cp $script_dir/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf $VBOXDRV_CONF_DIR/vboxdrv.conf +old_id=`/usr/sbin/modinfo | /usr/xpg4/bin/grep vbox | cut -f 1 -d ' ' | sort -n -r ` +if test -n "$old_id"; then + echo "* unloading $old_id..." + sync + sync + $SUDO /usr/sbin/modunload -i $old_id +#else +# echo "* If it fails below, run: $SUDO add_drv -m'* 0666 root sys' vboxdrv" +fi +$SUDO /usr/sbin/rem_drv vboxdrv || echo "* ignored rem_drv failure..." +$SUDO /usr/sbin/add_drv vboxdrv + +if /usr/xpg4/bin/grep -q vboxdrv /etc/devlink.tab; then + echo "* vboxdrv already present in /etc/devlink.tab" +else + echo "* Adding vboxdrv to /etc/devlink.tab" + $SUDO rm -f /tmp/devlink.tab.vboxdrv + echo "" > /tmp/devlink.tab.vboxdrv + echo '# vbox' >> /tmp/devlink.tab.vboxdrv + echo 'type=ddi_pseudo;name=vboxdrv \D' >> /tmp/devlink.tab.vboxdrv + $SUDO /bin/sh -c 'cat /tmp/devlink.tab.vboxdrv >> /etc/devlink.tab' +fi + +echo "* loading vboxdrv..." +sync +sync +$SUDO /usr/sbin/modload $VBOXDRV_DIR/vboxdrv +/usr/sbin/modinfo | /usr/xpg4/bin/grep vboxdrv +echo "* dmesg:" +dmesg | tail -20 +if test ! -h /dev/vboxdrv; then + $SUDO /usr/sbin/devfsadm -i vboxdrv +fi +ls -laL /dev/vboxdrv diff --git a/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf b/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf new file mode 100644 index 000000000..4b7d53d20 --- /dev/null +++ b/src/VBox/HostDrivers/Support/solaris/vboxdrv.conf @@ -0,0 +1,25 @@ +# +# Sun xVM VirtualBox +# OpenSolaris Host Driver Configuration +# +# Copyright (C) 2007-2008 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +# This needs to go into /platform/i86pc/kernel/drv, +# while the 64-bit driver object goes into the amd64 +# subdirectory (32-bit drivers goes into the same +# directory). After copying execute add_drv vboxdrv. +# +name="vboxdrv" parent="pseudo" instance=0; diff --git a/src/VBox/HostDrivers/Support/testcase/Makefile.kmk b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk new file mode 100644 index 000000000..c755f723c --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk @@ -0,0 +1,83 @@ +# $Id: Makefile.kmk 14515 2008-11-24 12:33:00Z vboxsync $ +## @file +# Sub-Makefile for the SUPLib testcases. +# + +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +PROGRAMS += \ + SUPInstall \ + SUPUninstall +ifdef VBOX_WITH_TESTCASES +PROGRAMS += \ + tstPage \ + tstContiguous \ + tstInit \ + tstInt \ + tstLow \ + tstPin \ + tstGIP-2 \ + tstGetPagingMode +endif # VBOX_WITH_TESTCASES + +SUPInstall_TEMPLATE = VBOXR3EXE +SUPInstall_SOURCES = SUPInstall.cpp +SUPInstall_LIBS = $(LIB_RUNTIME) + +SUPUninstall_TEMPLATE = VBOXR3EXE +SUPUninstall_SOURCES = SUPUninstall.cpp +SUPUninstall_LIBS = $(LIB_RUNTIME) + +tstInt_TEMPLATE = VBOXR3EXE +tstInt_SOURCES = tstInt.cpp +tstInt_LIBS = $(LIB_RUNTIME) + +tstContiguous_TEMPLATE= VBOXR3TSTEXE +tstContiguous_SOURCES = tstContiguous.cpp + +tstInit_TEMPLATE = VBOXR3TSTEXE +tstInit_SOURCES = tstInit.cpp + +tstLow_TEMPLATE = VBOXR3TSTEXE +tstLow_SOURCES = tstLow.cpp + +tstPin_TEMPLATE = VBOXR3TSTEXE +tstPin_SOURCES = tstPin.cpp + +tstPage_TEMPLATE = VBOXR3TSTEXE +tstPage_SOURCES = tstPage.cpp + +tstGIP-2_TEMPLATE = VBOXR3TSTEXE +tstGIP-2_SOURCES = tstGIP-2.cpp + +tstGetPagingMode_TEMPLATE = VBOXR3TSTEXE +tstGetPagingMode_SOURCES = tstGetPagingMode.cpp + +include $(KBUILD_PATH)/subfooter.kmk + diff --git a/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp new file mode 100644 index 000000000..a71280730 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/SUPInstall.cpp @@ -0,0 +1,53 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Testcase for driver install + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + RTR3Init(); + int rc = SUPInstall(); + if (RT_SUCCESS(rc)) + { + printf("installed successfully\n"); + return 0; + } + printf("installation failed. rc=%d\n", rc); + + return 1; +} diff --git a/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp new file mode 100644 index 000000000..c52234828 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/SUPUninstall.cpp @@ -0,0 +1,53 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Testcase for driver uninstall + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + RTR3Init(); + int rc = SUPUninstall(); + if (RT_SUCCESS(rc)) + { + printf("uninstalled successfully\n"); + return 0; + } + printf("uninstallation failed. rc=%d\n", rc); + + return 1; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp new file mode 100644 index 000000000..e0bf7de3a --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstContiguous.cpp @@ -0,0 +1,104 @@ +/* $Id: tstContiguous.cpp 13837 2008-11-05 02:54:02Z vboxsync $ */ +/** @file + * VBox Support Driver - Contiguous Memory Testcase (ring-3). + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/param.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <stdlib.h> +#include <string.h> + + +int main(int argc, char **argv) +{ + int rc; + int rcRet = 0; + + RTR3Init(); + rc = SUPR3Init(NULL); + RTPrintf("tstContiguous: SUPR3Init -> rc=%Rrc\n", rc); + rcRet += rc != 0; + if (!rc) + { + /* + * Allocate a bit of contiguous memory. + */ + RTHCPHYS HCPhys; + void *pv = SUPContAlloc(8, &HCPhys); + rcRet += pv == NULL || HCPhys == 0; + if (pv && HCPhys) + { + memset(pv, 0xff, PAGE_SIZE * 8); + pv = SUPContAlloc(5, &HCPhys); + rcRet += pv == NULL || HCPhys == 0; + if (pv && HCPhys) + { + memset(pv, 0x7f, PAGE_SIZE * 5); + rc = SUPContFree(pv, 5); + rcRet += rc != 0; + if (rc) + RTPrintf("tstContiguous: SUPContFree failed! rc=%Rrc\n", rc); + + void *apv[128]; + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + { + apv[i] = SUPContAlloc(1 + (i % 11), &HCPhys); + if (!apv[i]) + { + RTPrintf("tstContiguous: i=%d: failed to allocate %d pages\n", i, 1 + (i % 11)); + rcRet++; + } + } + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + if (apv[i]) + { + rc = SUPContFree(apv[i], 1 + (i % 11)); + rcRet += rc != 0; + if (rc) + RTPrintf("tstContiguous: i=%d SUPContFree failed! rc=%Rrc\n", i, rc); + } + } + else + RTPrintf("tstContiguous: SUPContAlloc (2nd) failed!\n"); + } + else + RTPrintf("tstContiguous: SUPContAlloc failed!\n"); + + rc = SUPTerm(); + RTPrintf("tstContiguous: SUPTerm -> rc=%Rrc\n", rc); + rcRet += rc != 0; + } + + return rcRet ? 1 : 0; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp new file mode 100644 index 000000000..4bb0494e6 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp @@ -0,0 +1,185 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Test the Global Info Page interface + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/thread.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/initterm.h> +#include <iprt/getopt.h> + + +int main(int argc, char **argv) +{ + RTR3Init(); + + /* + * Parse args + */ + static const RTOPTIONDEF g_aOptions[] = + { + { "--interations", 'i', RTGETOPT_REQ_INT32 }, + { "--hex", 'h', RTGETOPT_REQ_NOTHING }, + { "--decimal", 'd', RTGETOPT_REQ_NOTHING }, + { "--spin", 's', RTGETOPT_REQ_NOTHING } + }; + + uint32_t cIterations = 40; + bool fHex = true; + bool fSpin = false; + int ch; + int iArg = 1; + RTOPTIONUNION ValueUnion; + while ((ch = RTGetOpt(argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), &iArg, &ValueUnion))) + { + switch (ch) + { + case 'i': + cIterations = ValueUnion.u32; + break; + + case 'd': + fHex = false; + break; + + case 'h': + fHex = true; + break; + + case 's': + fSpin = true; + break; + + default: + if (ch < 0) + RTPrintf("tstGIP-2: %Rrc: %s\n", ch, ValueUnion.psz); + else + RTPrintf("tstGIP-2: syntax error: %s\n", ValueUnion.psz); + return 1; + } + } + if (iArg < argc) + { + RTPrintf("tstGIP-2: syntax error: %s\n", ValueUnion.psz); + return 1; + } + + /* + * Init + */ + PSUPDRVSESSION pSession = NIL_RTR0PTR; + int rc = SUPR3Init(&pSession); + if (RT_SUCCESS(rc)) + { + if (g_pSUPGlobalInfoPage) + { + RTPrintf("tstGIP-2: u32UpdateHz=%RU32 u32UpdateIntervalNS=%RU32 u64NanoTSLastUpdateHz=%RX64 u32Mode=%d (%s) u32Version=%#x\n", + g_pSUPGlobalInfoPage->u32UpdateHz, + g_pSUPGlobalInfoPage->u32UpdateIntervalNS, + g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz, + g_pSUPGlobalInfoPage->u32Mode, + g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_SYNC_TSC ? "sync" + : g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_ASYNC_TSC ? "async" + : "???", + g_pSUPGlobalInfoPage->u32Version); + RTPrintf(fHex + ? "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz TSC Interval History...\n" + : "tstGIP-2: it: u64NanoTS delta u64TSC UpIntTSC H TransId CpuHz TSC Interval History...\n"); + static SUPGIPCPU s_aaCPUs[2][RT_ELEMENTS(g_pSUPGlobalInfoPage->aCPUs)]; + for (uint32_t i = 0; i < cIterations; i++) + { + /* copy the data */ + memcpy(&s_aaCPUs[i & 1][0], &g_pSUPGlobalInfoPage->aCPUs[0], sizeof(g_pSUPGlobalInfoPage->aCPUs)); + + /* display it & find something to spin on. */ + uint32_t u32TransactionId = 0; + uint32_t volatile *pu32TransactionId = NULL; + for (unsigned iCpu = 0; iCpu < RT_ELEMENTS(g_pSUPGlobalInfoPage->aCPUs); iCpu++) + if ( g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz > 0 + && g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz != _4G + 1) + { + PSUPGIPCPU pPrevCpu = &s_aaCPUs[!(i & 1)][iCpu]; + PSUPGIPCPU pCpu = &s_aaCPUs[i & 1][iCpu]; + RTPrintf(fHex + ? "tstGIP-2: %4d/%d: %016llx %09llx %016llx %08x %d %08x %15llu %08x %08x %08x %08x %08x %08x %08x %08x (%d)\n" + : "tstGIP-2: %4d/%d: %016llu %09llu %016llu %010u %d %010u %15llu %08x %08x %08x %08x %08x %08x %08x %08x (%d)\n", + i, iCpu, + pCpu->u64NanoTS, + i ? pCpu->u64NanoTS - pPrevCpu->u64NanoTS : 0, + pCpu->u64TSC, + pCpu->u32UpdateIntervalTSC, + pCpu->iTSCHistoryHead, + pCpu->u32TransactionId, + pCpu->u64CpuHz, + pCpu->au32TSCHistory[0], + pCpu->au32TSCHistory[1], + pCpu->au32TSCHistory[2], + pCpu->au32TSCHistory[3], + pCpu->au32TSCHistory[4], + pCpu->au32TSCHistory[5], + pCpu->au32TSCHistory[6], + pCpu->au32TSCHistory[7], + pCpu->cErrors); + if (!pu32TransactionId) + { + pu32TransactionId = &g_pSUPGlobalInfoPage->aCPUs[iCpu].u32TransactionId; + u32TransactionId = pCpu->u32TransactionId; + } + } + + /* wait a bit / spin */ + if (!fSpin) + RTThreadSleep(9); + else + while (u32TransactionId == *pu32TransactionId) + /* nop */; + } + } + else + { + RTPrintf("tstGIP-2: g_pSUPGlobalInfoPage is NULL\n"); + rc = -1; + } + + SUPTerm(); + } + else + RTPrintf("tstGIP-2: SUPR3Init failed: %Rrc\n", rc); + return !!rc; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp new file mode 100644 index 000000000..ac75110f5 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstGetPagingMode.cpp @@ -0,0 +1,99 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Test the interface for querying host paging mode + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> + + +int main(int argc, char **argv) +{ + int rc; + RTR3Init(); + rc = SUPR3Init(NULL); + if (RT_SUCCESS(rc)) + { + SUPPAGINGMODE enmMode = SUPGetPagingMode(); + switch (enmMode) + { + case SUPPAGINGMODE_INVALID: + RTPrintf("SUPPAGINGMODE_INVALID\n"); + break; + case SUPPAGINGMODE_32_BIT: + RTPrintf("SUPPAGINGMODE_32_BIT\n"); + break; + case SUPPAGINGMODE_32_BIT_GLOBAL: + RTPrintf("SUPPAGINGMODE_32_BIT_GLOBAL\n"); + break; + case SUPPAGINGMODE_PAE: + RTPrintf("SUPPAGINGMODE_PAE\n"); + break; + case SUPPAGINGMODE_PAE_GLOBAL: + RTPrintf("SUPPAGINGMODE_PAE_GLOBAL\n"); + break; + case SUPPAGINGMODE_PAE_NX: + RTPrintf("SUPPAGINGMODE_PAE_NX\n"); + break; + case SUPPAGINGMODE_PAE_GLOBAL_NX: + RTPrintf("SUPPAGINGMODE_PAE_GLOBAL_NX\n"); + break; + case SUPPAGINGMODE_AMD64: + RTPrintf("SUPPAGINGMODE_AMD64\n"); + break; + case SUPPAGINGMODE_AMD64_GLOBAL: + RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL\n"); + break; + case SUPPAGINGMODE_AMD64_NX: + RTPrintf("SUPPAGINGMODE_AMD64_NX\n"); + break; + case SUPPAGINGMODE_AMD64_GLOBAL_NX: + RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL_NX\n"); + break; + default: + RTPrintf("Unknown mode %d\n", enmMode); + rc = VERR_INTERNAL_ERROR; + break; + } + + int rc2 = SUPTerm(); + RTPrintf("SUPTerm -> rc=%Rrc\n", rc2); + } + else + RTPrintf("SUPR3Init -> rc=%Rrc\n", rc); + + return !RT_SUCCESS(rc); +} + diff --git a/src/VBox/HostDrivers/Support/testcase/tstInit.cpp b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp new file mode 100644 index 000000000..f4da86ae3 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstInit.cpp @@ -0,0 +1,55 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Tests init and term of the support library + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> + + +int main(int argc, char **argv) +{ + int rc; + RTR3Init(); + rc = SUPR3Init(NULL); + RTPrintf("tstInit: SUPR3Init -> rc=%d\n", rc); + if (!rc) + { + rc = SUPTerm(); + RTPrintf("tstInit: SUPTerm -> rc=%d\n", rc); + } + + return rc; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstInt.cpp b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp new file mode 100644 index 000000000..e519b1372 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstInt.cpp @@ -0,0 +1,233 @@ +/** $Id: tstInt.cpp 14831 2008-11-30 10:31:16Z vboxsync $ */ +/** @file + * Testcase: Test the interrupt gate feature of the support library. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/vm.h> +#include <VBox/vmm.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/alloc.h> +#include <iprt/time.h> + + +/** + * Makes a path to a file in the executable directory. + */ +static char *ExeDirFile(char *pszFile, const char *pszArgv0, const char *pszFilename) +{ + char *psz; + char *psz2; + + strcpy(pszFile, pszArgv0); + psz = strrchr(pszFile, '/'); + psz2 = strrchr(pszFile, '\\'); + if (psz < psz2) + psz = psz2; + if (!psz) + psz = strrchr(pszFile, ':'); + if (!psz) + { + strcpy(pszFile, "./"); + psz = &pszFile[1]; + } + strcpy(psz + 1, "VMMR0.r0"); + return pszFile; +} + +int main(int argc, char **argv) +{ + int rcRet = 0; + int i; + int rc; + int cIterations = argc > 1 ? RTStrToUInt32(argv[1]) : 32; + if (cIterations == 0) + cIterations = 64; + + /* + * Init. + */ + RTR3Init(); + PSUPDRVSESSION pSession; + rc = SUPR3Init(&pSession); + rcRet += rc != 0; + RTPrintf("tstInt: SUPR3Init -> rc=%Rrc\n", rc); + if (!rc) + { + /* + * Load VMM code. + */ + char szFile[RTPATH_MAX]; + rc = SUPLoadVMM(ExeDirFile(szFile, argv[0], "VMMR0.r0")); + if (!rc) + { + /* + * Create a fake 'VM'. + */ + PVMR0 pVMR0 = NIL_RTR0PTR; + PVM pVM = NULL; + const unsigned cPages = RT_ALIGN_Z(sizeof(*pVM), PAGE_SIZE) >> PAGE_SHIFT; + PSUPPAGE paPages = (PSUPPAGE)RTMemAllocZ(cPages * sizeof(SUPPAGE)); + if (paPages) + rc = SUPLowAlloc(cPages, (void **)&pVM, &pVMR0, &paPages[0]); + else + rc = VERR_NO_MEMORY; + if (RT_SUCCESS(rc)) + { + pVM->pVMRC = 0; + pVM->pVMR3 = pVM; + pVM->pVMR0 = pVMR0; + pVM->paVMPagesR3 = paPages; + pVM->pSession = pSession; + pVM->enmVMState = VMSTATE_CREATED; + + rc = SUPSetVMForFastIOCtl(pVMR0); + if (!rc) + { + + /* + * Call VMM code with invalid function. + */ + for (i = cIterations; i > 0; i--) + { + rc = SUPCallVMMR0(pVMR0, VMMR0_DO_SLOW_NOP, NULL); + if (rc != VINF_SUCCESS) + { + RTPrintf("tstInt: SUPCallVMMR0 -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i); + rcRet++; + break; + } + } + RTPrintf("tstInt: Performed SUPCallVMMR0 %d times (rc=%Rrc)\n", cIterations, rc); + + /* + * The fast path. + */ + if (rc == VINF_SUCCESS) + { + RTTimeNanoTS(); + uint64_t StartTS = RTTimeNanoTS(); + uint64_t StartTick = ASMReadTSC(); + uint64_t MinTicks = UINT64_MAX; + for (i = 0; i < 1000000; i++) + { + uint64_t OneStartTick = ASMReadTSC(); + rc = SUPCallVMMR0Fast(pVMR0, VMMR0_DO_NOP, 0); + uint64_t Ticks = ASMReadTSC() - OneStartTick; + if (Ticks < MinTicks) + MinTicks = Ticks; + + if (RT_UNLIKELY(rc != VINF_SUCCESS)) + { + RTPrintf("tstInt: SUPCallVMMR0Fast -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i); + rcRet++; + break; + } + } + uint64_t Ticks = ASMReadTSC() - StartTick; + uint64_t NanoSecs = RTTimeNanoTS() - StartTS; + + RTPrintf("tstInt: SUPCallVMMR0Fast - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n", + i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks); + + /* + * The ordinary path. + */ + RTTimeNanoTS(); + StartTS = RTTimeNanoTS(); + StartTick = ASMReadTSC(); + MinTicks = UINT64_MAX; + for (i = 0; i < 1000000; i++) + { + uint64_t OneStartTick = ASMReadTSC(); + rc = SUPCallVMMR0Ex(pVMR0, VMMR0_DO_SLOW_NOP, 0, NULL); + uint64_t Ticks = ASMReadTSC() - OneStartTick; + if (Ticks < MinTicks) + MinTicks = Ticks; + + if (RT_UNLIKELY(rc != VINF_SUCCESS)) + { + RTPrintf("tstInt: SUPCallVMMR0Ex -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i); + rcRet++; + break; + } + } + Ticks = ASMReadTSC() - StartTick; + NanoSecs = RTTimeNanoTS() - StartTS; + + RTPrintf("tstInt: SUPCallVMMR0Ex - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n", + i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks); + } + } + else + { + RTPrintf("tstInt: SUPSetVMForFastIOCtl failed: %Rrc\n", rc); + rcRet++; + } + } + else + { + RTPrintf("tstInt: SUPContAlloc2(%#zx,,) failed\n", sizeof(*pVM)); + rcRet++; + } + + /* + * Unload VMM. + */ + rc = SUPUnloadVMM(); + if (rc) + { + RTPrintf("tstInt: SUPUnloadVMM failed with rc=%Rrc\n", rc); + rcRet++; + } + } + else + { + RTPrintf("tstInt: SUPLoadVMM failed with rc=%Rrc\n", rc); + rcRet++; + } + + /* + * Terminate. + */ + rc = SUPTerm(); + rcRet += rc != 0; + RTPrintf("tstInt: SUPTerm -> rc=%Rrc\n", rc); + } + + return !!rc; +} + diff --git a/src/VBox/HostDrivers/Support/testcase/tstLow.cpp b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp new file mode 100644 index 000000000..b15aa237d --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstLow.cpp @@ -0,0 +1,159 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Test allocating physical memory below 4G + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> + +#include <string.h> + +int main(int argc, char **argv) +{ + int rc; + int rcRet = 0; + + RTR3Init(); + RTPrintf("tstLow: TESTING...\n"); + + rc = SUPR3Init(NULL); + if (RT_SUCCESS(rc)) + { + /* + * Allocate a bit of contiguous memory. + */ + SUPPAGE aPages0[128]; + void *pvPages0 = (void *)0x77777777; + memset(&aPages0[0], 0x8f, sizeof(aPages0)); + rc = SUPLowAlloc(RT_ELEMENTS(aPages0), &pvPages0, NULL, aPages0); + if (RT_SUCCESS(rc)) + { + /* check that the pages are below 4GB and valid. */ + for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++) + { + RTPrintf("%-4d: Phys=%RHp Reserved=%p\n", iPage, aPages0[iPage].Phys, aPages0[iPage].uReserved); + if (aPages0[iPage].uReserved != 0) + { + rcRet++; + RTPrintf("tstLow: error: aPages0[%d].uReserved=%#x expected 0!\n", iPage, aPages0[iPage].uReserved); + } + if ( aPages0[iPage].Phys >= _4G + || (aPages0[iPage].Phys & PAGE_OFFSET_MASK)) + { + rcRet++; + RTPrintf("tstLow: error: aPages0[%d].Phys=%RHp!\n", iPage, aPages0[iPage].Phys); + } + } + if (!rcRet) + { + for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++) + memset((char *)pvPages0 + iPage * PAGE_SIZE, iPage, PAGE_SIZE); + for (unsigned iPage = 0; iPage < RT_ELEMENTS(aPages0); iPage++) + for (uint8_t *pu8 = (uint8_t *)pvPages0 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++) + if (*pu8 != (uint8_t)iPage) + { + RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%p off=%#x\n", + *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK); + rcRet++; + } + } + SUPLowFree(pvPages0, RT_ELEMENTS(aPages0)); + } + else + { + RTPrintf("SUPLowAlloc(%d,,) failed -> rc=%Rrc\n", RT_ELEMENTS(aPages0), rc); + rcRet++; + } + + /* + * Allocate odd amounts in from 1 to 127. + */ + for (unsigned cPages = 1; cPages <= 127; cPages++) + { + SUPPAGE aPages1[128]; + void *pvPages1 = (void *)0x77777777; + memset(&aPages1[0], 0x8f, sizeof(aPages1)); + rc = SUPLowAlloc(cPages, &pvPages1, NULL, aPages1); + if (RT_SUCCESS(rc)) + { + /* check that the pages are below 4GB and valid. */ + for (unsigned iPage = 0; iPage < cPages; iPage++) + { + RTPrintf("%-4d::%-4d: Phys=%RHp Reserved=%p\n", cPages, iPage, aPages1[iPage].Phys, aPages1[iPage].uReserved); + if (aPages1[iPage].uReserved != 0) + { + rcRet++; + RTPrintf("tstLow: error: aPages1[%d].uReserved=%#x expected 0!\n", iPage, aPages1[iPage].uReserved); + } + if ( aPages1[iPage].Phys >= _4G + || (aPages1[iPage].Phys & PAGE_OFFSET_MASK)) + { + rcRet++; + RTPrintf("tstLow: error: aPages1[%d].Phys=%RHp!\n", iPage, aPages1[iPage].Phys); + } + } + if (!rcRet) + { + for (unsigned iPage = 0; iPage < cPages; iPage++) + memset((char *)pvPages1 + iPage * PAGE_SIZE, iPage, PAGE_SIZE); + for (unsigned iPage = 0; iPage < cPages; iPage++) + for (uint8_t *pu8 = (uint8_t *)pvPages1 + iPage * PAGE_SIZE, *pu8End = pu8 + PAGE_SIZE; pu8 < pu8End; pu8++) + if (*pu8 != (uint8_t)iPage) + { + RTPrintf("tstLow: error: invalid page content %02x != %02x. iPage=%p off=%#x\n", + *pu8, (uint8_t)iPage, iPage, (uintptr_t)pu8 & PAGE_OFFSET_MASK); + rcRet++; + } + } + SUPLowFree(pvPages1, cPages); + } + else + { + RTPrintf("SUPLowAlloc(%d,,) failed -> rc=%Rrc\n", cPages, rc); + rcRet++; + } + } + + } + else + { + RTPrintf("SUPR3Init -> rc=%Rrc\n", rc); + rcRet++; + } + + + return rcRet; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstPage.cpp b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp new file mode 100644 index 000000000..295661fac --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstPage.cpp @@ -0,0 +1,96 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Test the page allocation interface + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/param.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <string.h> + + +int main(int argc, char **argv) +{ + int cErrors = 0; + int rc = 0; + RTR3InitAndSUPLib(); + rc = SUPR3Init(NULL); + cErrors += rc != 0; + if (!rc) + { + void *pv; + rc = SUPPageAlloc(1, &pv); + cErrors += rc != 0; + if (!rc) + { + memset(pv, 0xff, PAGE_SIZE); + rc = SUPPageFree(pv, 1); + cErrors += rc != 0; + if (rc) + RTPrintf("tstPage: SUPPageFree() failed rc=%d\n", rc); + } + else + RTPrintf("tstPage: SUPPageAlloc(1,) failed rc=%d\n", rc); + + /* + * Big chunk. + */ + rc = SUPPageAlloc(1023, &pv); + cErrors += rc != 0; + if (!rc) + { + memset(pv, 0xfe, 1023 << PAGE_SHIFT); + rc = SUPPageFree(pv, 1023); + cErrors += rc != 0; + if (rc) + RTPrintf("tstPage: SUPPageFree() failed rc=%d\n", rc); + } + else + RTPrintf("tstPage: SUPPageAlloc(1,) failed rc=%d\n", rc); + + + //rc = SUPTerm(); + cErrors += rc != 0; + if (rc) + RTPrintf("tstPage: SUPTerm failed rc=%d\n", rc); + } + else + RTPrintf("tstPage: SUPR3Init failed rc=%d\n", rc); + + if (!cErrors) + RTPrintf("tstPage: SUCCESS\n"); + else + RTPrintf("tstPage: FAILURE - %d errors\n", cErrors); + return !!cErrors; +} diff --git a/src/VBox/HostDrivers/Support/testcase/tstPin.cpp b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp new file mode 100644 index 000000000..553de4586 --- /dev/null +++ b/src/VBox/HostDrivers/Support/testcase/tstPin.cpp @@ -0,0 +1,213 @@ +/** @file + * + * VBox host drivers - Ring-0 support drivers - Testcases: + * Test the memory locking interface + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/thread.h> +#include <iprt/string.h> + + +int main(int argc, char **argv) +{ + int rc; + int rcRet = 0; + RTHCPHYS HCPhys; + + RTR3InitAndSUPLib(); + rc = SUPR3Init(NULL); + RTPrintf("SUPR3Init -> rc=%d\n", rc); + rcRet += rc != 0; + if (!rc) + { + /* + * Simple test. + */ + void *pv; + int rc = SUPPageAlloc(1, &pv); + AssertRC(rc); + RTPrintf("pv=%p\n", pv); + SUPPAGE aPages[1]; + rc = SUPPageLock(pv, 1, &aPages[0]); + RTPrintf("rc=%d aPages[0]=%RHp\n", rc, pv, aPages[0]); + RTThreadSleep(1500); +#if 0 + RTPrintf("Unlocking...\n"); + RTThreadSleep(250); + rc = SUPPageUnlock(pv); + RTPrintf("rc=%d\n", rc); + RTThreadSleep(1500); +#endif + + /* + * More extensive. + */ + static struct + { + void *pv; + void *pvAligned; + SUPPAGE aPages[16]; + } aPinnings[500]; + for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i++) + { + aPinnings[i].pv = NULL; + SUPPageAlloc(0x10000 >> PAGE_SHIFT, &aPinnings[i].pv); + aPinnings[i].pvAligned = RT_ALIGN_P(aPinnings[i].pv, PAGE_SIZE); + rc = SUPPageLock(aPinnings[i].pvAligned, 0xf000 >> PAGE_SHIFT, &aPinnings[i].aPages[0]); + if (!rc) + { + RTPrintf("i=%d: pvAligned=%p pv=%p:\n", i, aPinnings[i].pvAligned, aPinnings[i].pv); + memset(aPinnings[i].pv, 0xfa, 0x10000); + unsigned c4GPluss = 0; + for (unsigned j = 0; j < (0xf000 >> PAGE_SHIFT); j++) + if (aPinnings[i].aPages[j].Phys >= _4G) + { + RTPrintf("%2d: vrt=%p phys=%RHp\n", j, (char *)aPinnings[i].pvAligned + (j << PAGE_SHIFT), aPinnings[i].aPages[j].Phys); + c4GPluss++; + } + RTPrintf("i=%d: c4GPluss=%d\n", i, c4GPluss); + } + else + { + RTPrintf("SUPPageLock -> rc=%d\n", rc); + rcRet++; + SUPPageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT); + aPinnings[i].pv = aPinnings[i].pvAligned = NULL; + break; + } + } + + for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2) + { + if (aPinnings[i].pvAligned) + { + rc = SUPPageUnlock(aPinnings[i].pvAligned); + if (rc) + { + RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", aPinnings[i].pvAligned, rc); + rcRet++; + } + memset(aPinnings[i].pv, 0xaf, 0x10000); + } + } + + for (unsigned i = 0; i < sizeof(aPinnings) / sizeof(aPinnings[0]); i += 2) + { + if (aPinnings[i].pv) + { + memset(aPinnings[i].pv, 0xcc, 0x10000); + SUPPageFree(aPinnings[i].pv, 0x10000 >> PAGE_SHIFT); + aPinnings[i].pv = NULL; + } + } + + + /* + * Allocate a bit of contiguous memory. + */ + pv = SUPContAlloc(RT_ALIGN_Z(15003, PAGE_SIZE) >> PAGE_SHIFT, &HCPhys); + rcRet += pv == NULL || HCPhys == 0; + if (pv && HCPhys) + { + RTPrintf("SUPContAlloc(15003) -> HCPhys=%llx pv=%p\n", HCPhys, pv); + void *pv0 = pv; + memset(pv0, 0xaf, 15003); + pv = SUPContAlloc(RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT, &HCPhys); + rcRet += pv == NULL || HCPhys == 0; + if (pv && HCPhys) + { + RTPrintf("SUPContAlloc(12999) -> HCPhys=%llx pv=%p\n", HCPhys, pv); + memset(pv, 0xbf, 12999); + rc = SUPContFree(pv, RT_ALIGN_Z(12999, PAGE_SIZE) >> PAGE_SHIFT); + rcRet += rc != 0; + if (rc) + RTPrintf("SUPContFree failed! rc=%d\n", rc); + } + else + RTPrintf("SUPContAlloc (2nd) failed!\n"); + memset(pv0, 0xaf, 15003); + /* pv0 is intentionally not freed! */ + } + else + RTPrintf("SUPContAlloc failed!\n"); + + /* + * Allocate a big chunk of virtual memory and then lock it. + */ + #define BIG_SIZE 72*1024*1024 + #define BIG_SIZEPP (BIG_SIZE + PAGE_SIZE) + pv = NULL; + SUPPageAlloc(BIG_SIZEPP >> PAGE_SHIFT, &pv); + if (pv) + { + static SUPPAGE aPages[BIG_SIZE >> PAGE_SHIFT]; + void *pvAligned = RT_ALIGN_P(pv, PAGE_SIZE); + rc = SUPPageLock(pvAligned, BIG_SIZE >> PAGE_SHIFT, &aPages[0]); + if (!rc) + { + /* dump */ + RTPrintf("SUPPageLock(%p,%d,) succeeded!\n", pvAligned, BIG_SIZE); + memset(pv, 0x42, BIG_SIZEPP); + #if 0 + for (unsigned j = 0; j < (BIG_SIZE >> PAGE_SHIFT); j++) + RTPrintf("%2d: vrt=%p phys=%08x\n", j, (char *)pvAligned + (j << PAGE_SHIFT), (uintptr_t)aPages[j].pvPhys); + #endif + + /* unlock */ + rc = SUPPageUnlock(pvAligned); + if (rc) + { + RTPrintf("SUPPageUnlock(%p) -> rc=%d\n", pvAligned, rc); + rcRet++; + } + memset(pv, 0xcc, BIG_SIZEPP); + } + else + { + RTPrintf("SUPPageLock(%p) -> rc=%d\n", pvAligned, rc); + rcRet++; + } + SUPPageFree(pv, BIG_SIZEPP >> PAGE_SHIFT); + } + + rc = SUPTerm(); + RTPrintf("SUPTerm -> rc=%d\n", rc); + rcRet += rc != 0; + } + + return rcRet; +} diff --git a/src/VBox/HostDrivers/Support/win/Makefile.kup b/src/VBox/HostDrivers/Support/win/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp new file mode 100644 index 000000000..5af79d3d2 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp @@ -0,0 +1,652 @@ +/* $Id: SUPDrv-win.cpp 13913 2008-11-06 12:59:01Z vboxsync $ */ +/** @file + * VBoxDrv - The VirtualBox Support Driver - Windows NT specifics. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include "../SUPDrvInternal.h" +#include <excpt.h> +#include <iprt/assert.h> +#include <iprt/process.h> +#include <iprt/initterm.h> +#include <iprt/power.h> +#include <VBox/log.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** The support service name. */ +#define SERVICE_NAME "VBoxDrv" +/** Win32 Device name. */ +#define DEVICE_NAME "\\\\.\\VBoxDrv" +/** NT Device name. */ +#define DEVICE_NAME_NT L"\\Device\\VBoxDrv" +/** Win Symlink name. */ +#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxDrv" +/** The Pool tag (VBox). */ +#define SUPDRV_NT_POOL_TAG 'xoBV' + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +#if 0 //def RT_ARCH_AMD64 +typedef struct SUPDRVEXECMEM +{ + PMDL pMdl; + void *pvMapping; + void *pvAllocation; +} SUPDRVEXECMEM, *PSUPDRVEXECMEM; +#endif + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj); +static NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack); +static NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2); +static NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS VBoxDrvNtErr2NtStatus(int rc); + +/******************************************************************************* +* External Functions * +*******************************************************************************/ +DECLASM(int) UNWIND_WRAP(RTPowerSignalEvent)(RTPOWEREVENT enmEvent); + +/******************************************************************************* +* Exported Functions * +*******************************************************************************/ +__BEGIN_DECLS +ULONG _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath); +__END_DECLS + + +/** + * Driver entry point. + * + * @returns appropriate status code. + * @param pDrvObj Pointer to driver object. + * @param pRegPath Registry base path. + */ +ULONG _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) +{ + NTSTATUS rc; + dprintf(("VBoxDrv::DriverEntry\n")); + + /* + * Create device. + * (That means creating a device object and a symbolic link so the DOS + * subsystems (OS/2, win32, ++) can access the device.) + */ + UNICODE_STRING DevName; + RtlInitUnicodeString(&DevName, DEVICE_NAME_NT); + PDEVICE_OBJECT pDevObj; + rc = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj); + if (NT_SUCCESS(rc)) + { + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS); + rc = IoCreateSymbolicLink(&DosName, &DevName); + if (NT_SUCCESS(rc)) + { + int vrc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDevObj->DeviceExtension; + memset(pDevExt, 0, sizeof(*pDevExt)); + + vrc = supdrvInitDevExt(pDevExt); + if (!vrc) + { + /* + * Setup the driver entry points in pDrvObj. + */ + pDrvObj->DriverUnload = VBoxDrvNtUnload; + pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxDrvNtCreate; + pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxDrvNtClose; + pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxDrvNtDeviceControl; +//#if 0 /** @todo test IDC on windows. */ + pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxDrvNtInternalDeviceControl; +//#endif + pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxDrvNtNotSupportedStub; + pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxDrvNtNotSupportedStub; + + /* more? */ + + /* Register ourselves for power state changes. */ + UNICODE_STRING CallbackName; + OBJECT_ATTRIBUTES Attr; + + RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState"); + InitializeObjectAttributes(&Attr, &CallbackName, OBJ_CASE_INSENSITIVE, NULL, NULL); + + rc = ExCreateCallback(&pDevExt->pObjPowerCallback, &Attr, TRUE, TRUE); + if (rc == STATUS_SUCCESS) + pDevExt->hPowerCallback = ExRegisterCallback(pDevExt->pObjPowerCallback, VBoxPowerDispatchCallback, pDevObj); + + dprintf(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n")); + return STATUS_SUCCESS; + } + + dprintf(("supdrvInitDevExit failed with vrc=%d!\n", vrc)); + rc = VBoxDrvNtErr2NtStatus(vrc); + + IoDeleteSymbolicLink(&DosName); + RTR0Term(); + } + else + { + dprintf(("RTR0Init failed with vrc=%d!\n", vrc)); + rc = VBoxDrvNtErr2NtStatus(vrc); + } + } + else + dprintf(("IoCreateSymbolicLink failed with rc=%#x!\n", rc)); + + IoDeleteDevice(pDevObj); + } + else + dprintf(("IoCreateDevice failed with rc=%#x!\n", rc)); + + if (NT_SUCCESS(rc)) + rc = STATUS_INVALID_PARAMETER; + dprintf(("VBoxDrv::DriverEntry returning %#x\n", rc)); + return rc; +} + + +/** + * Unload the driver. + * + * @param pDrvObj Driver object. + */ +void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDrvObj->DeviceObject->DeviceExtension; + + dprintf(("VBoxDrvNtUnload at irql %d\n", KeGetCurrentIrql())); + + /* Clean up the power callback registration. */ + if (pDevExt->hPowerCallback) + ExUnregisterCallback(pDevExt->hPowerCallback); + if (pDevExt->pObjPowerCallback) + ObDereferenceObject(pDevExt->pObjPowerCallback); + + /* + * We ASSUME that it's not possible to unload a driver with open handles. + * Start by deleting the symbolic link + */ + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS); + NTSTATUS rc = IoDeleteSymbolicLink(&DosName); + + /* + * Terminate the GIP page and delete the device extension. + */ + supdrvDeleteDevExt(pDevExt); + RTR0Term(); + IoDeleteDevice(pDrvObj->DeviceObject); +} + + +/** + * Create (i.e. Open) file entry point. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + dprintf(("VBoxDrvNtCreate\n")); + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack->FileObject; + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDevObj->DeviceExtension; + + /* + * We are not remotely similar to a directory... + * (But this is possible.) + */ + if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE) + { + pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return STATUS_NOT_A_DIRECTORY; + } + + /* + * Call common code for the rest. + */ + pFileObj->FsContext = NULL; + PSUPDRVSESSION pSession; +//#if 0 /** @todo check if this works, consider OBJ_KERNEL_HANDLE too. */ + bool fUser = pIrp->RequestorMode != KernelMode; +//#else + // bool fUser = true; +//#endif + int rc = supdrvCreateSession(pDevExt, fUser, &pSession); + if (!rc) + pFileObj->FsContext = pSession; + + NTSTATUS rcNt = pIrp->IoStatus.Status = VBoxDrvNtErr2NtStatus(rc); + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return rcNt; +} + + +/** + * Close file entry point. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack->FileObject; + dprintf(("VBoxDrvNtClose: pDevExt=%p pFileObj=%p pSession=%p\n", + pDevExt, pFileObj, pFileObj->FsContext)); + supdrvCloseSession(pDevExt, (PSUPDRVSESSION)pFileObj->FsContext); + pFileObj->FsContext = NULL; + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + +/** + * Device I/O Control entry point. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PSUPDRVSESSION pSession = (PSUPDRVSESSION)pStack->FileObject->FsContext; + + /* + * Deal with the two high-speed IOCtl that takes it's arguments from + * the session and iCmd, and only returns a VBox status code. + * + * Note: The previous method of returning the rc prior to IOC version + * 7.4 has been abandond, we're no longer compatible with that + * interface. + */ + ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode; + if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || ulCmd == SUP_IOCTL_FAST_DO_HWACC_RUN + || ulCmd == SUP_IOCTL_FAST_DO_NOP) + { + /* Raise the IRQL to DISPATCH_LEVEl to prevent Windows from rescheduling us to another CPU/core. */ + Assert(KeGetCurrentIrql() <= DISPATCH_LEVEL); + KIRQL oldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + int rc = supdrvIOCtlFast(ulCmd, (uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession); + KeLowerIrql(oldIrql); + + /* Complete the I/O request. */ + NTSTATUS rcNt = pIrp->IoStatus.Status = RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rcNt; + } + + return VBoxDrvNtDeviceControlSlow(pDevExt, pSession, pIrp, pStack); +} + + +/** + * Worker for VBoxDrvNtDeviceControl that takes the slow IOCtl functions. + * + * @returns NT status code. + * + * @param pDevObj Device object. + * @param pSession The session. + * @param pIrp Request packet. + * @param pStack The stack location containing the DeviceControl parameters. + */ +static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack) +{ + NTSTATUS rcNt; + unsigned cbOut = 0; + int rc = 0; + dprintf2(("VBoxDrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n", + pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode, + pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength, + pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession)); + +#ifdef RT_ARCH_AMD64 + /* Don't allow 32-bit processes to do any I/O controls. */ + if (!IoIs32bitProcess(pIrp)) +#endif + { + /* Verify that it's a buffered CTL. */ + if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED) + { + /* Verify that the sizes in the request header are correct. */ + PSUPREQHDR pHdr = (PSUPREQHDR)pIrp->AssociatedIrp.SystemBuffer; + if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) + && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn + && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut) + { + /* + * Do the job. + */ + rc = supdrvIOCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr); + if (!rc) + { + rcNt = STATUS_SUCCESS; + cbOut = pHdr->cbOut; + if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength) + { + cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength; + OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", + pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode)); + } + } + else + rcNt = STATUS_INVALID_PARAMETER; + dprintf2(("VBoxDrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc)); + } + else + { + dprintf(("VBoxDrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n", + pStack->Parameters.DeviceIoControl.IoControlCode, + pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0, + pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0, + pStack->Parameters.DeviceIoControl.InputBufferLength, + pStack->Parameters.DeviceIoControl.OutputBufferLength)); + rcNt = STATUS_INVALID_PARAMETER; + } + } + else + { + dprintf(("VBoxDrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n", + pStack->Parameters.DeviceIoControl.IoControlCode)); + rcNt = STATUS_NOT_SUPPORTED; + } + } +#ifdef RT_ARCH_AMD64 + else + { + dprintf(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n")); + rcNt = STATUS_NOT_SUPPORTED; + } +#endif + + /* complete the request. */ + pIrp->IoStatus.Status = rcNt; + pIrp->IoStatus.Information = cbOut; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rcNt; +} + + +/** + * Internal Device I/O Control entry point, used for IDC. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack ? pStack->FileObject : NULL; + PSUPDRVSESSION pSession = pFileObj ? (PSUPDRVSESSION)pFileObj->FsContext : NULL; + NTSTATUS rcNt; + unsigned cbOut = 0; + int rc = 0; + dprintf2(("VBoxDrvNtInternalDeviceControl(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n", + pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode, + pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength, + pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession)); + +/** @todo IDC on NT: figure when to create the session and that stuff... */ + + /* Verify that it's a buffered CTL. */ + if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED) + { + /* Verify the pDevExt in the session. */ + if ( ( !pSession + && pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_CONNECT) + || ( VALID_PTR(pSession) + && pSession->pDevExt == pDevExt)) + { + /* Verify that the size in the request header is correct. */ + PSUPDRVIDCREQHDR pHdr = (PSUPDRVIDCREQHDR)pIrp->AssociatedIrp.SystemBuffer; + if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) + && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cb + && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cb) + { + /* + * Do the job. + */ + rc = supdrvIDC(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr); + if (!rc) + { + rcNt = STATUS_SUCCESS; + cbOut = pHdr->cb; + } + else + rcNt = STATUS_INVALID_PARAMETER; + dprintf2(("VBoxDrvNtInternalDeviceControl: returns %#x/rc=%#x\n", rcNt, rc)); + } + else + { + dprintf(("VBoxDrvNtInternalDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx Irp=%#lx/%#lx!\n", + pStack->Parameters.DeviceIoControl.IoControlCode, + pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cb : 0, + pStack->Parameters.DeviceIoControl.InputBufferLength, + pStack->Parameters.DeviceIoControl.OutputBufferLength)); + rcNt = STATUS_INVALID_PARAMETER; + } + } + else + rcNt = STATUS_NOT_SUPPORTED; + } + else + { + dprintf(("VBoxDrvNtInternalDeviceControl: not buffered request (%#x) - not supported\n", + pStack->Parameters.DeviceIoControl.IoControlCode)); + rcNt = STATUS_NOT_SUPPORTED; + } + + /* complete the request. */ + pIrp->IoStatus.Status = rcNt; + pIrp->IoStatus.Information = cbOut; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rcNt; +} + + +/** + * Stub function for functions we don't implemented. + * + * @returns STATUS_NOT_SUPPORTED + * @param pDevObj Device object. + * @param pIrp IRP. + */ +NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + dprintf(("VBoxDrvNtNotSupportedStub\n")); + pDevObj = pDevObj; + + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_NOT_SUPPORTED; +} + + +/** + * ExRegisterCallback handler for power events + * + * @param pCallbackContext User supplied parameter (pDevObj) + * @param pArgument1 First argument + * @param pArgument2 Second argument + */ +VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2) +{ + PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCallbackContext; + + dprintf(("VBoxPowerDispatchCallback: %x %x\n", pArgument1, pArgument2)); + + /* Power change imminent? */ + if ((unsigned)pArgument1 == PO_CB_SYSTEM_STATE_LOCK) + { + if ((unsigned)pArgument2 == 0) + dprintf(("VBoxPowerDispatchCallback: about to go into suspend mode!\n")); + else + dprintf(("VBoxPowerDispatchCallback: resumed!\n")); + + /* Inform any clients that have registered themselves with IPRT. */ + UNWIND_WRAP(RTPowerSignalEvent)(((unsigned)pArgument2 == 0) ? RTPOWEREVENT_SUSPEND : RTPOWEREVENT_RESUME); + } +} + + +/** + * Initializes any OS specific object creator fields. + */ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +/** + * Checks if the session can access the object. + * + * @returns true if a decision has been made. + * @returns false if the default access policy should be applied. + * + * @param pObj The object in question. + * @param pSession The session wanting to access the object. + * @param pszObjName The object name, can be NULL. + * @param prc Where to store the result when returning true. + */ +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + + +/** + * Force async tsc mode (stub). + */ +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + return false; +} + + +/** + * Converts a supdrv error code to an nt status code. + * + * @returns corresponding nt status code. + * @param rc supdrv error code (SUPDRV_ERR_* defines). + */ +static NTSTATUS VBoxDrvNtErr2NtStatus(int rc) +{ + switch (rc) + { + case 0: return STATUS_SUCCESS; + case SUPDRV_ERR_GENERAL_FAILURE: return STATUS_NOT_SUPPORTED; + case SUPDRV_ERR_INVALID_PARAM: return STATUS_INVALID_PARAMETER; + case SUPDRV_ERR_INVALID_MAGIC: return STATUS_UNKNOWN_REVISION; + case SUPDRV_ERR_INVALID_HANDLE: return STATUS_INVALID_HANDLE; + case SUPDRV_ERR_INVALID_POINTER: return STATUS_INVALID_ADDRESS; + case SUPDRV_ERR_LOCK_FAILED: return STATUS_NOT_LOCKED; + case SUPDRV_ERR_ALREADY_LOADED: return STATUS_IMAGE_ALREADY_LOADED; + case SUPDRV_ERR_PERMISSION_DENIED: return STATUS_ACCESS_DENIED; + case SUPDRV_ERR_VERSION_MISMATCH: return STATUS_REVISION_MISMATCH; + } + + return STATUS_UNSUCCESSFUL; +} + + + +/** @todo use the nocrt stuff? */ +int VBOXCALL mymemcmp(const void *pv1, const void *pv2, size_t cb) +{ + const uint8_t *pb1 = (const uint8_t *)pv1; + const uint8_t *pb2 = (const uint8_t *)pv2; + for (; cb > 0; cb--, pb1++, pb2++) + if (*pb1 != *pb2) + return *pb1 - *pb2; + return 0; +} + + +#if 0 /* See alternative in SUPDrvA-win.asm */ +/** + * Alternative version of SUPR0Printf for Windows. + * + * @returns 0. + * @param pszFormat The format string. + */ +SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + va_list va; + char szMsg[512]; + + va_start(va, pszFormat); + size_t cch = RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va); + szMsg[sizeof(szMsg) - 1] = '\0'; + va_end(va); + + RTLogWriteDebugger(szMsg, cch); + return 0; +} +#endif diff --git a/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm new file mode 100644 index 000000000..dc28974c4 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm @@ -0,0 +1,328 @@ +; $Id$ +;; @file +; VirtualBox Support Driver - Windows NT specific assembly parts. +; + +; +; Copyright (C) 2006-2007 Sun Microsystems, Inc. +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +; Clara, CA 95054 USA or visit http://www.sun.com if you need +; additional information or have any questions. +; + +;******************************************************************************* +;* Header Files * +;******************************************************************************* +%include "iprt/ntwrap.mac" + +BEGINCODE +%ifdef RT_ARCH_AMD64 +%define _DbgPrint DbgPrint +%endif +extern _DbgPrint + +%if 1 ; see alternative in SUPDrv-win.cpp +;; +; Kind of alias for DbgPrint +BEGINPROC SUPR0Printf + jmp _DbgPrint +ENDPROC SUPR0Printf +%endif + + +%ifdef RT_WITH_W64_UNWIND_HACK + %ifdef RT_ARCH_AMD64 + +; +; This has the same order as the list in SUPDrv.c +; +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ComponentRegisterFactory +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ComponentDeregisterFactory +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ComponentQueryFactory +NtWrapDyn2DrvFunctionWith5Params supdrvNtWrap, SUPR0ObjRegister +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ObjAddRef +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ObjAddRefEx +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ObjRelease +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ObjVerifyAccess +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0LockMem +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0UnlockMem +NtWrapDyn2DrvFunctionWith5Params supdrvNtWrap, SUPR0ContAlloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0ContFree +NtWrapDyn2DrvFunctionWith5Params supdrvNtWrap, SUPR0LowAlloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0LowFree +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0MemAlloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0MemGetPhys +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0MemFree +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0PageAlloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0PageFree +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0Printf - cannot wrap this buster. +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, SUPR0GetPagingMode +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemAlloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemAllocZ +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemFree +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemDup +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemDupEx +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMemRealloc +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAllocLow +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAllocPage +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAllocPhys +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAllocPhysNC +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAllocCont +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjEnterPhys +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjLockUser +NtWrapDyn2DrvFunctionWith5Params supdrvNtWrap, RTR0MemObjMapKernel +NtWrapDyn2DrvFunctionWith7Params supdrvNtWrap, RTR0MemObjMapKernelEx +NtWrapDyn2DrvFunctionWith6Params supdrvNtWrap, RTR0MemObjMapUser +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAddress - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjAddressR3 - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjSize - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjIsMapping - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjGetPagePhysAddr - not necessary +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0MemObjFree +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTProcSelf - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTR0ProcHandleSelf - not necessary +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemFastMutexCreate +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemFastMutexDestroy +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemFastMutexRequest +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemFastMutexRelease +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventCreate +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventSignal +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventWait +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventWaitNoResume +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventDestroy +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiCreate +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiSignal +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiReset +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiWait +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiWaitNoResume +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSemEventMultiDestroy +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockCreate +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockDestroy +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockAcquire +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockRelease +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockAcquireNoInts +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTSpinlockReleaseNoInts +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTTimeNanoTS - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTTimeMilliTS - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTTimeSystemNanoTS - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTTimeSystemMilliTS - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadNativeSelf - not necessary +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadSleep +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadYield +%if 0 ; Thread APIs, Part 2 +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadSelf +NtWrapDyn2DrvFunctionWith7Params supdrvNtWrap, RTThreadCreate +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadGetNative +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadWait +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadWaitNoResume +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadGetName +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadSelfName +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadGetType +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadUserSignal +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadUserReset +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadUserWait +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTThreadUserWaitNoResume +%endif +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogDefaultInstance - a bit of a gamble, but we do not want the overhead! +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpCpuId - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpCpuIdFromSetIndex - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpCpuIdToSetIndex - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpIsCpuPossible - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpGetCount - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpGetMaxCpuId - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpGetOnlineCount - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpGetOnlineSet - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpGetSet - not necessary +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpIsCpuOnline - not necessary +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpOnAll +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpOnOthers +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpOnSpecific +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTMpIsCpuWorkPending +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogRelDefaultInstance - not necessary. +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogSetDefaultInstanceThread +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogLogger - can't wrap this buster. +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogLoggerEx - can't wrap this buster. +NtWrapDyn2DrvFunctionWith5Params supdrvNtWrap, RTLogLoggerExV +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogPrintf - can't wrap this buster. ;; @todo provide va_list log wrappers in RuntimeR0. +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, RTLogPrintfV +NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, AssertMsg1 +;NtWrapDyn2DrvFunctionWithAllRegParams supdrvNtWrap, AssertMsg2 - can't wrap this buster. +NtWrapDrv2DynFunctionWithAllRegParams supdrvNtWrap, RTPowerSignalEvent + + +;; +; @cproto DECLASM(int) supdrvNtWrapVMMR0EntryEx(PFNRT pfnVMMR0EntryEx, PVM pVM, unsigned uOperation, PSUPVMMR0REQHDR pReq, uint64_t u64Arg, PSUPDRVSESSION pSession); +; +; @param pfnVMMR0EntryEx rcx +; @param pVM rdx +; @param uOperation r8 +; @param pReq r9 +; @param u64Arg [rsp + 28h] / [rbp + 30h] +; @param pSession [rsp + 30h] / [rbp + 38h] +; +BEGINPROC supdrvNtWrapVMMR0EntryEx + NtWrapProlog supdrvNtWrapVMMR0EntryEx + NtWrapCreateMarker + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + mov r9, [rbp + 30h] + mov r11, [rbp + 38h] + mov [rsp + 20h], r11 + call rax + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapVMMR0EntryEx +ENDPROC supdrvNtWrapVMMR0EntryEx + + +;; +; @cproto DECLASM(int) supdrvNtWrapVMMR0EntryFast(PFNRT pfnVMMR0EntryFast, PVM pVM, unsigned idCPU, unsigned uOperation); +; +; @param pfnVMMR0EntryFast rcx +; @param pVM rdx +; @param idCPU r8 +; @param uOperation r9 +; +BEGINPROC supdrvNtWrapVMMR0EntryFast + NtWrapProlog supdrvNtWrapVMMR0EntryFast + NtWrapCreateMarker + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + call rax + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapVMMR0EntryFast +ENDPROC supdrvNtWrapVMMR0EntryFast + + +;; +; @cproto DECLASM(void) supdrvNtWrapObjDestructor(PFNRT pfnDestruction, void *pvObj, void *pvUser1, void *pvUser2); +; +; @param pfnDestruction rcx +; @param pvObj rdx +; @param pvUser1 r8 +; @param pvUser2 r9 +; +BEGINPROC supdrvNtWrapObjDestructor + NtWrapProlog supdrvNtWrapObjDestructor + NtWrapCreateMarker + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + call rax + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapObjDestructor +ENDPROC supdrvNtWrapObjDestructor + + +;; +; @cproto DECLASM(void *) supdrvNtWrapQueryFactoryInterface(PFNRT pfnQueryFactoryInterface, struct SUPDRVFACTORY const *pSupDrvFactory, +; PSUPDRVSESSION pSession, const char *pszInterfaceUuid); +; +; @param pfnQueryFactoryInterface rcx +; @param pSupDrvFactory rdx +; @param pSession r8 +; @param pszInterfaceUuid r9 +; +BEGINPROC supdrvNtWrapQueryFactoryInterface + NtWrapProlog supdrvNtWrapQueryFactoryInterface + NtWrapCreateMarker + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + call rax + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapQueryFactoryInterface +ENDPROC supdrvNtWrapQueryFactoryInterface + + +;; +; @cproto DECLASM(int) supdrvNtWrapModuleInit(PFNRT pfnModuleInit); +; +; @param pfnModuleInit rcx +; +BEGINPROC supdrvNtWrapModuleInit + NtWrapProlog supdrvNtWrapModuleInit + NtWrapCreateMarker + + call rcx + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapModuleInit +ENDPROC supdrvNtWrapModuleInit + + +;; +; @cproto DECLASM(void) supdrvNtWrapModuleTerm(PFNRT pfnModuleTerm); +; +; @param pfnModuleInit rcx +; +BEGINPROC supdrvNtWrapModuleTerm + NtWrapProlog supdrvNtWrapModuleTerm + NtWrapCreateMarker + + call rcx + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapModuleTerm +ENDPROC supdrvNtWrapModuleTerm + + +;; +; @cproto DECLASM(int) supdrvNtWrapServiceReqHandler(PFNRT pfnServiceReqHandler, PSUPDRVSESSION pSession, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr); +; +; @param pfnSerivceReqHandler rcx +; @param pSession rdx +; @param uOperation r8 +; @param u64Arg r9 +; @param pReq [rsp + 28h] / [rbp + 30h] +; +BEGINPROC supdrvNtWrapServiceReqHandler + NtWrapProlog supdrvNtWrapServiceReqHandler + NtWrapCreateMarker + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + mov r9, [rbp + 30h] + call rax + + NtWrapDestroyMarker + NtWrapEpilog supdrvNtWrapServiceReqHandler +ENDPROC supdrvNtWrapServiceReqHandler + + + %endif ; RT_ARCH_AMD64 +%endif ; RT_WITH_W64_UNWIND_HACK + diff --git a/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp new file mode 100644 index 000000000..1102570a6 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp @@ -0,0 +1,599 @@ +/* $Id: SUPLib-win.cpp 13865 2008-11-05 14:14:11Z vboxsync $ */ +/** @file + * VirtualBox Support Library - Windows NT specific parts. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include <iprt/log.h> +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#include <Windows.h> + +#include <VBox/sup.h> +#include <VBox/types.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** The support service name. */ +#define SERVICE_NAME "VBoxDrv" +/** Win32 Device name. */ +#define DEVICE_NAME "\\\\.\\VBoxDrv" +/** NT Device name. */ +#define DEVICE_NAME_NT L"\\Device\\VBoxDrv" +/** Win32 Symlink name. */ +#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxDrv" + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int suplibOsCreateService(void); +//unused: static int suplibOsUpdateService(void); +static int suplibOsDeleteService(void); +static int suplibOsStartService(void); +static int suplibOsStopService(void); +static int suplibConvertWin32Err(int); + + + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited) +{ + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + /* + * Try open the device. + */ + HANDLE hDevice = CreateFile(DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + if (hDevice == INVALID_HANDLE_VALUE) + { +#ifndef IN_SUP_HARDENED_R3 + /* + * Try start the service and retry opening it. + */ + suplibOsStartService(); + + hDevice = CreateFile(DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + if (hDevice == INVALID_HANDLE_VALUE) +#endif /* !IN_SUP_HARDENED_R3 */ + { + int rc = GetLastError(); + switch (rc) + { + /** @todo someone must test what is actually returned. */ + case ERROR_DEV_NOT_EXIST: + case ERROR_DEVICE_NOT_CONNECTED: + case ERROR_BAD_DEVICE: + case ERROR_DEVICE_REMOVED: + case ERROR_DEVICE_NOT_AVAILABLE: + return VERR_VM_DRIVER_LOAD_ERROR; + case ERROR_PATH_NOT_FOUND: + case ERROR_FILE_NOT_FOUND: + return VERR_VM_DRIVER_NOT_INSTALLED; + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + return VERR_VM_DRIVER_NOT_ACCESSIBLE; + default: + return VERR_VM_DRIVER_OPEN_ERROR; + } + + return -1 /** @todo define proper error codes for suplibOsInit failure. */; + } + } + + /* + * We're done. + */ + pThis->hDevice = (RTFILE)hDevice; + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsInstall(void) +{ + return suplibOsCreateService(); +} + + +int suplibOsUninstall(void) +{ + int rc = suplibOsStopService(); + if (!rc) + rc = suplibOsDeleteService(); + return rc; +} + + +/** + * Creates the service. + * + * @returns 0 on success. + * @returns -1 on failure. + */ +static int suplibOsCreateService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", LastError)); + if (hSMgrCreate) + { + char szDriver[RTPATH_MAX]; + int rc = RTPathProgram(szDriver, sizeof(szDriver) - sizeof("\\VBoxDrv.sys")); + if (RT_SUCCESS(rc)) + { + strcat(szDriver, "\\VBoxDrv.sys"); + SC_HANDLE hService = CreateService(hSMgrCreate, + SERVICE_NAME, + "VBox Support Driver", + SERVICE_QUERY_STATUS, + SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + szDriver, + NULL, NULL, NULL, NULL, NULL); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(hService, ("CreateService failed! LastError=%Rwa szDriver=%s\n", LastError, szDriver)); + CloseServiceHandle(hService); + CloseServiceHandle(hSMgrCreate); + return hService ? 0 : -1; + } + CloseServiceHandle(hSMgrCreate); + return rc; + } + return -1; +} + + +/** + * Stops a possibly running service. + * + * @returns 0 on success. + * @returns -1 on failure. + */ +static int suplibOsStopService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + int rc = -1; + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", LastError)); + if (hSMgr) + { + SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS); + if (hService) + { + /* + * Stop the service. + */ + SERVICE_STATUS Status; + QueryServiceStatus(hService, &Status); + if (Status.dwCurrentState == SERVICE_STOPPED) + rc = 0; + else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status)) + { + int iWait = 100; + while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0) + { + Sleep(100); + QueryServiceStatus(hService, &Status); + } + if (Status.dwCurrentState == SERVICE_STOPPED) + rc = 0; + else + AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState)); + } + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", LastError, Status.dwCurrentState)); + } + CloseServiceHandle(hService); + } + else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + rc = 0; + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError)); + } + CloseServiceHandle(hSMgr); + } + return rc; +} + + +/** + * Deletes the service. + * + * @returns 0 on success. + * @returns -1 on failure. + */ +int suplibOsDeleteService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + int rc = -1; + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", LastError)); + if (hSMgr) + { + SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, DELETE); + if (hService) + { + /* + * Delete the service. + */ + if (DeleteService(hService)) + rc = 0; + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("DeleteService failed LastError=%Rwa\n", LastError)); + } + CloseServiceHandle(hService); + } + else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + rc = 0; + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError)); + } + CloseServiceHandle(hSMgr); + } + return rc; +} + +#if 0 +/** + * Creates the service. + * + * @returns 0 on success. + * @returns -1 on failure. + */ +static int suplibOsUpdateService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed LastError=%Rwa\n", LastError)); + if (hSMgr) + { + SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_CHANGE_CONFIG); + if (hService) + { + char szDriver[RTPATH_MAX]; + int rc = RTPathProgram(szDriver, sizeof(szDriver) - sizeof("\\VBoxDrv.sys")); + if (RT_SUCCESS(rc)) + { + strcat(szDriver, "\\VBoxDrv.sys"); + + SC_LOCK hLock = LockServiceDatabase(hSMgr); + if (ChangeServiceConfig(hService, + SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + szDriver, + NULL, NULL, NULL, NULL, NULL, NULL)) + { + + UnlockServiceDatabase(hLock); + CloseServiceHandle(hService); + CloseServiceHandle(hSMgr); + return 0; + } + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("ChangeServiceConfig failed LastError=%Rwa\n", LastError)); + } + } + UnlockServiceDatabase(hLock); + CloseServiceHandle(hService); + } + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError)); + } + CloseServiceHandle(hSMgr); + } + return -1; +} +#endif + + +/** + * Attempts to start the service, creating it if necessary. + * + * @returns 0 on success. + * @returns -1 on failure. + * @param fRetry Indicates retry call. + */ +static int suplibOsStartService(void) +{ + /* + * Check if the driver service is there. + */ + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_START); + if (hSMgr == NULL) + { + AssertMsgFailed(("couldn't open service manager in SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS mode!\n")); + return -1; + } + + /* + * Try open our service to check it's status. + */ + SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START); + if (!hService) + { + /* + * Create the service. + */ + int rc = suplibOsCreateService(); + if (rc) + return rc; + + /* + * Try open the service. + */ + hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START); + } + + /* + * Check if open and on demand create succeeded. + */ + int rc = -1; + if (hService) + { + + /* + * Query service status to see if we need to start it or not. + */ + SERVICE_STATUS Status; + BOOL fRc = QueryServiceStatus(hService, &Status); + Assert(fRc); + if ( Status.dwCurrentState != SERVICE_RUNNING + && Status.dwCurrentState != SERVICE_START_PENDING) + { + /* + * Start it. + */ + fRc = StartService(hService, 0, NULL); + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(fRc, ("StartService failed with LastError=%Rwa\n", LastError)); + } + + /* + * Wait for the service to finish starting. + * We'll wait for 10 seconds then we'll give up. + */ + QueryServiceStatus(hService, &Status); + if (Status.dwCurrentState == SERVICE_START_PENDING) + { + int iWait; + for (iWait = 100; iWait > 0 && Status.dwCurrentState == SERVICE_START_PENDING; iWait--) + { + Sleep(100); + QueryServiceStatus(hService, &Status); + } + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsg(Status.dwCurrentState != SERVICE_RUNNING, + ("Failed to start. LastError=%Rwa iWait=%d status=%d\n", + LastError, iWait, Status.dwCurrentState)); + } + + if (Status.dwCurrentState == SERVICE_RUNNING) + rc = 0; + + /* + * Close open handles. + */ + CloseServiceHandle(hService); + } + else + { + DWORD LastError = GetLastError(); NOREF(LastError); + AssertMsgFailed(("OpenService failed! LastError=%Rwa\n", LastError)); + } + if (!CloseServiceHandle(hSMgr)) + AssertFailed(); + + return rc; +} + + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Check if we're initited at all. + */ + if (pThis->hDevice != NIL_RTFILE) + { + if (!CloseHandle((HANDLE)pThis->hDevice)) + AssertFailed(); + pThis->hDevice = NIL_RTFILE; + } + + return VINF_SUCCESS; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + /* + * Issue the device I/O control. + */ + PSUPREQHDR pHdr = (PSUPREQHDR)pvReq; + Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut)); + DWORD cbReturned = (ULONG)pHdr->cbOut; + if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, pvReq, pHdr->cbIn, pvReq, cbReturned, &cbReturned, NULL)) + return 0; + return suplibConvertWin32Err(GetLastError()); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + /* + * Issue device I/O control. + */ + DWORD cbReturned = 0; + if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, NULL, 0, (LPVOID)idCpu, 0, &cbReturned, NULL)) + return VINF_SUCCESS; + return suplibConvertWin32Err(GetLastError()); +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (*ppvPages) + return VINF_SUCCESS; + return suplibConvertWin32Err(GetLastError()); +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + if (VirtualFree(pvPages, 0, MEM_RELEASE)) + return VINF_SUCCESS; + return suplibConvertWin32Err(GetLastError()); +} + + +/** + * Converts a supdrv error code to an nt status code. + * + * @returns corresponding SUPDRV_ERR_*. + * @param rc Win32 error code. + */ +static int suplibConvertWin32Err(int rc) +{ + /* Conversion program (link with ntdll.lib from ddk): + #define _WIN32_WINNT 0x0501 + #include <windows.h> + #include <ntstatus.h> + #include <winternl.h> + #include <stdio.h> + + int main() + { + #define CONVERT(a) printf(#a " %#x -> %d\n", a, RtlNtStatusToDosError((a))) + CONVERT(STATUS_SUCCESS); + CONVERT(STATUS_NOT_SUPPORTED); + CONVERT(STATUS_INVALID_PARAMETER); + CONVERT(STATUS_UNKNOWN_REVISION); + CONVERT(STATUS_INVALID_HANDLE); + CONVERT(STATUS_INVALID_ADDRESS); + CONVERT(STATUS_NOT_LOCKED); + CONVERT(STATUS_IMAGE_ALREADY_LOADED); + CONVERT(STATUS_ACCESS_DENIED); + CONVERT(STATUS_REVISION_MISMATCH); + + return 0; + } + */ + + switch (rc) + { + //case 0: return STATUS_SUCCESS; + case 0: return VINF_SUCCESS; + //case SUPDRV_ERR_GENERAL_FAILURE: return STATUS_NOT_SUPPORTED; + case ERROR_NOT_SUPPORTED: return VERR_GENERAL_FAILURE; + //case SUPDRV_ERR_INVALID_PARAM: return STATUS_INVALID_PARAMETER; + case ERROR_INVALID_PARAMETER: return VERR_INVALID_PARAMETER; + //case SUPDRV_ERR_INVALID_MAGIC: return STATUS_ACCESS_DENIED; + case ERROR_UNKNOWN_REVISION: return VERR_INVALID_MAGIC; + //case SUPDRV_ERR_INVALID_HANDLE: return STATUS_INVALID_HANDLE; + case ERROR_INVALID_HANDLE: return VERR_INVALID_HANDLE; + //case SUPDRV_ERR_INVALID_POINTER: return STATUS_INVALID_ADDRESS; + case ERROR_UNEXP_NET_ERR: return VERR_INVALID_POINTER; + //case SUPDRV_ERR_LOCK_FAILED: return STATUS_NOT_LOCKED; + case ERROR_NOT_LOCKED: return VERR_LOCK_FAILED; + //case SUPDRV_ERR_ALREADY_LOADED: return STATUS_IMAGE_ALREADY_LOADED; + case ERROR_SERVICE_ALREADY_RUNNING: return VERR_ALREADY_LOADED; + //case SUPDRV_ERR_PERMISSION_DENIED: return STATUS_ACCESS_DENIED; + case ERROR_ACCESS_DENIED: return VERR_PERMISSION_DENIED; + //case SUPDRV_ERR_VERSION_MISMATCH: return STATUS_REVISION_MISMATCH; + case ERROR_REVISION_MISMATCH: return VERR_VERSION_MISMATCH; + } + + /* fall back on the default conversion. */ + return RTErrConvertFromWin32(rc); +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c b/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c new file mode 100644 index 000000000..6021ecdd6 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPR0IdcClient-win.c @@ -0,0 +1,160 @@ +/* $Id: SUPR0IdcClient-win.c 10263 2008-07-05 00:26:17Z vboxsync $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Windows Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <VBox/err.h> + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** NT Device name. */ +#define DEVICE_NAME_NT L"\\Device\\VBoxDrv" + + +/** + * Internal I/O Control call worker. + * + * @returns VBox status code. + * @param pDeviceObject The device object to call. + * @param pFileObject The file object for the connection. + * @param uReq The request. + * @param pReq The request packet. + */ +static int supR0IdcNtCallInternal(PDEVICE_OBJECT pDeviceObject, PFILE_OBJECT pFileObject, uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + int rc; + IO_STATUS_BLOCK IoStatusBlock; + KEVENT Event; + PIRP pIrp; + NTSTATUS rcNt; + + /* + * Build the request. + */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */ + pDeviceObject, + pReq, /* InputBuffer */ + pReq->cb, /* InputBufferLength */ + pReq, /* OutputBuffer */ + pReq->cb, /* OutputBufferLength */ + TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */ + &Event, /* Event */ + &IoStatusBlock); /* IoStatusBlock */ + if (pIrp) + { + IoGetNextIrpStackLocation(pIrp)->FileObject = pFileObject; + + /* + * Call the driver, wait for an async request to complete (should never happen). + */ + rcNt = IoCallDriver(pDeviceObject, pIrp); + if (rcNt == STATUS_PENDING) + { + rcNt = KeWaitForSingleObject(&Event, /* Object */ + Executive, /* WaitReason */ + KernelMode, /* WaitMode */ + FALSE, /* Altertable */ + NULL); /* TimeOut */ + rcNt = IoStatusBlock.Status; + } + if (NT_SUCCESS(rcNt)) + rc = pReq->rc; + else + rc = RTErrConvertFromNtStatus(rcNt); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + PDEVICE_OBJECT pDeviceObject = NULL; + PFILE_OBJECT pFileObject = NULL; + UNICODE_STRING wszDeviceName; + NTSTATUS rcNt; + int rc; + + /* + * Get the device object pointer. + */ + RtlInitUnicodeString(&wszDeviceName, DEVICE_NAME_NT); + rcNt = IoGetDeviceObjectPointer(&wszDeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); + if (NT_SUCCESS(rcNt)) + { + /* + * Make the connection call. + */ + rc = supR0IdcNtCallInternal(pDeviceObject, pFileObject, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); + if (RT_SUCCESS(rc)) + { + pHandle->s.pDeviceObject = pDeviceObject; + pHandle->s.pFileObject = pFileObject; + return rc; + } + + /* only the file object. */ + ObDereferenceObject(pFileObject); + } + else + rc = RTErrConvertFromNtStatus(rcNt); + + pHandle->s.pDeviceObject = NULL; + pHandle->s.pFileObject = NULL; + return rc; +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + PFILE_OBJECT pFileObject = pHandle->s.pFileObject; + int rc = supR0IdcNtCallInternal(pHandle->s.pDeviceObject, pFileObject, SUPDRV_IDC_REQ_DISCONNECT, pReq); + if (RT_SUCCESS(rc)) + { + pHandle->s.pDeviceObject = NULL; + pHandle->s.pFileObject = NULL; + ObDereferenceObject(pFileObject); + } + + return rc; +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNtCallInternal(pHandle->s.pDeviceObject, pHandle->s.pFileObject, uReq, pReq); +} + diff --git a/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp b/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp new file mode 100644 index 000000000..fae7d49d0 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPSvc-win.cpp @@ -0,0 +1,900 @@ +/* $Id: SUPSvc-win.cpp 14303 2008-11-18 13:45:38Z vboxsync $ */ +/** @file + * VirtualBox Support Service - Windows Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#include <Windows.h> + +#include <VBox/log.h> +#include <VBox/version.h> +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/getopt.h> +#include <iprt/semaphore.h> +#ifdef DEBUG_bird +# include <iprt/env.h> +#endif + +#include "../SUPSvcInternal.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** The service name. */ +#define SUPSVC_SERVICE_NAME "VBoxSupSvc" +/** The service display name. */ +#define SUPSVC_SERVICE_DISPLAY_NAME "VirtualBox Support Service" + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The service control handler handle. */ +static SERVICE_STATUS_HANDLE g_hSupSvcWinCtrlHandler = NULL; +/** The service status. */ +static uint32_t volatile g_u32SupSvcWinStatus = SERVICE_STOPPED; +/** The semaphore the main service thread is waiting on in supSvcWinServiceMain. */ +static RTSEMEVENTMULTI g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess); + + +/** + * Opens the service control manager. + * + * When this fails, an error message will be displayed. + * + * @returns Valid handle on success. + * NULL on failure, will display an error message. + * + * @param pszAction The action which is requesting access to SCM. + * @param dwAccess The desired access. + */ +static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess) +{ + SC_HANDLE hSCM = OpenSCManager(NULL /* lpMachineName*/, NULL /* lpDatabaseName */, dwAccess); + if (hSCM == NULL) + { + DWORD err = GetLastError(); + switch (err) + { + case ERROR_ACCESS_DENIED: + supSvcDisplayError("%s - OpenSCManager failure: access denied\n", pszAction); + break; + default: + supSvcDisplayError("%s - OpenSCManager failure: %d\n", pszAction, err); + break; + } + } + return hSCM; +} + + +/** + * Opens the service. + * + * Last error is preserved on failure and set to 0 on success. + * + * @returns Valid service handle on success. + * NULL on failure, will display an error message unless it's ignored. + * + * @param pszAction The action which is requestion access to the service. + * @param dwSCMAccess The service control manager access. + * @param dwSVCAccess The desired service access. + * @param cIgnoredErrors The number of ignored errors. + * @param ... Errors codes that should not cause a message to be displayed. + */ +static SC_HANDLE supSvcWinOpenService(const char *pszAction, DWORD dwSCMAccess, DWORD dwSVCAccess, + unsigned cIgnoredErrors, ...) +{ + SC_HANDLE hSCM = supSvcWinOpenSCManager(pszAction, dwSCMAccess); + if (!hSCM) + return NULL; + + SC_HANDLE hSvc = OpenService(hSCM, SUPSVC_SERVICE_NAME, dwSVCAccess); + if (hSvc) + { + CloseServiceHandle(hSCM); + SetLastError(0); + } + else + { + DWORD err = GetLastError(); + bool fIgnored = false; + va_list va; + va_start(va, cIgnoredErrors); + while (!fIgnored && cIgnoredErrors-- > 0) + fIgnored = va_arg(va, long) == err; + va_end(va); + if (!fIgnored) + { + switch (err) + { + case ERROR_ACCESS_DENIED: + supSvcDisplayError("%s - OpenService failure: access denied\n", pszAction); + break; + case ERROR_SERVICE_DOES_NOT_EXIST: + supSvcDisplayError("%s - OpenService failure: The service does not exist. Reinstall it.\n", pszAction); + break; + default: + supSvcDisplayError("%s - OpenService failure: %d\n", pszAction, err); + break; + } + } + + CloseServiceHandle(hSCM); + SetLastError(err); + } + return hSvc; +} + + + +void supSvcOsLogErrorStr(const char *pszMsg) +{ + HANDLE hEventLog = RegisterEventSource(NULL /* local computer */, "VBoxSupSvc"); + AssertReturnVoid(hEventLog != NULL); + const char *apsz[2]; + apsz[0] = "VBoxSupSvc"; + apsz[1] = pszMsg; + BOOL fRc = ReportEvent(hEventLog, /* hEventLog */ + EVENTLOG_ERROR_TYPE, /* wType */ + 0, /* wCategory */ + 0 /** @todo mc */, /* dwEventID */ + NULL, /* lpUserSid */ + RT_ELEMENTS(apsz), /* wNumStrings */ + 0, /* dwDataSize */ + apsz, /* lpStrings */ + NULL); /* lpRawData */ + AssertMsg(fRc, ("%d\n", GetLastError())); + DeregisterEventSource(hEventLog); +} + + +static int supSvcWinInterrogate(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"interrogate\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinStop(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"stop\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinContinue(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"continue\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinPause(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"pause\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinStart(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"start\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinQueryDescription(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"qdescription\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinQueryConfig(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"qconfig\" action is not implemented.\n"); + return 1; +} + + +static int supSvcWinDisable(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"disable\" action is not implemented.\n"); + return 1; +} + +static int supSvcWinEnable(int argc, char **argv) +{ + RTPrintf("VBoxSupSvc: The \"enable\" action is not implemented.\n"); + return 1; +} + + +/** + * Handle the 'delete' action. + * + * @returns 0 or 1. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static int supSvcWinDelete(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fVerbose = false; + static const RTOPTIONDEF s_aOptions[] = + { + { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTOPTIONUNION Value; + while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value))) + switch (ch) + { + case 'v': fVerbose = true; break; + default: return supSvcDisplayGetOptError("delete", ch, argc, argv, iArg, &Value); + } + if (iArg != argc) + return supSvcDisplayTooManyArgsError("delete", argc, argv, iArg); + + /* + * Create the service. + */ + int rc = 1; + SC_HANDLE hSvc = supSvcWinOpenService("delete", SERVICE_CHANGE_CONFIG, DELETE, + 1, ERROR_SERVICE_DOES_NOT_EXIST); + if (hSvc) + { + if (DeleteService(hSvc)) + { + RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME); + rc = 0; + } + else + supSvcDisplayError("delete - DeleteService failed, err=%d.\n", GetLastError()); + CloseServiceHandle(hSvc); + } + else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + { + + if (fVerbose) + RTPrintf("The service %s was not installed, nothing to be done.", SUPSVC_SERVICE_NAME); + else + RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME); + rc = 0; + } + return rc; +} + + +/** + * Handle the 'create' action. + * + * @returns 0 or 1. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static int supSvcWinCreate(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fVerbose = false; + static const RTOPTIONDEF s_aOptions[] = + { + { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTOPTIONUNION Value; + while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value))) + switch (ch) + { + case 'v': fVerbose = true; break; + default: return supSvcDisplayGetOptError("create", ch, argc, argv, iArg, &Value); + } + if (iArg != argc) + return supSvcDisplayTooManyArgsError("create", argc, argv, iArg); + + /* + * Create the service. + */ + int rc = 1; + SC_HANDLE hSCM = supSvcWinOpenSCManager("create", SC_MANAGER_CREATE_SERVICE); /*SC_MANAGER_ALL_ACCESS*/ + if (hSCM) + { + char szExecPath[MAX_PATH]; + if (GetModuleFileName(NULL /* the executable */, szExecPath, sizeof(szExecPath))) + { + if (fVerbose) + RTPrintf("Creating the %s service, binary \"%s\"...\n", + SUPSVC_SERVICE_NAME, szExecPath); /* yea, the binary name isn't UTF-8, but wtf. */ + + SC_HANDLE hSvc = CreateService(hSCM, /* hSCManager */ + SUPSVC_SERVICE_NAME, /* lpServiceName */ + SUPSVC_SERVICE_DISPLAY_NAME, /* lpDisplayName */ + SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, /* dwDesiredAccess */ + SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */ + SERVICE_DEMAND_START/*_AUTO*/, /* dwStartType */ + SERVICE_ERROR_NORMAL, /* dwErrorControl */ + szExecPath, /* lpBinaryPathName */ + NULL, /* lpLoadOrderGroup */ + NULL, /* lpdwTagId */ + NULL, /* lpDependencies */ + NULL, /* lpServiceStartName (=> LocalSystem) */ + NULL); /* lpPassword */ + if (hSvc) + { + RTPrintf("Successfully created the %s service.\n", SUPSVC_SERVICE_NAME); + /** @todo Set the service description or it'll look weird in the vista service manager. + * Anything else that should be configured? Start access or something? */ + rc = 0; + CloseServiceHandle(hSvc); + } + else + { + DWORD err = GetLastError(); + switch (err) + { + case ERROR_SERVICE_EXISTS: + supSvcDisplayError("create - The service already exists.\n"); + break; + default: + supSvcDisplayError("create - CreateService failed, err=%d.\n", GetLastError()); + break; + } + } + CloseServiceHandle(hSvc); + } + else + supSvcDisplayError("create - Failed to obtain the executable path: %d\n", GetLastError()); + } + return rc; +} + + +/** + * Sets the service status, just a SetServiceStatus Wrapper. + * + * @returns See SetServiceStatus. + * @param dwStatus The current status. + * @param iWaitHint The wait hint, if < 0 then supply a default. + * @param dwExitCode The service exit code. + */ +static bool supSvcWinSetServiceStatus(DWORD dwStatus, int iWaitHint, DWORD dwExitCode) +{ + SERVICE_STATUS SvcStatus; + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SvcStatus.dwWin32ExitCode = dwExitCode; + SvcStatus.dwServiceSpecificExitCode = 0; + SvcStatus.dwWaitHint = iWaitHint >= 0 ? iWaitHint : 3000; + SvcStatus.dwCurrentState = dwStatus; + LogFlow(("supSvcWinSetServiceStatus: %d -> %d\n", g_u32SupSvcWinStatus, dwStatus)); + g_u32SupSvcWinStatus = dwStatus; + switch (dwStatus) + { + case SERVICE_START_PENDING: + SvcStatus.dwControlsAccepted = 0; + break; + default: + SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + break; + } + + static DWORD dwCheckPoint = 0; + switch (dwStatus) + { + case SERVICE_RUNNING: + case SERVICE_STOPPED: + SvcStatus.dwCheckPoint = 0; + default: + SvcStatus.dwCheckPoint = ++dwCheckPoint; + break; + } + return SetServiceStatus(g_hSupSvcWinCtrlHandler, &SvcStatus) != FALSE; +} + + +/** + * Service control handler (extended). + * + * @returns Windows status (see HandlerEx). + * @retval NO_ERROR if handled. + * @retval ERROR_CALL_NOT_IMPLEMENTED if not handled. + * + * @param dwControl The control code. + * @param dwEventType Event type. (specific to the control?) + * @param pvEventData Event data, specfic to the event. + * @param pvContext The context pointer registered with the handler. + * Currently not used. + */ +static DWORD WINAPI supSvcWinServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID pvContext) +{ + LogFlow(("supSvcWinServiceCtrlHandlerEx: dwControl=%#x dwEventType=%#x pvEventData=%p\n", + dwControl, dwEventType, pvEventData)); + + switch (dwControl) + { + /* + * Interrogate the service about it's current status. + * MSDN says that this should just return NO_ERROR and does + * not need to set the status again. + */ + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + /* + * Request to stop the service. + */ + case SERVICE_CONTROL_STOP: + { + /* + * Check if the real services can be stopped and then tell them to stop. + */ + supSvcWinSetServiceStatus(SERVICE_STOP_PENDING, 3000, NO_ERROR); + int rc = supSvcTryStopServices(); + if (RT_SUCCESS(rc)) + { + /* + * Notify the main thread that we're done, it will wait for the + * real services to stop, destroy them, and finally set the windows + * service status to SERVICE_STOPPED and return. + */ + rc = RTSemEventMultiSignal(g_hSupSvcWinEvent); + if (RT_FAILURE(rc)) + supSvcLogError("SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc); + } + return NO_ERROR; + } + + case SERVICE_CONTROL_PAUSE: + case SERVICE_CONTROL_CONTINUE: + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_PARAMCHANGE: + case SERVICE_CONTROL_NETBINDADD: + case SERVICE_CONTROL_NETBINDREMOVE: + case SERVICE_CONTROL_NETBINDENABLE: + case SERVICE_CONTROL_NETBINDDISABLE: + case SERVICE_CONTROL_DEVICEEVENT: + case SERVICE_CONTROL_HARDWAREPROFILECHANGE: + case SERVICE_CONTROL_POWEREVENT: + case SERVICE_CONTROL_SESSIONCHANGE: +#ifdef SERVICE_CONTROL_PRESHUTDOWN /* vista */ + case SERVICE_CONTROL_PRESHUTDOWN: +#endif + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } + + NOREF(dwEventType); + NOREF(pvEventData); + NOREF(pvContext); + return NO_ERROR; +} + + +/** + * Windows Service Main. + * + * This is invoked when the service is started and should not return until + * the service has been stopped. + * + * @param cArgs Argument count. + * @param papszArgs Argument vector. + */ +static VOID WINAPI supSvcWinServiceMain(DWORD cArgs, LPSTR *papszArgs) +{ + LogFlowFuncEnter(); + + /* + * Register the control handler function for the service and report to SCM. + */ + Assert(g_u32SupSvcWinStatus == SERVICE_STOPPED); + g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerEx(SUPSVC_SERVICE_NAME, supSvcWinServiceCtrlHandlerEx, NULL); + if (g_hSupSvcWinCtrlHandler) + { + DWORD err = ERROR_GEN_FAILURE; + if (supSvcWinSetServiceStatus(SERVICE_START_PENDING, 3000, NO_ERROR)) + { + /* + * Parse arguments. + */ + static const RTOPTIONDEF s_aOptions[] = + { + { "--dummy", 'd', RTGETOPT_REQ_NOTHING } + }; + int iArg = 1; /* the first arg is the service name */ + int ch; + int rc = 0; + RTOPTIONUNION Value; + while ( !rc + && (ch = RTGetOpt(cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value))) + switch (ch) + { + default: rc = supSvcLogGetOptError("main", ch, cArgs, papszArgs, iArg, &Value); break; + } + if (iArg != cArgs) + rc = supSvcLogTooManyArgsError("main", cArgs, papszArgs, iArg); + if (!rc) + { + /* + * Create the event semaphore we'll be waiting on and + * then instantiate the actual services. + */ + int rc = RTSemEventMultiCreate(&g_hSupSvcWinEvent); + if (RT_SUCCESS(rc)) + { + rc = supSvcCreateAndStartServices(); + if (RT_SUCCESS(rc)) + { + /* + * Update the status and enter the work loop. + * + * The work loop is just a dummy wait here as the services run + * in independant threads. + */ + if (supSvcWinSetServiceStatus(SERVICE_RUNNING, 0, 0)) + { + LogFlow(("supSvcWinServiceMain: calling RTSemEventMultiWait\n")); + rc = RTSemEventMultiWait(g_hSupSvcWinEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + LogFlow(("supSvcWinServiceMain: woke up\n")); + err = NO_ERROR; + } + else + supSvcLogError("RTSemEventWait failed, rc=%Rrc", rc); + } + else + { + err = GetLastError(); + supSvcLogError("SetServiceStatus failed, err=%d", err); + } + + /* + * Destroy the service instances, stopping them if + * they're still running (weird failure cause). + */ + supSvcStopAndDestroyServices(); + } + + RTSemEventMultiDestroy(g_hSupSvcWinEvent); + g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI; + } + else + supSvcLogError("RTSemEventMultiCreate failed, rc=%Rrc", rc); + } + /* else: bad args */ + } + else + { + err = GetLastError(); + supSvcLogError("SetServiceStatus failed, err=%d", err); + } + supSvcWinSetServiceStatus(SERVICE_STOPPED, 0, err); + } + else + supSvcLogError("RegisterServiceCtrlHandlerEx failed, err=%d", GetLastError()); + LogFlowFuncLeave(); +} + + +/** + * Handle the 'create' action. + * + * @returns 0 or 1. + * @param argc The action argument count. + * @param argv The action argument vector. + */ +static int supSvcWinRunIt(int argc, char **argv) +{ + LogFlowFuncEnter(); + + /* + * Initialize release logging. + */ + /** @todo release logging of the system-wide service. */ + + /* + * Parse the arguments. + */ + static const RTOPTIONDEF s_aOptions[] = + { + { "--dummy", 'd', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTOPTIONUNION Value; + while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value))) + switch (ch) + { + default: return supSvcDisplayGetOptError("runit", ch, argc, argv, iArg, &Value); + } + if (iArg != argc) + return supSvcDisplayTooManyArgsError("runit", argc, argv, iArg); + + /* + * Register the service with the service control manager + * and start dispatching requests from it (all done by the API). + */ + static SERVICE_TABLE_ENTRY const s_aServiceStartTable[] = + { + { SUPSVC_SERVICE_NAME, supSvcWinServiceMain }, + { NULL, NULL} + }; + if (StartServiceCtrlDispatcher(&s_aServiceStartTable[0])) + { + LogFlowFuncLeave(); + return 0; /* told to quit, so quit. */ + } + + DWORD err = GetLastError(); + switch (err) + { + case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: + supSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n"); + break; + default: + supSvcLogError("StartServiceCtrlDispatcher failed, err=%d", err); + break; + } + return 1; +} + + +/** + * Show the version info. + * + * @returns 0. + */ +static int supSvcWinShowVersion(int argc, char **argv) +{ + /* + * Parse the arguments. + */ + bool fBrief = false; + static const RTOPTIONDEF s_aOptions[] = + { + { "--brief", 'b', RTGETOPT_REQ_NOTHING } + }; + int iArg = 0; + int ch; + RTOPTIONUNION Value; + while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value))) + switch (ch) + { + case 'b': fBrief = true; break; + default: return supSvcDisplayGetOptError("version", ch, argc, argv, iArg, &Value); + + } + if (iArg != argc) + return supSvcDisplayTooManyArgsError("version", argc, argv, iArg); + + /* + * Do the printing. + */ + if (fBrief) + RTPrintf("%s\n", VBOX_VERSION_STRING); + else + RTPrintf("VirtualBox System Service Version %s\n" + "(C) 2008 Sun Microsystems, Inc.\n" + "All rights reserved.\n", + VBOX_VERSION_STRING); + return 0; +} + + +/** + * Show the usage help screen. + * + * @returns 0. + */ +static int supSvcWinShowHelp(void) +{ + RTPrintf("VirtualBox System Service Version %s\n" + "(C) 2008 Sun Microsystems, Inc.\n" + "All rights reserved.\n" + "\n", + VBOX_VERSION_STRING); + RTPrintf("Usage:\n" + "\n" + "VBoxSupSvc\n" + " Runs the service.\n" + "VBoxSupSvc <version|-v|--version> [-brief]\n" + " Displays the version.\n" + "VBoxSupSvc <help|-?|-h|--help> [...]\n" + " Displays this help screen.\n" + "\n" + "VBoxSupSvc <install|/RegServer|/i>\n" + " Installs the service.\n" + "VBoxSupSvc <install|delete|/UnregServer|/u>\n" + " Uninstalls the service.\n" + ); + return 0; +} + + +/** + * VBoxSUPSvc main(), Windows edition. + * + * + * @returns 0 on success. + * + * @param argc Number of arguments in argv. + * @param argv Argument vector. + */ +int main(int argc, char **argv) +{ + /* + * Initialize the IPRT first of all. + */ +#ifdef DEBUG_bird + RTEnvSet("VBOX_LOG", "sup=~0"); + RTEnvSet("VBOX_LOG_DEST", "file=E:\\temp\\VBoxSupSvc.log"); + RTEnvSet("VBOX_LOG_FLAGS", "unbuffered thread msprog"); +#endif + int rc = RTR3Init(); + if (RT_FAILURE(rc)) + { + supSvcLogError("RTR3Init failed with rc=%Rrc", rc); + return 1; + } + + /* + * Parse the initial arguments to determin the desired action. + */ + enum + { + kSupSvcAction_RunIt, + + kSupSvcAction_Create, + kSupSvcAction_Delete, + + kSupSvcAction_Enable, + kSupSvcAction_Disable, + kSupSvcAction_QueryConfig, + kSupSvcAction_QueryDescription, + + kSupSvcAction_Start, + kSupSvcAction_Pause, + kSupSvcAction_Continue, + kSupSvcAction_Stop, + kSupSvcAction_Interrogate, + + kSupSvcAction_End + } enmAction = kSupSvcAction_RunIt; + int iArg = 1; + if (argc > 1) + { + if ( !stricmp(argv[iArg], "/RegServer") + || !stricmp(argv[iArg], "install") + || !stricmp(argv[iArg], "/i")) + enmAction = kSupSvcAction_Create; + else if ( !stricmp(argv[iArg], "/UnregServer") + || !stricmp(argv[iArg], "/u") + || !stricmp(argv[iArg], "uninstall") + || !stricmp(argv[iArg], "delete")) + enmAction = kSupSvcAction_Delete; + + else if (!stricmp(argv[iArg], "enable")) + enmAction = kSupSvcAction_Enable; + else if (!stricmp(argv[iArg], "disable")) + enmAction = kSupSvcAction_Disable; + else if (!stricmp(argv[iArg], "qconfig")) + enmAction = kSupSvcAction_QueryConfig; + else if (!stricmp(argv[iArg], "qdescription")) + enmAction = kSupSvcAction_QueryDescription; + + else if ( !stricmp(argv[iArg], "start") + || !stricmp(argv[iArg], "/t")) + enmAction = kSupSvcAction_Start; + else if (!stricmp(argv[iArg], "pause")) + enmAction = kSupSvcAction_Start; + else if (!stricmp(argv[iArg], "continue")) + enmAction = kSupSvcAction_Continue; + else if (!stricmp(argv[iArg], "stop")) + enmAction = kSupSvcAction_Stop; + else if (!stricmp(argv[iArg], "interrogate")) + enmAction = kSupSvcAction_Interrogate; + else if ( !stricmp(argv[iArg], "help") + || !stricmp(argv[iArg], "?") + || !stricmp(argv[iArg], "/?") + || !stricmp(argv[iArg], "-?") + || !stricmp(argv[iArg], "/h") + || !stricmp(argv[iArg], "-h") + || !stricmp(argv[iArg], "/help") + || !stricmp(argv[iArg], "-help") + || !stricmp(argv[iArg], "--help")) + return supSvcWinShowHelp(); + else if ( !stricmp(argv[iArg], "version") + || !stricmp(argv[iArg], "/v") + || !stricmp(argv[iArg], "-v") + || !stricmp(argv[iArg], "/version") + || !stricmp(argv[iArg], "-version") + || !stricmp(argv[iArg], "--version")) + return supSvcWinShowVersion(argc - iArg - 1, argv + iArg + 1); + else + iArg--; + iArg++; + } + + /* + * Dispatch it. + */ + switch (enmAction) + { + case kSupSvcAction_RunIt: + return supSvcWinRunIt(argc - iArg, argv + iArg); + + case kSupSvcAction_Create: + return supSvcWinCreate(argc - iArg, argv + iArg); + case kSupSvcAction_Delete: + return supSvcWinDelete(argc - iArg, argv + iArg); + + case kSupSvcAction_Enable: + return supSvcWinEnable(argc - iArg, argv + iArg); + case kSupSvcAction_Disable: + return supSvcWinDisable(argc - iArg, argv + iArg); + case kSupSvcAction_QueryConfig: + return supSvcWinQueryConfig(argc - iArg, argv + iArg); + case kSupSvcAction_QueryDescription: + return supSvcWinQueryDescription(argc - iArg, argv + iArg); + + case kSupSvcAction_Start: + return supSvcWinStart(argc - iArg, argv + iArg); + case kSupSvcAction_Pause: + return supSvcWinPause(argc - iArg, argv + iArg); + case kSupSvcAction_Continue: + return supSvcWinContinue(argc - iArg, argv + iArg); + case kSupSvcAction_Stop: + return supSvcWinStop(argc - iArg, argv + iArg); + case kSupSvcAction_Interrogate: + return supSvcWinInterrogate(argc - iArg, argv + iArg); + + default: + AssertMsgFailed(("enmAction=%d\n", enmAction)); + return 1; + } +} + diff --git a/src/VBox/HostDrivers/Support/win/VBoxDrv.inf b/src/VBox/HostDrivers/Support/win/VBoxDrv.inf new file mode 100644 index 000000000..f2abe5db8 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/VBoxDrv.inf @@ -0,0 +1,73 @@ +; +; VBox host drivers - Ring-0 support drivers - Win32 host +; +; INF file for installing the Win32 driver +; +; Copyright (C) 2006-2007 Sun Microsystems, Inc. +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +; Clara, CA 95054 USA or visit http://www.sun.com if you need +; additional information or have any questions. +; + +[Version] +Signature="$Windows NT$" +Class=System +ClassGuid={ce4a90b5-1d8c-435d-b349-232ce55cb17f} +Provider=%SUN% +;edit-DriverVer=08/26/2008,2.00.0000 +DriverPackageType=KernelService +;cat CatalogFile=VBoxDrv.cat + +[DestinationDirs] +DefaultDestDir = 12 + +[DefaultInstall] +CopyFiles=VBoxDrv.CopyFiles + +[DefaultInstall.Services] +AddService=VBoxDrv,0x00000002,VBoxDrv_Service + +[Manufacturer] +;x86 %SUN%=SUN +;amd64 %SUN%=SUN, NTamd64 + +[SourceDisksFiles] +VBoxDrv.sys=1 + +[SourceDisksNames] +1=%DISK_NAME%, + +[VBoxDrv.CopyFiles] +VBoxDrv.sys + +[VBoxDrv_Service] +DisplayName = %VBoxDrv.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +;StartType = 3 ; SERVICE_DEMAND_START +StartType = 1 ; autostart to fix Vista problem +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxDrv.sys + +[Strings] +SUN = "Sun Microsystems, Inc." +VBoxDrv.SVCDESC = "VirtualBox Service" +VBoxDrv.DRVDESC = "VirtualBox Driver" +DISK_NAME = "VirtualBox Driver Installation Disk" diff --git a/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk new file mode 100644 index 000000000..0f1fdf72d --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk @@ -0,0 +1,314 @@ +# $Id: Makefile.kmk 15949 2009-01-14 18:21:43Z vboxsync $ +## @file +# Sub-Makefile for the Network Filter Driver (VBoxNetFlt). +# + +# +# Copyright (C) 2008 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + + +if1of ($(KBUILD_TARGET), darwin win) +# +# VBoxNetFlt.sys - The mixed case driver. +# +SYSMODS += VBoxNetFlt +VBoxNetFlt_TEMPLATE = VBOXR0DRV +VBoxNetFlt_INST = $(INST_VBOXNETFLT)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/) +if defined(VBOX_SIGNING_MODE) && "$(KBUILD_TARGET)" == "win" + VBoxNetFlt_NOINST = true +endif +VBoxNetFlt_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV) IN_SUP_STATIC +VBoxNetFlt_SDKS.win = W2K3DDK WINPSDKINCS +VBoxNetFlt_INCS := $(PATH_SUB_CURRENT) +VBoxNetFlt_SOURCES.darwin = darwin/VBoxNetFlt-darwin.cpp +VBoxNetFlt_SOURCES.win = \ + win/VBoxNetFlt-win.c \ + win/VBoxNetFltPt-win.c \ + win/VBoxNetFlt-win.rc +ifdef VBOX_NETFLT_ONDEMAND_BIND + VBoxNetFlt_DEFS.win += VBOX_NETFLT_ONDEMAND_BIND +else + VBoxNetFlt_DEFS.win += VBOXNETFLT_STATIC_CONFIG + VBoxNetFlt_SOURCES.win += win/VBoxNetFltMp-win.c +endif +VBoxNetFlt_SOURCES = VBoxNetFlt.c +VBoxNetFlt_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded +VBoxNetFlt_LDFLAGS.win.x86 = -Entry:DriverEntry@8 +VBoxNetFlt_LDFLAGS.win.amd64 = -Entry:DriverEntry +VBoxNetFlt_SOURCES.win.amd64 += win/VBoxNetFltA-win.asm +VBoxNetFlt_DEFS.win.amd64 += RT_WITH_W64_UNWIND_HACK +VBoxNetFlt_LIBS.win = \ + $(PATH_SDK_W2K3DDK_LIB)/ntoskrnl.lib \ + $(PATH_SDK_W2K3DDK_LIB)/hal.lib \ + $(PATH_SDK_W2K3DDK_LIB)/ndis.lib \ + $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +VBoxNetFlt_LIBS = \ + $(PATH_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB) + + +# Darwin extras. +ifeq ($(KBUILD_TARGET),darwin) +INSTALLS += VBoxNetFlt.kext +VBoxNetFlt.kext_INST = $(INST_VBOXNETFLT)Contents/ +VBoxNetFlt.kext_SOURCES = \ + $(PATH_VBoxNetFlt.kext)/Info.plist +VBoxNetFlt.kext_CLEAN = \ + $(PATH_VBoxNetFlt.kext)/Info.plist + +$$(PATH_VBoxNetFlt.kext)/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxNetFlt,$@,$<) + $(xQUIET)$(RM) -f $@ + $(xQUIET)$(SED) \ + -e 's/@VBOX_VERSION_STRING@/$(VBOX_VERSION_STRING)/g' \ + -e 's/@VBOX_VERSION_MAJOR@/$(VBOX_VERSION_MAJOR)/g' \ + -e 's/@VBOX_VERSION_MINOR@/$(VBOX_VERSION_MINOR)/g' \ + -e 's/@VBOX_VERSION_BUILD@/$(VBOX_VERSION_BUILD)/g' \ + --output $@ \ + $< + +INSTALLS.darwin += Scripts-darwin +Scripts-darwin_INST = $(INST_DIST) +Scripts-darwin_SOURCES = \ + darwin/loadnetflt.sh +endif # darwin + + +ifeq ($(KBUILD_TARGET),win) +# +# Windows extras. +# +INSTALLS.win += VBoxNetFlt-inf +VBoxNetFlt-inf_INST = $(INST_BIN) +VBoxNetFlt-inf_MODE = a+r,u+w +VBoxNetFlt-inf_SOURCES = \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf \ + $(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt_m.inf +VBoxNetFlt-inf_CLEAN = $(VBoxNetFlt-inf_SOURCES) +VBoxNetFlt-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetFltCat.dir $(PATH_TARGET)/VBoxNetFltMpCat.dir + +$(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf: $(PATH_SUB_CURRENT)/win/VBoxNetFlt.inf $(MAKEFILE_CURRENT) | $$(dir $$@) + $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<) + $(call VBOX_EDIT_INF_FN,$<,$@) + +$(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt_m.inf: $(PATH_SUB_CURRENT)/win/VBoxNetFlt_m.inf $(MAKEFILE_CURRENT) | $$(dir $$@) + $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<) + $(call VBOX_EDIT_INF_FN,$<,$@) + + ifdef VBOX_SIGNING_MODE +VBoxNetFlt-inf_SOURCES += \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat \ + $(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt_m.cat + +$(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys: $$(TARGET_VBoxNetFlt) | $$(dir $$@) + $(INSTALL) -m 644 $< $(@D) + +$(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt.sys: $$(TARGET_VBoxNetFlt) | $$(dir $$@) + $(INSTALL) -m 644 $< $(@D) + +$(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNotify.dll: $$(TARGET_VBoxNetFltNotify) | $$(dir $$@) + $(INSTALL) -m 644 $< $(@D) + +$(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat: \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNotify.dll \ + $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf + $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<) + $(call VBOX_MAKE_CAT_FN, $(@D),$@) + +$(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt_m.cat: \ + $(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt.sys \ + $(PATH_TARGET)/VBoxNetFltMpCat.dir/VBoxNetFlt_m.inf + $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<) + $(call VBOX_MAKE_CAT_FN, $(@D),$@) + + endif # signing + +# +# WinNetConfig - static library with host network interface config API +# +LIBRARIES.win += WinNetConfig +WinNetConfig_TEMPLATE = VBOXR3STATIC +WinNetConfig_DEFS = _WIN32_WINNT=0x0501 _UNICODE UNICODE +WinNetConfig_SDKS = WINPSDK W2K3DDK +WinNetConfig_SOURCES = \ + win/WinNetConfig.cpp + +# +# NetFltInstall +# +PROGRAMS.win += NetFltInstall +NetFltInstall_TEMPLATE = VBOXR3STATIC +NetFltInstall_SDKS = WINPSDK W2K3DDK VBOX_NTDLL +NetFltInstall_SOURCES = win/NetFltInstall.cpp +NetFltInstall_LIBS = $(TARGET_WinNetConfig) + + +# +# NetFltUninstall +# +PROGRAMS.win += NetFltUninstall +NetFltUninstall_TEMPLATE = VBOXR3STATIC +NetFltUninstall_SDKS = WINPSDK W2K3DDK VBOX_NTDLL +NetFltUninstall_SOURCES = win/NetFltUninstall.cpp +NetFltUninstall_LIBS = $(TARGET_WinNetConfig) + + +# +# VBoxNetFltNotify +# +DLLS.win += VBoxNetFltNotify +VBoxNetFltNotify_TEMPLATE = VBOXR3STATIC +VBoxNetFltNotify_SDKS = WINPSDK W2K3DDK VBOX_NTDLL +VBoxNetFltNotify_DEFS = _WIN32_WINNT=0x0500 WIN32 _ATL_STATIC_REGISTRY +VBoxNetFltNotify_INCS = \ + $(PATH_VBoxNetFltNotify) +VBoxNetFltNotify_SOURCES = \ + win/notifyobj/VBoxNetFltNotify.cpp \ + win/notifyobj/VBoxNetFltNotify.def \ + win/notifyobj/VBoxNetFltNotify.rc +VBoxNetFltNotify_LIBS = \ + $(PATH_TOOL_$(VBOX_VCC_TOOL)_ATLMFC_LIB)/atls.lib +#VBoxNetFltNotify_INTERMEDIATES = +VBoxNetFltNotify_DEPS = \ + $(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn_i.c \ + $(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn_p.c \ + $(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn.h \ + $(PATH_VBoxNetFltNotify)/dlldata.c \ + $(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn.tlb +VBoxNetFltNotify_CLEAN = $(VBoxNetFltNotify_DEPS) + +VBOXNETFLT_NOTIFY_IDL ?= $(EXEC_X86_WIN32) $(call VBOX_FN_MAKE_WIN_PATH,$(firstword $(wildcard \ + $(PATH_SDK_WINPSDK_BIN)/Midl.Exe\ + $(PATH_SDK_WINPSDK)/Bin/Midl.Exe\ + $(PATH_DEVTOOLS)/win.x86/bin/midl.exe\ + ) Sorry_Cannot_Find_The_Midl_Compiler_In_The_PSDK)) + +$$(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn_i.c \ ++ $$(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn_p.c \ ++ $$(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn.h \ ++ $$(PATH_VBoxNetFltNotify)/dlldata.c \ ++ $$(PATH_VBoxNetFltNotify)/VBoxNetFltNotifyn.tlb: \ + $(PATH_SUB_CURRENT)/win/notifyobj/VBoxNetFltNotifyn.idl \ + | $$(dir $$@) + $(VBOXNETFLT_NOTIFY_IDL) /nologo \ + /out $(call VBOX_FN_MAKE_WIN_PATH,$(PATH_VBoxNetFltNotify)) \ + /cpp_cmd $(subst $(EXEC_X86_WIN32),,$(call VBOX_FN_MAKE_WIN_PATH,$(TOOL_$(VBOX_VCC_TOOL)_CC))) \ + $(addprefix /I , $(call VBOX_FN_MAKE_WIN_PATH,$(SDK_W2K3DDK_INCS))) \ + $(call VBOX_FN_MAKE_WIN_PATH,$<) +endif #ifeq ($(KBUILD_TARGET), win) + +else if1of ($(KBUILD_TARGET), linux solaris) ## @todo merge this with the mixed case stuff. +# +# vboxnetflt(.ko/.o/) - The lower case driver. +# Note! On Solaris the name has to be <= 8 chars long. +# + ifdef VBOX_WITH_VBOXDRV +SYSMODS += vboxnetflt +vboxnetflt_TEMPLATE = VBOXR0DRV +vboxnetflt_NAME.solaris = vboxflt +vboxnetflt_DEFS.linux = KBUILD_MODNAME=KBUILD_STR\(vboxnetflt\) KBUILD_BASENAME=KBUILD_STR\(vboxnetflt\) MODULE +vboxnetflt_DEFS = IN_RT_R0 +vboxnetflt_DEFS.solaris += VBOX_SVN_REV=$(VBOX_SVN_REV) +vboxnetflt_DEPS.solaris += $(VBOX_SVN_REV_KMK) +vboxnetflt_NOINST.linux = true +vboxnetflt_INCS.linux := \ + $(PATH_ROOT)/src/VBox/Runtime/r0drv/linux +vboxnetflt_INCS := \ + $(PATH_SUB_CURRENT) +vboxnetflt_LDFLAGS.solaris += -N drv/vboxdrv +ifdef VBOXNETFLT_SOLARIS_USE_NETINFO + vboxnetflt_DEFS.solaris += VBOXNETFLT_SOLARIS_USE_NETINFO + vboxnetflt_LDFLAGS.solaris += -N misc/neti +endif +vboxnetflt_LIBS = \ + $(PATH_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB) +## @todo vboxflt should resolves all the IPRT bits from vboxdrv. +#vboxnetflt_LIBS += \ +# $(PATH_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) +vboxnetflt_SOURCES.linux = linux/VBoxNetFlt-linux.c +vboxnetflt_SOURCES.solaris = solaris/VBoxNetFlt-solaris.c +vboxnetflt_SOURCES = VBoxNetFlt.c + endif + +endif + +ifeq ($(KBUILD_TARGET),linux) + # + # Install source files for compliation on Linux. + # files_vboxnetflt defines VBOX_VBOXNETFLT_SOURCES. + # + include $(PATH_SUB_CURRENT)/linux/files_vboxnetflt + INSTALLS += VBoxNetFlt-src VBoxNetFlt-sh + VBoxNetFlt-src_INST = bin/src/vboxnetflt/ + VBoxNetFlt-src_MODE = a+r,u+w + VBoxNetFlt-src_SOURCES = $(subst ",,$(VBOX_VBOXNETFLT_SOURCES)) #" + VBoxNetFlt-src_SOURCES+= \ + $(if $(VBOX_OSE),,\ + $(PATH_VBoxNetFlt-src)/dkms.conf) \ + $(PATH_VBoxNetFlt-src)/Makefile + VBoxNetFlt-src_CLEAN = \ + $(PATH_VBoxNetFlt-src)/dkms.conf \ + $(PATH_VBoxNetFlt-src)/Makefile \ + $(PATH_TARGET)/VBoxNetFlt-src-1.dep + + VBoxNetFlt-sh_INST = bin/src/vboxnetflt/ + VBoxNetFlt-sh_MODE = a+rx,u+w + VBoxNetFlt-sh_SOURCES = \ + $(PATH_VBoxNetFlt-sh)/build_in_tmp \ + $(if $(VBOX_OSE),,\ + $(PATH_ROOT)/src/VBox/HostDrivers/linux/do_Module.symvers) + VBoxNetFlt-sh_CLEAN = $(PATH_VBoxNetFlt-sh)/build_in_tmp + + +includedep $(PATH_TARGET)/VBoxNetFlt-src-1.dep +$$(PATH_VBoxNetFlt-src)/Makefile: \ + $(PATH_SUB_CURRENT)/linux/Makefile \ + $$(if $$(eq $$(VBoxNetFlt/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \ + | $$(dir $$@) +ifndef VBOX_WITH_HARDENING + $(QUIET)$(SED) -e "s;-DVBOX_WITH_HARDENING;;g" --output $@ $< +else + $(QUIET)$(CP) -f $< $@ +endif + %$(QUIET2)$(RM) -f -- $(PATH_TARGET)/VBoxNetFlt-src-1.dep + %$(QUIET2)$(APPEND) '$(PATH_TARGET)/VBoxNetFlt-src-1.dep' 'VBoxNetFlt/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)' + +## Scripts needed for building the kernel modules + +$$(PATH_VBoxNetFlt-sh)/build_in_tmp: \ + $(PATH_ROOT)/src/VBox/HostDrivers/linux/build_in_tmp \ + $(VBOX_VERSION_STAMP) \ + | $$(dir $$@) + $(call MSG_TOOL,Creating,,$@) + $(QUIET)$(SED) -e "s;_VERSION_;${VBOX_VERSION_STRING};g; s;_MODULE_;vboxnetflt;g" --output $@ $< + $(QUIET)chmod 0755 $@ + +$$(PATH_VBoxNetFlt-src)/dkms.conf: \ + $(PATH_SUB_CURRENT)/linux/dkms.conf \ + $(VBOX_VERSION_STAMP) \ + | $$(dir $$@) + $(call MSG_TOOL,Creating,,$@) + $(QUIET)$(SED) -e "s;_VERSION_;${VBOX_VERSION_STRING};g" --output $@ $< + +endif # linux + +include $(KBUILD_PATH)/subfooter.kmk + + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c new file mode 100644 index 000000000..ca26bcf1f --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c @@ -0,0 +1,1341 @@ +/* $Id: VBoxNetFlt.c 15950 2009-01-14 18:25:22Z vboxsync $ */ +/** @file + * VBoxNetFlt - Network Filter Driver (Host), Common Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/** @page pg_netflt VBoxNetFlt - Network Interface Filter + * + * This is a kernel module that attaches to a real interface on the host + * and filters and injects packets. + * + * In the big picture we're one of the three trunk interface on the internal + * network, the one named "NIC Filter Driver": @image html Networking_Overview.gif + * + * + * @section sec_netflt_msc Locking / Sequence Diagrams + * + * This secion contains a few sequence diagrams describing the problematic + * transitions of a host interface filter instance. + * + * The thing that makes it all a bit problematic is that multiple events may + * happen at the same time, and that we have to be very careful to avoid + * deadlocks caused by mixing our locks with the ones in the host kernel. + * The main events are receive, send, async send completion, disappearance of + * the host networking interface and it's reappearance. The latter two events + * are can be caused by driver unloading/loading or the device being physical + * unplugged (e.g. a USB network device). + * + * The strategy for dealing with these issues are: + * - Use a simple state machine. + * - Require the user (IntNet) to serialize all its calls to us, + * while at the same time not owning any lock used by any of the + * the callbacks we might call on receive and async send completion. + * - Make sure we're 100% idle before disconnecting, and have a + * disconnected status on both sides to fend off async calls. + * - Protect the host specific interface handle and the state variables + * using a spinlock. + * + * + * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release + * + * @msc + * VM, IntNet, NetFlt, Kernel, Wire; + * + * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"]; + * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ]; + * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ]; + * NetFlt=>Kernel [label="pkt0 to wire", linecolor="green", textcolor="green"]; + * Kernel->Wire [label="pkt0 to wire", linecolor="green", textcolor="green"]; + * + * --- [label="Suspending the trunk interface"]; + * IntNet=>IntNet [label="Lock Network"]; + * + * Wire->Kernel [label="pkt1 - racing us", linecolor="red", textcolor="red"]; + * Kernel=>>NetFlt [label="pkt1 - racing us", linecolor="red", textcolor="red"]; + * NetFlt=>>IntNet [label="pkt1 recv - blocks", linecolor="red", textcolor="red"]; + * + * IntNet=>IntNet [label="Mark Trunk Suspended"]; + * IntNet=>IntNet [label="Unlock Network"]; + * + * IntNet=>NetFlt [label="pfnSetActive(false)"]; + * NetFlt=>NetFlt [label="Mark inactive (atomic)"]; + * IntNet<<NetFlt; + * IntNet=>NetFlt [label="pfnWaitForIdle(forever)"]; + * + * IntNet=>>NetFlt [label="pkt1 to host", linecolor="red", textcolor="red"]; + * NetFlt=>>Kernel [label="pkt1 to host", linecolor="red", textcolor="red"]; + * + * Kernel<-Wire [label="pkt0 on wire", linecolor="green", textcolor="green"]; + * NetFlt<<Kernel [label="pkt0 on wire", linecolor="green", textcolor="green"]; + * IntNet<<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"]; + * IntNet<<=IntNet [label="Lock Net, free SG, Unlock Net", linecolor="green", textcolor="green"]; + * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"]; + * NetFlt<-NetFlt [label="idle", linecolor="green", textcolor="green"]; + * + * IntNet<<NetFlt [label="idle (pfnWaitForIdle)"]; + * + * Wire->Kernel [label="pkt2", linecolor="red", textcolor="red"]; + * Kernel=>>NetFlt [label="pkt2", linecolor="red", textcolor="red"]; + * NetFlt=>>Kernel [label="pkt2 to host", linecolor="red", textcolor="red"]; + * + * VM->IntNet [label="pkt3", linecolor="green", textcolor="green"]; + * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Route packet -> drop", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ]; + * + * --- [label="The trunk interface is idle now, disconnect it"]; + * IntNet=>IntNet [label="Lock Network"]; + * IntNet=>IntNet [label="Unlink Trunk"]; + * IntNet=>IntNet [label="Unlock Network"]; + * IntNet=>NetFlt [label="pfnDisconnectAndRelease"]; + * NetFlt=>Kernel [label="iflt_detach"]; + * NetFlt<<=Kernel [label="iff_detached"]; + * NetFlt>>Kernel [label="iff_detached"]; + * NetFlt<<Kernel [label="iflt_detach"]; + * NetFlt=>NetFlt [label="Release"]; + * IntNet<<NetFlt [label="pfnDisconnectAndRelease"]; + * + * @endmsc + * + * + * + * @subsection subsec_netflt_msc_hif_rm Host Interface Removal + * + * The ifnet_t (pIf) is a tricky customer as any reference to it can potentially + * race the filter detaching. The simple way of solving it on Darwin is to guard + * all access to the pIf member with a spinlock. The other host systems will + * probably have similar race conditions, so the spinlock is a generic thing. + * + * @msc + * VM, IntNet, NetFlt, Kernel; + * + * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"]; + * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ]; + * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ]; + * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ]; + * NetFlt=>Kernel [label="ifnet_reference w/ spinlock", linecolor="green", textcolor="green" ]; + * NetFlt<<Kernel [label="ifnet_reference", linecolor="green", textcolor="green" ]; + * NetFlt=>Kernel [label="pkt0 to wire (blocks)", linecolor="green", textcolor="green" ]; + * + * --- [label="The host interface is being disconnected"]; + * Kernel->NetFlt [label="iff_detached"]; + * NetFlt=>Kernel [label="ifnet_release w/ spinlock"]; + * NetFlt<<Kernel [label="ifnet_release"]; + * NetFlt=>NetFlt [label="fDisconnectedFromHost=true"]; + * NetFlt>>Kernel [label="iff_detached"]; + * + * NetFlt<<Kernel [label="dropped", linecolor="green", textcolor="green"]; + * NetFlt=>NetFlt [label="Acquire spinlock", linecolor="green", textcolor="green"]; + * NetFlt=>Kernel [label="ifnet_release", linecolor="green", textcolor="green"]; + * NetFlt<<Kernel [label="ifnet_release", linecolor="green", textcolor="green"]; + * NetFlt=>NetFlt [label="pIf=NULL", linecolor="green", textcolor="green"]; + * NetFlt=>NetFlt [label="Release spinlock", linecolor="green", textcolor="green"]; + * IntNet<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"]; + * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"]; + * IntNet<<NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green"]; + * + * @endmsc + * + * + * + * @subsection subsec_netflt_msc_hif_rm Host Interface Rediscovery + * + * The rediscovery is performed when we receive a send request and a certain + * period have elapsed since the last attempt, i.e. we're polling it. We + * synchronize the rediscovery with disconnection from the internal network + * by means of the pfnWaitForIdle call, so no special handling is required. + * + * @msc + * VM2, VM1, IntNet, NetFlt, Kernel, Wire; + * + * --- [label="Rediscovery conditions are not met"]; + * VM1->IntNet [label="pkt0"]; + * IntNet=>IntNet [label="Lock Network"]; + * IntNet=>IntNet [label="Route packet -> wire"]; + * IntNet=>IntNet [label="Unlock Network"]; + * IntNet=>NetFlt [label="pkt0 to wire"]; + * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"]; + * IntNet<<NetFlt [label="pkt0 to wire (dropped)"]; + * + * --- [label="Rediscovery conditions"]; + * VM1->IntNet [label="pkt1"]; + * IntNet=>IntNet [label="Lock Network"]; + * IntNet=>IntNet [label="Route packet -> wire"]; + * IntNet=>IntNet [label="Unlock Network"]; + * IntNet=>NetFlt [label="pkt1 to wire"]; + * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"]; + * NetFlt=>NetFlt [label="fRediscoveryPending=true w/ spinlock"]; + * NetFlt=>Kernel [label="ifnet_find_by_name"]; + * NetFlt<<Kernel [label="ifnet_find_by_name (success)"]; + * + * VM2->IntNet [label="pkt2", linecolor="red", textcolor="red"]; + * IntNet=>IntNet [label="Lock Network", linecolor="red", textcolor="red"]; + * IntNet=>IntNet [label="Route packet -> wire", linecolor="red", textcolor="red"]; + * IntNet=>IntNet [label="Unlock Network", linecolor="red", textcolor="red"]; + * IntNet=>NetFlt [label="pkt2 to wire", linecolor="red", textcolor="red"]; + * NetFlt=>NetFlt [label="!pIf || fRediscoveryPending (w/ spinlock)", linecolor="red", textcolor="red"]; + * IntNet<<NetFlt [label="pkt2 to wire (dropped)", linecolor="red", textcolor="red"]; + + * NetFlt=>Kernel [label="iflt_attach"]; + * NetFlt<<Kernel [label="iflt_attach (success)"]; + * NetFlt=>NetFlt [label="Acquire spinlock"]; + * NetFlt=>NetFlt [label="Set pIf and update flags"]; + * NetFlt=>NetFlt [label="Release spinlock"]; + * + * NetFlt=>Kernel [label="pkt1 to wire"]; + * Kernel->Wire [label="pkt1 to wire"]; + * NetFlt<<Kernel [label="pkt1 to wire"]; + * IntNet<<NetFlt [label="pkt1 to wire"]; + * + * + * @endmsc + * + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_NET_FLT_DRV +#include "VBoxNetFltInternal.h" + +#include <VBox/sup.h> +#include <VBox/log.h> +#include <VBox/err.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/time.h> +#include <iprt/uuid.h> + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +#define IFPORT_2_VBOXNETFLTINS(pIfPort) \ + ( (PVBOXNETFLTINS)((uint8_t *)pIfPort - RT_OFFSETOF(VBOXNETFLTINS, MyPort)) ) + + +/** + * Finds a instance by its name, the caller does the locking. + * + * + * @returns Pointer to the instance by the given name. NULL if not found. + * @param pGlobals The globals. + * @param pszName The name of the instance. + */ +static PVBOXNETFLTINS vboxNetFltFindInstanceLocked(PVBOXNETFLTGLOBALS pGlobals, const char *pszName) +{ + PVBOXNETFLTINS pCur; + for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext) + if (!strcmp(pszName, pCur->szName)) + return pCur; + return NULL; +} + + +/** + * Finds a instance by its name, will request the mutex. + * + * @returns Pointer to the instance by the given name. NULL if not found. + * @param pGlobals The globals. + * @param pszName The name of the instance. + */ +DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName) +{ + PVBOXNETFLTINS pRet; + int rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + AssertRCReturn(rc, NULL); + + pRet = vboxNetFltFindInstanceLocked(pGlobals, pszName); + + rc = RTSemFastMutexRelease(pGlobals->hFastMtx); + AssertRC(rc); + return pRet; +} + + +/** + * Unlinks an instance from the chain. + * + * @param pGlobals The globals. + * @param pToUnlink The instance to unlink. + */ +static void vboxNetFltUnlinkLocked(PVBOXNETFLTGLOBALS pGlobals, PVBOXNETFLTINS pToUnlink) +{ + if (pGlobals->pInstanceHead == pToUnlink) + pGlobals->pInstanceHead = pToUnlink->pNext; + else + { + PVBOXNETFLTINS pCur; + for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext) + if (pCur->pNext == pToUnlink) + { + pCur->pNext = pToUnlink->pNext; + break; + } + Assert(pCur); + } + pToUnlink->pNext = NULL; +} + + +AssertCompileMemberSize(VBOXNETFLTINS, enmState, sizeof(uint32_t)); + +/** + * Sets the enmState member atomically. + * + * Used for all updates. + * + * @param pThis The instance. + * @param enmNewState The new value. + */ +DECLINLINE(void) vboxNetFltSetState(PVBOXNETFLTINS pThis, VBOXNETFTLINSSTATE enmNewState) +{ + ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, enmNewState); +} + + +/** + * Gets the enmState member atomically. + * + * Used for all reads. + * + * @returns The enmState value. + * @param pThis The instance. + */ +DECLINLINE(VBOXNETFTLINSSTATE) vboxNetFltGetState(PVBOXNETFLTINS pThis) +{ + return (VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState); +} + + +/** + * Performs interface rediscovery if it was disconnected from the host. + * + * @returns true if successfully rediscovered and connected, false if not. + * @param pThis The instance. + */ +static bool vboxNetFltMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + uint64_t Now = RTTimeNanoTS(); + bool fRediscovered; + bool fDoIt; + + /* + * Rediscovered already? Time to try again? + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + + fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); + fDoIt = !fRediscovered + && !ASMAtomicUoReadBool(&pThis->fRediscoveryPending) + && Now - ASMAtomicUoReadU64(&pThis->NanoTSLastRediscovery) > UINT64_C(5000000000); /* 5 sec */ + if (fDoIt) + ASMAtomicWriteBool(&pThis->fRediscoveryPending, true); + + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + /* + * Call the OS specific code to do the job. + * Update the state when the call returns, that is everything except for + * the fDisconnectedFromHost flag which the OS specific code shall set. + */ + if (fDoIt) + { + fRediscovered = vboxNetFltOsMaybeRediscovered(pThis); + + Assert(!fRediscovered || !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)); + + ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, RTTimeNanoTS()); + ASMAtomicWriteBool(&pThis->fRediscoveryPending, false); + + if (fRediscovered) + vboxNetFltPortOsSetActive(pThis, pThis->fActive); + } + + return fRediscovered; +} + +#ifdef RT_WITH_W64_UNWIND_HACK +# if defined(RT_OS_WINDOWS) && defined(RT_ARCH_AMD64) +# define NETFLT_DECL_CALLBACK(type) DECLASM(DECLHIDDEN(type)) +# define NETFLT_CALLBACK(_n) netfltNtWrap##_n + +NETFLT_DECL_CALLBACK(int) NETFLT_CALLBACK(vboxNetFltPortXmit)(PINTNETTRUNKIFPORT pIfPort, PINTNETSG pSG, uint32_t fDst); +NETFLT_DECL_CALLBACK(bool) NETFLT_CALLBACK(vboxNetFltPortIsPromiscuous)(PINTNETTRUNKIFPORT pIfPort); +NETFLT_DECL_CALLBACK(void) NETFLT_CALLBACK(vboxNetFltPortGetMacAddress)(PINTNETTRUNKIFPORT pIfPort, PRTMAC pMac); +NETFLT_DECL_CALLBACK(bool) NETFLT_CALLBACK(vboxNetFltPortIsHostMac)(PINTNETTRUNKIFPORT pIfPort, PCRTMAC pMac); +NETFLT_DECL_CALLBACK(int) NETFLT_CALLBACK(vboxNetFltPortWaitForIdle)(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies); +NETFLT_DECL_CALLBACK(bool) NETFLT_CALLBACK(vboxNetFltPortSetActive)(PINTNETTRUNKIFPORT pIfPort, bool fActive); +NETFLT_DECL_CALLBACK(void) NETFLT_CALLBACK(vboxNetFltPortDisconnectAndRelease)(PINTNETTRUNKIFPORT pIfPort); +NETFLT_DECL_CALLBACK(void) NETFLT_CALLBACK(vboxNetFltPortRetain)(PINTNETTRUNKIFPORT pIfPort); +NETFLT_DECL_CALLBACK(void) NETFLT_CALLBACK(vboxNetFltPortRelease)(PINTNETTRUNKIFPORT pIfPort); + +# else +# error "UNSUPPORTED (RT_WITH_W64_UNWIND_HACK)" +# endif +#else +# define NETFLT_DECL_CALLBACK(type) static DECLCALLBACK(type) +# define NETFLT_CALLBACK(_n) _n +#endif + +/** + * @copydoc INTNETTRUNKIFPORT::pfnXmit + */ +NETFLT_DECL_CALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, PINTNETSG pSG, uint32_t fDst) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + int rc = VINF_SUCCESS; + + /* + * Input validation. + */ + AssertPtr(pThis); + AssertPtr(pSG); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE); + Assert(pThis->fActive); + + /* + * Do a busy retain and then make sure we're connected to the interface + * before invoking the OS specific code. + */ + vboxNetFltRetain(pThis, true /* fBusy */); + if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost) + || vboxNetFltMaybeRediscovered(pThis)) + rc = vboxNetFltPortOsXmit(pThis, pSG, fDst); + vboxNetFltRelease(pThis, true /* fBusy */); + + return rc; +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnIsPromiscuous + */ +NETFLT_DECL_CALLBACK(bool) vboxNetFltPortIsPromiscuous(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected); + Assert(pThis->fActive); + + /* + * Ask the OS specific code. + */ + return vboxNetFltPortOsIsPromiscuous(pThis); +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnGetMacAddress + */ +NETFLT_DECL_CALLBACK(void) vboxNetFltPortGetMacAddress(PINTNETTRUNKIFPORT pIfPort, PRTMAC pMac) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected); + Assert(pThis->fActive); + + /* + * Forward the question to the OS specific code. + */ + vboxNetFltPortOsGetMacAddress(pThis, pMac); +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnIsHostMac + */ +NETFLT_DECL_CALLBACK(bool) vboxNetFltPortIsHostMac(PINTNETTRUNKIFPORT pIfPort, PCRTMAC pMac) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected); + Assert(pThis->fActive); + + /* + * Ask the OS specific code. + */ + return vboxNetFltPortOsIsHostMac(pThis, pMac); +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle + */ +NETFLT_DECL_CALLBACK(int) vboxNetFltPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + int rc; + + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE); + AssertReturn(!pThis->fActive, VERR_INVALID_STATE); + + /* + * Go to sleep on the semaphore after checking the busy count. + */ + vboxNetFltRetain(pThis, false /* fBusy */); + + rc = VINF_SUCCESS; + while (pThis->cBusy && RT_SUCCESS(rc)) + rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */ + + vboxNetFltRelease(pThis, false /* fBusy */); + + return rc; +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnSetActive + */ +NETFLT_DECL_CALLBACK(bool) vboxNetFltPortSetActive(PINTNETTRUNKIFPORT pIfPort, bool fActive) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + + /* + * Input validation. + */ + AssertPtr(pThis); + AssertPtr(pThis->pGlobals); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, false); + + /* + * We're assuming that the caller is serializing the calls, so we don't + * have to be extremely careful here. Just update first and then call + * the OS specific code, the update must be serialized for various reasons. + */ + if (ASMAtomicReadBool(&pThis->fActive) != fActive) + { + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + ASMAtomicWriteBool(&pThis->fActive, fActive); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + vboxNetFltPortOsSetActive(pThis, fActive); + } + else + fActive = !fActive; + return !fActive; +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease + */ +NETFLT_DECL_CALLBACK(void) vboxNetFltPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + + /* + * Serious paranoia. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); + AssertPtr(pThis->pGlobals); + Assert(pThis->hEventIdle != NIL_RTSEMEVENT); + Assert(pThis->hSpinlock != NIL_RTSPINLOCK); + Assert(pThis->szName[0]); + + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected); + Assert(!pThis->fActive); + Assert(!pThis->fRediscoveryPending); + Assert(!pThis->cBusy); + + /* + * Disconnect and release it. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + vboxNetFltOsDisconnectIt(pThis); + pThis->pSwitchPort = NULL; + +#ifdef VBOXNETFLT_STATIC_CONFIG + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); +#endif + + vboxNetFltRelease(pThis, false /* fBusy */); +} + + +/** + * Destroy a device that has been disconnected from the switch. + * + * @return true iff the instance is destroyed, false otherwise + * @param pThis The instance to be destroyed. This is + * no longer valid when this function returns. + */ +static bool vboxNetFltCheckDestroyInstance(PVBOXNETFLTINS pThis) +{ + PVBOXNETFLTGLOBALS pGlobals = pThis->pGlobals; + int rc; + uint32_t cRefs = ASMAtomicUoReadU32((uint32_t volatile *)&pThis->cRefs); + LogFlow(("vboxNetFltCheckDestroyInstance: pThis=%p (%s)\n", pThis, pThis->szName)); + + /* + * Validate the state. + */ +#ifdef VBOXNETFLT_STATIC_CONFIG + Assert( vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting + || vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected); +#else + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting); +#endif + Assert(!pThis->fActive); + Assert(!pThis->fRediscoveryPending); + Assert(!pThis->cRefs); + Assert(!pThis->cBusy); + Assert(!pThis->pSwitchPort); + + /* + * Make sure the state is 'disconnecting' and let the OS specific code + * do its part of the cleanup outside the mutex. + */ + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc); + if(cRefs != 0) + { + Assert(cRefs < UINT32_MAX / 2); + RTSemFastMutexRelease(pGlobals->hFastMtx); + return false; + } + vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroying); + RTSemFastMutexRelease(pGlobals->hFastMtx); + + vboxNetFltOsDeleteInstance(pThis); + + /* + * Unlink the instance and free up its resources. + */ + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc); + vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroyed); + vboxNetFltUnlinkLocked(pGlobals, pThis); + RTSemFastMutexRelease(pGlobals->hFastMtx); + + RTSemEventDestroy(pThis->hEventIdle); + pThis->hEventIdle = NIL_RTSEMEVENT; + RTSpinlockDestroy(pThis->hSpinlock); + pThis->hSpinlock = NIL_RTSPINLOCK; + RTMemFree(pThis); + return true; +} + + +/** + * Releases a reference to the specified instance. + * + * This method will destroy the instance when the count reaches 0. + * It will also take care of decrementing the counter and idle wakeup. + * + * @param pThis The instance. + * @param fBusy Whether the busy counter should be decremented too. + */ +DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy) +{ + uint32_t cRefs; + + /* + * Paranoid Android. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); + Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid + && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed); + AssertPtr(pThis->pGlobals); + Assert(pThis->hEventIdle != NIL_RTSEMEVENT); + Assert(pThis->hSpinlock != NIL_RTSPINLOCK); + Assert(pThis->szName[0]); + + /* + * Work the busy counter. + */ + if (fBusy) + { + cRefs = ASMAtomicDecU32(&pThis->cBusy); + if (!cRefs) + { + int rc = RTSemEventSignal(pThis->hEventIdle); + AssertRC(rc); + } + else + Assert(cRefs < UINT32_MAX / 2); + } + + /* + * The object reference counting. + */ + cRefs = ASMAtomicDecU32(&pThis->cRefs); + if (!cRefs) + vboxNetFltCheckDestroyInstance(pThis); + else + Assert(cRefs < UINT32_MAX / 2); +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnRetain + */ +NETFLT_DECL_CALLBACK(void) vboxNetFltPortRelease(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + vboxNetFltRelease(pThis, false /* fBusy */); +} + + +/** + * Retains a reference to the specified instance and a busy reference too. + * + * @param pThis The instance. + * @param fBusy Whether the busy counter should be incremented as well. + */ +DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy) +{ + uint32_t cRefs; + + /* + * Paranoid Android. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); + Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid + && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed); + AssertPtr(pThis->pGlobals); + Assert(pThis->hEventIdle != NIL_RTSEMEVENT); + Assert(pThis->hSpinlock != NIL_RTSPINLOCK); + Assert(pThis->szName[0]); + + /* + * Retain the object. + */ + cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs > 1 && cRefs < UINT32_MAX / 2); + + /* + * Work the busy counter. + */ + if (fBusy) + { + cRefs = ASMAtomicIncU32(&pThis->cBusy); + Assert(cRefs > 0 && cRefs < UINT32_MAX / 2); + } + + NOREF(cRefs); +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnRetain + */ +NETFLT_DECL_CALLBACK(void) vboxNetFltPortRetain(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); + vboxNetFltRetain(pThis, false /* fBusy */); +} + + +/** + * Connects the instance to the specified switch port. + * + * Called while owning the lock. We're ASSUMING that the internal + * networking code is already owning an recursive mutex, so, there + * will be no deadlocks when vboxNetFltOsConnectIt calls back into + * it for setting preferences. + * + * @returns VBox status code. + * @param pThis The instance. + * @param pSwitchPort The port on the internal network 'switch'. + * @param ppIfPort Where to return our port interface. + */ +static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort) +{ + int rc; + + /* + * Validate state. + */ + Assert(!pThis->fActive); + Assert(!pThis->fRediscoveryPending); + Assert(!pThis->cBusy); +#ifdef VBOXNETFLT_STATIC_CONFIG + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected); +#else + Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Initializing); +#endif + + /* + * Do the job. + * Note that we're calling the os stuff while owning the semaphore here. + */ + pThis->pSwitchPort = pSwitchPort; + rc = vboxNetFltOsConnectIt(pThis); + if (RT_SUCCESS(rc)) + { + vboxNetFltSetState(pThis, kVBoxNetFltInsState_Connected); +#ifdef VBOXNETFLT_STATIC_CONFIG + *ppIfPort = &pThis->MyPort; +#endif + } + else + pThis->pSwitchPort = NULL; + + Assert(!pThis->fActive); + return rc; +} + + +/** + * Creates a new instance. + * + * The new instance will be in the suspended state in a dynamic config and in + * the inactive in a static one. + * + * Called without owning the lock, but will request is several times. + * + * @returns VBox status code. + * @param pGlobals The globals. + * @param pszName The instance name. + * @param pSwitchPort The port on the switch that we're connected with (dynamic only). + * @param ppIfPort Where to store the pointer to our port interface (dynamic only). + */ +static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort +#ifdef VBOXNETFLT_STATIC_CONFIG + , void * pContext +#endif + ) +{ + /* + * Allocate and initialize a new instance before requesting the mutex. + */ + int rc; + size_t const cchName = strlen(pszName); + PVBOXNETFLTINS pNew = (PVBOXNETFLTINS)RTMemAllocZ(RT_OFFSETOF(VBOXNETFLTINS, szName[cchName + 1])); + if (!pNew) + return VERR_INTNET_FLT_IF_FAILED; + pNew->pNext = NULL; + pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION; + pNew->MyPort.pfnRetain = NETFLT_CALLBACK(vboxNetFltPortRetain); + pNew->MyPort.pfnRelease = NETFLT_CALLBACK(vboxNetFltPortRelease); + pNew->MyPort.pfnDisconnectAndRelease= NETFLT_CALLBACK(vboxNetFltPortDisconnectAndRelease); + pNew->MyPort.pfnSetActive = NETFLT_CALLBACK(vboxNetFltPortSetActive); + pNew->MyPort.pfnWaitForIdle = NETFLT_CALLBACK(vboxNetFltPortWaitForIdle); + pNew->MyPort.pfnGetMacAddress = NETFLT_CALLBACK(vboxNetFltPortGetMacAddress); + pNew->MyPort.pfnIsHostMac = NETFLT_CALLBACK(vboxNetFltPortIsHostMac); + pNew->MyPort.pfnIsPromiscuous = NETFLT_CALLBACK(vboxNetFltPortIsPromiscuous); + pNew->MyPort.pfnXmit = NETFLT_CALLBACK(vboxNetFltPortXmit); + pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION; + pNew->pSwitchPort = NULL; + pNew->pGlobals = pGlobals; + pNew->hSpinlock = NIL_RTSPINLOCK; + pNew->enmState = kVBoxNetFltInsState_Initializing; + pNew->fActive = false; + pNew->fDisconnectedFromHost = false; + pNew->fRediscoveryPending = false; + pNew->NanoTSLastRediscovery = INT64_MAX; + pNew->cRefs = 1; + pNew->cBusy = 0; + pNew->hEventIdle = NIL_RTSEMEVENT; + memcpy(pNew->szName, pszName, cchName + 1); + + rc = RTSpinlockCreate(&pNew->hSpinlock); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pNew->hEventIdle); + if (RT_SUCCESS(rc)) + { + rc = vboxNetFltOsPreInitInstance(pNew); + if (RT_SUCCESS(rc)) + { + /* + * Insert the instance into the chain, checking for + * duplicates first of course (race). + */ + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + if (RT_SUCCESS(rc)) + { + if (!vboxNetFltFindInstanceLocked(pGlobals, pszName)) + { + pNew->pNext = pGlobals->pInstanceHead; + pGlobals->pInstanceHead = pNew; + RTSemFastMutexRelease(pGlobals->hFastMtx); + + /* + * Call the OS specific initialization code. + */ + rc = vboxNetFltOsInitInstance(pNew +#ifdef VBOXNETFLT_STATIC_CONFIG + , pContext +#endif + ); + RTSemFastMutexRequest(pGlobals->hFastMtx); + if (RT_SUCCESS(rc)) + { +#ifdef VBOXNETFLT_STATIC_CONFIG + pNew->enmState = kVBoxNetFltInsState_Unconnected; + Assert(!pSwitchPort); + *ppIfPort = &pNew->MyPort; + RTSemFastMutexRelease(pGlobals->hFastMtx); + return rc; + +#else + /* + * Connect it as well, the OS specific bits has to be done outside + * the lock as they may call back to into intnet. + */ + rc = vboxNetFltConnectIt(pNew, pSwitchPort, ppIfPort); + if (RT_SUCCESS(rc)) + { + RTSemFastMutexRelease(pGlobals->hFastMtx); + *ppIfPort = &pNew->MyPort; + return rc; + } + + /* Bail out (failed). */ + vboxNetFltOsDeleteInstance(pNew); +#endif + } + vboxNetFltUnlinkLocked(pGlobals, pNew); + } + else + rc = VERR_INTNET_FLT_IF_BUSY; + RTSemFastMutexRelease(pGlobals->hFastMtx); + } + } + RTSemEventDestroy(pNew->hEventIdle); + } + RTSpinlockDestroy(pNew->hSpinlock); + } + + RTMemFree(pNew); + return rc; +} + +#ifdef VBOXNETFLT_STATIC_CONFIG + +/** + * searches for the NetFlt instance by its name and creates the new one if not found + * + * @return VINF_SUCCESS if new instance was created, VINF_ALREADY_INITIALIZED if an instanmce already exists, + * VERR_xxx in case of a failure + */ +DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void * pContext) +{ + PINTNETTRUNKIFPORT pIfPort; + int rc; + + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + AssertRCReturn(rc, rc); + + *ppInstance = vboxNetFltFindInstanceLocked(pGlobals, pszName); + if(*ppInstance) + { + VBOXNETFTLINSSTATE enmState = vboxNetFltGetState(*ppInstance); + if(enmState != kVBoxNetFltInsState_Destroying && enmState != kVBoxNetFltInsState_Destroyed) + { + vboxNetFltRetain(*ppInstance, false); + RTSemFastMutexRelease(pGlobals->hFastMtx); + return VINF_ALREADY_INITIALIZED; + } + + /*wait for the instance to be removed from the list */ + + *ppInstance = NULL; + + do + { + RTSemFastMutexRelease(pGlobals->hFastMtx); + + RTSemEventWait(pGlobals->hTimerEvent, 2); + + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + AssertRCReturn(rc, rc); + } while(vboxNetFltFindInstanceLocked(pGlobals, pszName)); + } + + RTSemFastMutexRelease(pGlobals->hFastMtx); + + rc = vboxNetFltNewInstance(pGlobals, pszName, NULL, &pIfPort, pContext); + if(RT_SUCCESS(rc)) + *ppInstance = IFPORT_2_VBOXNETFLTINS(pIfPort); + else + *ppInstance = NULL; + + return rc; +} + +#endif + +/** + * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect + */ +static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName, + PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort) +{ + PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory)); + PVBOXNETFLTINS pCur; + int rc; + + LogFlow(("vboxNetFltFactoryCreateAndConnect: pszName=%p:{%s}\n", pszName, pszName)); + Assert(pGlobals->cFactoryRefs > 0); + + /* + * Static: Find instance, check if busy, connect if not. + * Dynamic: Check for duplicate / busy interface instance. + */ + rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + AssertRCReturn(rc, rc); + + pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName); + if (pCur) + { +#ifdef VBOXNETFLT_STATIC_CONFIG + switch (vboxNetFltGetState(pCur)) + { + case kVBoxNetFltInsState_Unconnected: + /* instance can be destroyed when it is neither used by the IntNet nor by the ndis filter driver mechanism + * (i.e. the driver is not bound to the specified adapter)*/ + vboxNetFltRetain(pCur, false /* fBusy */); /** @todo who releases this on failure? */ + rc = vboxNetFltConnectIt(pCur, pSwitchPort, ppIfPort); + break; + case kVBoxNetFltInsState_Connected: + AssertFailed(); + rc = VERR_INTNET_FLT_IF_BUSY; + break; + case kVBoxNetFltInsState_Disconnecting: + AssertFailed(); + rc = VERR_INTNET_FLT_IF_BUSY; + break; + default: + /** @todo what? */ + rc = VERR_INTNET_FLT_IF_BUSY; + break; + } +#else + rc = VERR_INTNET_FLT_IF_BUSY; +#endif + RTSemFastMutexRelease(pGlobals->hFastMtx); + LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc)); + return rc; + } + + RTSemFastMutexRelease(pGlobals->hFastMtx); + +#ifdef VBOXNETFLT_STATIC_CONFIG + rc = VERR_INTNET_FLT_IF_NOT_FOUND; +#else + /* + * Dynamically create a new instance. + */ + rc = vboxNetFltNewInstance(pGlobals, pszName, pSwitchPort, ppIfPort); +#endif + LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc)); + return rc; +} + + +/** + * @copydoc INTNETTRUNKFACTORY::pfnRelease + */ +static DECLCALLBACK(void) vboxNetFltFactoryRelease(PINTNETTRUNKFACTORY pIfFactory) +{ + PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory)); + + int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs); + Assert(cRefs >= 0); NOREF(cRefs); + LogFlow(("vboxNetFltFactoryRelease: cRefs=%d (new)\n", cRefs)); +} + + +/** + * Implements the SUPDRV component factor interface query method. + * + * @returns Pointer to an interface. NULL if not supported. + * + * @param pSupDrvFactory Pointer to the componet factory registration structure. + * @param pSession The session - unused. + * @param pszInterfaceUuid The factory interface id. + */ +static DECLCALLBACK(void *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid) +{ + PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, SupDrvFactory)); + + /* + * Convert the UUID strings and compare them. + */ + RTUUID UuidReq; + int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid); + if (RT_SUCCESS(rc)) + { + if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR)) + { + ASMAtomicIncS32(&pGlobals->cFactoryRefs); + return &pGlobals->TrunkFactory; + } +#ifdef LOG_ENABLED + /* log legacy queries */ + /* else if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_V1_UUID_STR)) + Log(("VBoxNetFlt: V1 factory query\n")); + */ + else + Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid)); +#endif + } + else + Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid)); + + return NULL; +} + + +/** + * Checks whether the VBoxNetFlt wossname can be unloaded. + * + * This will return false if someone is currently using the module. + * + * @returns true if it's relatively safe to unload it, otherwise false. + * @param pGlobals Pointer to the globals. + */ +DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals) +{ + int rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + bool fRc = !pGlobals->pInstanceHead + && pGlobals->cFactoryRefs <= 0; + RTSemFastMutexRelease(pGlobals->hFastMtx); + AssertRC(rc); + return fRc; +} + +/** + * tries to deinitialize Idc + * we separate the globals settings "base" which is actually + * "general" globals settings except for Idc, and idc. + * This is needed for windows filter driver, which gets loaded prior to VBoxDrv, + * thus it's not possible to make idc initialization from the driver startup routine for it, + * though the "base is still needed for the driver to functions". + * @param pGlobals + * @return VINF_SUCCESS on succes, VERR_WRONG_ORDER if we're busy. + */ +DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals) +{ + int rc; + + Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX); + + /* + * Check before trying to deregister the factory. + */ + if (!vboxNetFltCanUnload(pGlobals)) + return VERR_WRONG_ORDER; + + /* + * Disconnect from SUPDRV and check that nobody raced us, + * reconnect if that should happen. + */ + rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + AssertRC(rc); + if (!vboxNetFltCanUnload(pGlobals)) + { + rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + AssertRC(rc); + return VERR_WRONG_ORDER; + } + + SUPR0IdcClose(&pGlobals->SupDrvIDC); + + return rc; +} + +/** + * performs "base" globals deinitialization + * we separate the globals settings "base" which is actually + * "general" globals settings except for Idc, and idc. + * This is needed for windows filter driver, which gets loaded prior to VBoxDrv, + * thus it's not possible to make idc initialization from the driver startup routine for it, + * though the "base is still needed for the driver to functions". + * @param pGlobals + * @return none + */ +DECLHIDDEN(void) vboxNetFltDeleteGlobalsBase(PVBOXNETFLTGLOBALS pGlobals) +{ + /* + * Release resources. + */ + RTSemFastMutexDestroy(pGlobals->hFastMtx); + pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX; + +#ifdef VBOXNETFLT_STATIC_CONFIG + RTSemEventDestroy(pGlobals->hTimerEvent); + pGlobals->hTimerEvent = NIL_RTSEMEVENT; +#endif + +} + +/** + * Called by the native part when the OS wants the driver to unload. + * + * @returns VINF_SUCCESS on succes, VERR_WRONG_ORDER if we're busy. + * + * @param pGlobals Pointer to the globals. + */ +DECLHIDDEN(int) vboxNetFltTryDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals) +{ + int rc = vboxNetFltTryDeleteIdc(pGlobals); + if(RT_SUCCESS(rc)) + { + vboxNetFltDeleteGlobalsBase(pGlobals); + } + return rc; +} + +/** + * performs the "base" globals initialization + * we separate the globals initialization to globals "base" initialization which is actually + * "general" globals initialization except for Idc not being initialized, and idc initialization. + * This is needed for windows filter driver, which gets loaded prior to VBoxDrv, + * thus it's not possible to make idc initialization from the driver startup routine for it. + * + * @returns VBox status code. + * @param pGlobals Pointer to the globals. */ +DECLHIDDEN(int) vboxNetFltInitGlobalsBase(PVBOXNETFLTGLOBALS pGlobals) +{ + /* + * Initialize the common portions of the structure. + */ + int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx); + if (RT_SUCCESS(rc)) + { +#ifdef VBOXNETFLT_STATIC_CONFIG + rc = RTSemEventCreate(&pGlobals->hTimerEvent); + if (RT_SUCCESS(rc)) + { +#endif + pGlobals->pInstanceHead = NULL; + + pGlobals->TrunkFactory.pfnRelease = vboxNetFltFactoryRelease; + pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetFltFactoryCreateAndConnect; + + strcpy(pGlobals->SupDrvFactory.szName, "VBoxNetFlt"); + pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetFltQueryFactoryInterface; + + return rc; +#ifdef VBOXNETFLT_STATIC_CONFIG + } + RTSemFastMutexDestroy(pGlobals->hFastMtx); +#endif + } + + return rc; +} + +/** + * performs the Idc initialization + * we separate the globals initialization to globals "base" initialization which is actually + * "general" globals initialization except for Idc not being initialized, and idc initialization. + * This is needed for windows filter driver, which gets loaded prior to VBoxDrv, + * thus it's not possible to make idc initialization from the driver startup routine for it. + * + * @returns VBox status code. + * @param pGlobals Pointer to the globals. */ +DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals) +{ + int rc; + /* + * Establish a connection to SUPDRV and register our component factory. + */ + rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL); + if (RT_SUCCESS(rc)) + { + rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + if (RT_SUCCESS(rc)) + { + Log(("VBoxNetFlt: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC))); + return rc; + } + + /* bail out. */ + LogRel(("VBoxNetFlt: Failed to register component factory, rc=%Rrc\n", rc)); + SUPR0IdcClose(&pGlobals->SupDrvIDC); + } + + return rc; +} + +/** + * Called by the native driver/kext module initialization routine. + * + * It will initialize the common parts of the globals, assuming the caller + * has already taken care of the OS specific bits. + * + * @returns VBox status code. + * @param pGlobals Pointer to the globals. + */ +DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals) +{ + /* + * Initialize the common portions of the structure. + */ + int rc = vboxNetFltInitGlobalsBase(pGlobals); + if (RT_SUCCESS(rc)) + { + rc = vboxNetFltInitIdc(pGlobals); + if (RT_SUCCESS(rc)) + { + return rc; + } + + /* bail out. */ + vboxNetFltDeleteGlobalsBase(pGlobals); + } + + return rc; +} + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h new file mode 100644 index 000000000..56d3aac33 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h @@ -0,0 +1,424 @@ +/* $Id: VBoxNetFltInternal.h 15923 2009-01-13 18:20:34Z vboxsync $ */ +/** @file + * VBoxNetFlt - Network Filter Driver (Host), Internal Header. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +#ifndef ___VBoxNetFltInternal_h___ +#define ___VBoxNetFltInternal_h___ + +#include <VBox/sup.h> +#include <VBox/intnet.h> +#include <iprt/semaphore.h> +#include <iprt/assert.h> + + +__BEGIN_DECLS + +/** Pointer to the globals. */ +typedef struct VBOXNETFLTGLOBALS *PVBOXNETFLTGLOBALS; + + +/** + * The state of a filter driver instance. + * + * The state machine differs a bit between the platforms because of + * the way we hook into the stack. On some hosts we can dynamically + * attach when required (on CreateInstance) and on others we will + * have to connect when the network stack is bound up. These modes + * are called static and dynamic config and governed at compile time + * by the VBOXNETFLT_STATIC_CONFIG define. + * + * See sec_netflt_msc for more details on locking and synchronization. + */ +typedef enum VBOXNETFTLINSSTATE +{ + /** The usual invalid state. */ + kVBoxNetFltInsState_Invalid = 0, + /** Initialization. + * We've reserved the interface name but need to attach to the actual + * network interface outside the lock to avoid deadlocks. + * In the dynamic case this happens during a Create(Instance) call. + * In the static case it happens during driver initialization. */ + kVBoxNetFltInsState_Initializing, +#ifdef VBOXNETFLT_STATIC_CONFIG + /** Unconnected, not hooked up to a switch (static only). + * The filter driver instance has been instantiated and hooked up, + * waiting to be connected to an internal network. */ + kVBoxNetFltInsState_Unconnected, +#endif + /** Connected to an internal network. */ + kVBoxNetFltInsState_Connected, + /** Disconnecting from the internal network and possibly the host network interface. + * Partly for reasons of deadlock avoidance again. */ + kVBoxNetFltInsState_Disconnecting, + /** Destroying the instance + * Partly for reasons of deadlock avoidance again. */ + kVBoxNetFltInsState_Destroying, + /** The instance has been disconnected from both the host and the internal network. */ + kVBoxNetFltInsState_Destroyed, + + /** The habitual 32-bit enum hack. */ + kVBoxNetFltInsState_32BitHack = 0x7fffffff +} VBOXNETFTLINSSTATE; + + +/** + * The per-instance data of the VBox filter driver. + * + * This is data associated with a network interface / NIC / wossname which + * the filter driver has been or may be attached to. When possible it is + * attached dynamically, but this may not be possible on all OSes so we have + * to be flexible about things. + * + * A network interface / NIC / wossname can only have one filter driver + * instance attached to it. So, attempts at connecting an internal network + * to an interface that's already in use (connected to another internal network) + * will result in a VERR_SHARING_VIOLATION. + * + * Only one internal network can connect to a filter driver instance. + */ +typedef struct VBOXNETFLTINS +{ + /** Pointer to the next interface in the list. (VBOXNETFLTGLOBAL::pInstanceHead) */ + struct VBOXNETFLTINS *pNext; + /** Our RJ-45 port. + * This is what the internal network plugs into. */ + INTNETTRUNKIFPORT MyPort; + /** The RJ-45 port on the INTNET "switch". + * This is what we're connected to. */ + PINTNETTRUNKSWPORT pSwitchPort; + /** Pointer to the globals. */ + PVBOXNETFLTGLOBALS pGlobals; + + /** The spinlock protecting the state variables and host interface handle. */ + RTSPINLOCK hSpinlock; + /** The current interface state. */ + VBOXNETFTLINSSTATE volatile enmState; + /** Active / Suspended indicator. */ + bool volatile fActive; + /** Disconnected from the host network interface. */ + bool volatile fDisconnectedFromHost; + /** Rediscovery is pending. + * cBusy will never reach zero during rediscovery, so which + * takes care of serializing rediscovery and disconnecting. */ + bool volatile fRediscoveryPending; +#if (ARCH_BITS == 32) && defined(__GNUC__) + uint32_t u32Padding; /**< Alignment padding, will assert in ASMAtomicUoWriteU64 otherwise. */ +#endif + /** The timestamp of the last rediscovery. */ + uint64_t volatile NanoTSLastRediscovery; + /** Reference count. */ + uint32_t volatile cRefs; + /** The busy count. + * This counts the number of current callers and pending packet. */ + uint32_t volatile cBusy; + /** The event that is signaled when we go idle and that pfnWaitForIdle blocks on. */ + RTSEMEVENT hEventIdle; + + union + { +#ifdef VBOXNETFLT_OS_SPECFIC + struct + { +# if defined(RT_OS_DARWIN) + /** @name Darwin instance data. + * @{ */ + /** Pointer to the darwin network interface we're attached to. + * This is treated as highly volatile and should only be read and retained + * while owning hSpinlock. Releasing references to this should not be done + * while owning it though as we might end up destroying it in some paths. */ + ifnet_t volatile pIfNet; + /** The interface filter handle. + * Same access rules as with pIfNet. */ + interface_filter_t volatile pIfFilter; + /** Whether we've need to set promiscuous mode when the interface comes up. */ + bool volatile fNeedSetPromiscuous; + /** Whether we've successfully put the interface into to promiscuous mode. + * This is for dealing with the ENETDOWN case. */ + bool volatile fSetPromiscuous; + /** The MAC address of the interface. */ + RTMAC Mac; + /** @} */ +# elif defined(RT_OS_LINUX) + /** @name Linux instance data + * @{ */ + /** Pointer to the device. */ + struct net_device volatile *pDev; + /** Whether we've successfully put the interface into to promiscuous mode. + * This is for dealing with the ENETDOWN case. */ + bool volatile fPromiscuousSet; + /** Whether device exists and physically attached. */ + bool volatile fRegistered; + /** The MAC address of the interface. */ + RTMAC Mac; + struct notifier_block Notifier; + struct packet_type PacketType; + struct sk_buff_head XmitQueue; + struct work_struct XmitTask; + /** @} */ +# elif defined(RT_OS_SOLARIS) + /** @name Solaris instance data. + * @{ */ + /** Pointer to the bound IPv4 stream. */ + void volatile *pvIp4Stream; + /** Pointer to the bound IPv6 stream. */ + void volatile *pvIp6Stream; + /** Pointer to the bound ARP stream. */ + void volatile *pvArpStream; + /** Pointer to the unbound promiscuous stream. */ + void volatile *pvPromiscStream; + /** Layered device handle to the interface. */ + ldi_handle_t hIface; + /** The MAC address of the interface. */ + RTMAC Mac; + /** Mutex protection used for loopback. */ + RTSEMFASTMUTEX hFastMtx; + /** @} */ +# elif defined(RT_OS_WINDOWS) + /** @name Windows instance data. + * @{ */ + /** Filter driver device context. */ + ADAPT IfAdaptor; + /** Packet worker thread info */ + PACKET_QUEUE_WORKER PacketQueueWorker; + /** The MAC address of the interface. Caching MAC for performance reasons. */ + RTMAC Mac; + /** mutex used to synchronize ADAPT init/deinit */ + RTSEMMUTEX hAdaptMutex; + /** @} */ +# else +# error "PORTME" +# endif + } s; +#endif + /** Padding. */ +#if defined(RT_OS_WINDOWS) +# if defined(VBOX_NETFLT_ONDEMAND_BIND) + uint8_t abPadding[192]; +# else + uint8_t abPadding[1024]; +# endif +#elif defined(RT_OS_LINUX) + uint8_t abPadding[320]; +#else + uint8_t abPadding[64]; +#endif + } u; + + /** The interface name. */ + char szName[1]; +} VBOXNETFLTINS; +/** Pointer to the instance data of a host network filter driver. */ +typedef struct VBOXNETFLTINS *PVBOXNETFLTINS; + +AssertCompileMemberAlignment(VBOXNETFLTINS, NanoTSLastRediscovery, 8); +#ifdef VBOXNETFLT_OS_SPECFIC +AssertCompile(RT_SIZEOFMEMB(VBOXNETFLTINS, u.s) <= RT_SIZEOFMEMB(VBOXNETFLTINS, u.abPadding)); +#endif + + +/** + * The global data of the VBox filter driver. + * + * This contains the bit required for communicating with support driver, VBoxDrv + * (start out as SupDrv). + */ +typedef struct VBOXNETFLTGLOBALS +{ + /** Mutex protecting the list of instances and state changes. */ + RTSEMFASTMUTEX hFastMtx; + /** Pointer to a list of instance data. */ + PVBOXNETFLTINS pInstanceHead; + + /** The INTNET trunk network interface factory. */ + INTNETTRUNKFACTORY TrunkFactory; + /** The SUPDRV component factory registration. */ + SUPDRVFACTORY SupDrvFactory; + /** The number of current factory references. */ + int32_t volatile cFactoryRefs; +#ifdef VBOXNETFLT_STATIC_CONFIG + /* wait timer event */ + RTSEMEVENT hTimerEvent; +#endif + /** The SUPDRV IDC handle (opaque struct). */ + SUPDRVIDCHANDLE SupDrvIDC; +} VBOXNETFLTGLOBALS; + + +DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(int) vboxNetFltTryDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName); + +DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy); +DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy); + +#ifdef VBOXNETFLT_STATIC_CONFIG +DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void * pContext); +DECLHIDDEN(int) vboxNetFltInitGlobalsBase(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(void) vboxNetFltDeleteGlobalsBase(PVBOXNETFLTGLOBALS pGlobals); +DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals); +#endif + + +/** @name The OS specific interface. + * @{ */ +/** + * Try rediscover the host interface. + * + * This is called periodically from the transmit path if we're marked as + * disconnected from the host. There is no chance of a race here. + * + * @returns true if the interface was successfully rediscovered and reattach, + * otherwise false. + * @param pThis The new instance. + */ +DECLHIDDEN(bool) vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis); + +/** + * Transmits a frame. + * + * @return IPRT status code. + * @param pThis The new instance. + * @param pSG The (scatter/)gather list. + * @param fDst The destination mask. At least one bit will be set. + * + * @remarks Owns the out-bound trunk port semaphore. + */ +DECLHIDDEN(int) vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst); + +/** + * Checks if the interface is in promiscuous mode from the host perspective. + * + * If it is, then the internal networking switch will send frames + * heading for the wire to the host as well. + * + * @see INTNETTRUNKIFPORT::pfnIsPromiscuous for more details. + * + * @returns true / false accordingly. + * @param pThis The instance. + * + * @remarks Owns the network lock and the out-bound trunk port semaphores. + */ +DECLHIDDEN(bool) vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis); + +/** + * Get the MAC address of the interface we're attached to. + * + * Used by the internal networking switch for implementing the + * shared-MAC-on-the-wire mode. + * + * @param pThis The instance. + * @param pMac Where to store the MAC address. + * If you don't know, set all the bits except the first (the multicast one). + * + * @remarks Owns the network lock and the out-bound trunk port semaphores. + */ +DECLHIDDEN(void) vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac); + +/** + * Checks if the specified MAC address is for any of the host interfaces. + * + * Used by the internal networking switch to decide the destination(s) + * of a frame. + * + * @returns true / false accordingly. + * @param pThis The instance. + * @param pMac The MAC address. + * + * @remarks Owns the network lock and the out-bound trunk port semaphores. + */ +DECLHIDDEN(bool) vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac); + +/** + * This is called when activating or suspending the instance. + * + * Use this method to enable and disable promiscuous mode on + * the interface to prevent unnecessary interrupt load. + * + * It is only called when the state changes. + * + * @param pThis The instance. + * + * @remarks Owns the lock for the out-bound trunk port. + */ +DECLHIDDEN(void) vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive); + +/** + * This is called to when disconnecting from a network. + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port. + */ +DECLHIDDEN(int) vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis); + +/** + * This is called to when connecting to a network. + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks Owns the semaphores for the global list, the network lock and the out-bound trunk port. + */ +DECLHIDDEN(int) vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis); + +/** + * Counter part to vboxNetFltOsInitInstance(). + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port. + */ +DECLHIDDEN(void) vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis); + +/** + * This is called to attach to the actual host interface + * after linking the instance into the list. + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks Owns no locks. + */ +DECLHIDDEN(int) vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis +#ifdef VBOXNETFLT_STATIC_CONFIG + , void * pContext +#endif + ); + +/** + * This is called to perform structure initializations. + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks Owns no locks. + */ +DECLHIDDEN(int) vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis); +/** @} */ + + +__END_DECLS + +#endif + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist new file mode 100644 index 000000000..940a884e2 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist @@ -0,0 +1,32 @@ + +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> <string>English</string> + <key>CFBundleExecutable</key> <string>VBoxNetFlt</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxNetFlt</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxNetFlt</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>CFBundleGetInfoString</key> <string>VirtualBox @VBOX_VERSION_STRING@, © 2007 Sun Microsystems, Inc.</string> + <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>IOKitPersonalities</key> + <dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.bsd</key> <string>8.8.1</string> + <key>com.apple.kpi.mach</key> <string>8.8.1</string> + <key>com.apple.kpi.libkern</key> <string>8.8.1</string> + <key>com.apple.kpi.unsupported</key> <string>8.8.1</string> + <key>com.apple.kpi.iokit</key> <string>8.8.1</string> + <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> +<!-- + <key>com.apple.kernel.bsd</key> <string>7.9.9</string> + <key>com.apple.kernel.mach</key> <string>7.9.9</string> +--> + </dict> +</dict> +</plist> diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp new file mode 100644 index 000000000..6d2afe61f --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp @@ -0,0 +1,1183 @@ +/* $Id: VBoxNetFlt-darwin.cpp 15527 2008-12-15 18:11:08Z vboxsync $ */ +/** @file + * VBoxNetFlt - Network Filter Driver (Host), Darwin Specific Code. + */ + +/* + * Copyright (C) 2006-2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +/* + * Deal with conflicts first. + * PVM - BSD mess, that FreeBSD has correct a long time ago. + * iprt/types.h before sys/param.h - prevents UINT32_C and friends. + */ +#include <iprt/types.h> +#include <sys/param.h> +#undef PVM + +#include <IOKit/IOLib.h> /* Assert as function */ + +#define LOG_GROUP LOG_GROUP_NET_FLT_DRV +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/version.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> +#include <iprt/alloc.h> +#include <iprt/alloca.h> +#include <iprt/time.h> +#include <iprt/net.h> + +#include <mach/kmod.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/ioccom.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/kern_event.h> +#include <net/kpi_interface.h> +__BEGIN_DECLS /* Buggy 10.4 headers, fixed in 10.5. */ +#include <sys/kpi_mbuf.h> +#include <net/kpi_interfacefilter.h> +__END_DECLS +#include <net/if.h> + +#define VBOXNETFLT_OS_SPECFIC 1 +#include "../VBoxNetFltInternal.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** The maximum number of SG segments. + * Used to prevent stack overflow and similar bad stuff. */ +#define VBOXNETFLT_DARWIN_MAX_SEGS 32 + +#if 0 +/** For testing extremely segmented frames. */ +#define VBOXNETFLT_DARWIN_TEST_SEG_SIZE 14 +#endif + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +__BEGIN_DECLS +static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData); +static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData); +__END_DECLS + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * The mbuf tag data. + * + * We have to associate the ethernet header with each packet we're sending + * because things like icmp will inherit the tag it self so the tag along + * isn't sufficent to identify our mbufs. For the icmp scenario the ethernet + * header naturarlly changes before the packet is send pack, so let check it. + */ +typedef struct VBOXNETFLTTAG +{ + /** The ethernet header of the outgoing frame. */ + RTNETETHERHDR EthHdr; +} VBOXNETFLTTAG; +/** Pointer to a VBoxNetFlt mbuf tag. */ +typedef VBOXNETFLTTAG *PVBOXNETFLTTAG; +/** Pointer to a const VBoxNetFlt mbuf tag. */ +typedef VBOXNETFLTTAG const *PCVBOXNETFLTTAG; + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Declare the module stuff. + */ +__BEGIN_DECLS +extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData); +extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData); + +KMOD_EXPLICIT_DECL(VBoxNetFlt, VBOX_VERSION_STRING, _start, _stop) +DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxNetFltDarwinStart; +DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxNetFltDarwinStop; +DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__; +__END_DECLS + + +/** + * The (common) global data. + */ +static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals; + +/** The unique tag id for this module. + * This is basically a unique string hash that lives on untill reboot. + * It is used for tagging mbufs. */ +static mbuf_tag_id_t g_idTag; + +/** the offset of the struct ifnet::if_pcount variable. */ +static unsigned g_offIfNetPCount = sizeof(void *) * (1 /*if_softc*/ + 1 /*if_name*/ + 2 /*if_link*/ + 2 /*if_addrhead*/ + 1 /*if_check_multi*/) + + sizeof(u_long) /*if_refcnt*/; +/** Macro for accessing ifnet::if_pcount. */ +#define VBOX_GET_PCOUNT(pIfNet) ( *(int *)((uintptr_t)pIfNet + g_offIfNetPCount) ) + + +/** + * Start the kernel module. + */ +static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData) +{ + int rc; + + /* + * Initialize IPRT and find our module tag id. + * (IPRT is shared with VBoxDrv, it creates the loggers.) + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + Log(("VBoxNetFltDarwinStart\n")); + errno_t err = mbuf_tag_id_find("org.VirtualBox.kext.VBoxFltDrv", &g_idTag); + if (!err) + { + /* + * Initialize the globals and connect to the support driver. + * + * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv) + * for establishing the connect to the support driver. + */ + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals); + if (RT_SUCCESS(rc)) + { + LogRel(("VBoxFltDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV)); + return KMOD_RETURN_SUCCESS; + } + + LogRel(("VBoxFltDrv: failed to initialize device extension (rc=%d)\n", rc)); + } + else + LogRel(("VBoxFltDrv: mbuf_tag_id_find failed, err=%d\n", err)); + RTR0Term(); + } + else + printf("VBoxFltDrv: failed to initialize IPRT (rc=%d)\n", rc); + + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + return KMOD_RETURN_FAILURE; +} + + +/** + * Stop the kernel module. + */ +static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData) +{ + Log(("VBoxNetFltDarwinStop\n")); + + /* + * Refuse to unload if anyone is currently using the filter driver. + * This is important as I/O kit / xnu will to be able to do usage + * tracking for us! + */ + int rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltGlobals); + if (RT_FAILURE(rc)) + { + Log(("VBoxNetFltDarwinStop - failed, busy.\n")); + return KMOD_RETURN_FAILURE; + } + + /* + * Undo the work done during start (in reverse order). + */ + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + + RTR0Term(); + + return KMOD_RETURN_SUCCESS; +} + + +/** + * Reads and retains the host interface handle. + * + * @returns The handle, NULL if detached. + * @param pThis + */ +DECLINLINE(ifnet_t) vboxNetFltDarwinRetainIfNet(PVBOXNETFLTINS pThis) +{ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + ifnet_t pIfNet = NULL; + + /* + * Be careful here to avoid problems racing the detached callback. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)) + { + pIfNet = (ifnet_t)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pIfNet); + if (pIfNet) + ifnet_reference(pIfNet); + } + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + return pIfNet; +} + + +/** + * Release the host interface handle previously retained + * by vboxNetFltDarwinRetainIfNet. + * + * @param pThis The instance. + * @param pIfNet The vboxNetFltDarwinRetainIfNet return value, NULL is fine. + */ +DECLINLINE(void) vboxNetFltDarwinReleaseIfNet(PVBOXNETFLTINS pThis, ifnet_t pIfNet) +{ + NOREF(pThis); + if (pIfNet) + ifnet_release(pIfNet); +} + + +/** + * Checks whether this is an mbuf created by vboxNetFltDarwinMBufFromSG, + * i.e. a buffer which we're pushing and should be ignored by the filter callbacks. + * + * @returns true / false accordingly. + * @param pThis The instance. + * @param pMBuf The mbuf. + * @param pvFrame The frame pointer, optional. + */ +DECLINLINE(bool) vboxNetFltDarwinMBufIsOur(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame) +{ + NOREF(pThis); + + /* + * Lookup the tag set by vboxNetFltDarwinMBufFromSG. + */ + PCVBOXNETFLTTAG pTagData; + size_t cbTagData; + errno_t err = mbuf_tag_find(pMBuf, g_idTag, 0 /* type */, &cbTagData, (void **)&pTagData); + if (err) + return false; + AssertReturn(cbTagData == sizeof(*pTagData), false); + + /* + * Dig out the ethernet header from the mbuf. + */ + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame; + if (!pEthHdr) + pEthHdr = (PCRTNETETHERHDR)mbuf_pkthdr_header(pMBuf); + if (!pEthHdr) + pEthHdr = (PCRTNETETHERHDR)mbuf_data(pMBuf); + /* ASSUMING that there is enough data to work on! */ + if ( pEthHdr->DstMac.au8[0] != pTagData->EthHdr.DstMac.au8[0] + || pEthHdr->DstMac.au8[1] != pTagData->EthHdr.DstMac.au8[1] + || pEthHdr->DstMac.au8[2] != pTagData->EthHdr.DstMac.au8[2] + || pEthHdr->DstMac.au8[3] != pTagData->EthHdr.DstMac.au8[3] + || pEthHdr->DstMac.au8[4] != pTagData->EthHdr.DstMac.au8[4] + || pEthHdr->DstMac.au8[5] != pTagData->EthHdr.DstMac.au8[5] + || pEthHdr->SrcMac.au8[0] != pTagData->EthHdr.SrcMac.au8[0] + || pEthHdr->SrcMac.au8[1] != pTagData->EthHdr.SrcMac.au8[1] + || pEthHdr->SrcMac.au8[2] != pTagData->EthHdr.SrcMac.au8[2] + || pEthHdr->SrcMac.au8[3] != pTagData->EthHdr.SrcMac.au8[3] + || pEthHdr->SrcMac.au8[4] != pTagData->EthHdr.SrcMac.au8[4] + || pEthHdr->SrcMac.au8[5] != pTagData->EthHdr.SrcMac.au8[5] + || pEthHdr->EtherType != pTagData->EthHdr.EtherType) + { + Log3(("tagged, but the ethernet header has changed\n")); + return false; + } + + return true; +} + + +/** + * Internal worker that create a darwin mbuf for a (scatter/)gather list. + * + * @returns Pointer to the mbuf. + * @param pThis The instance. + * @param pSG The (scatter/)gather list. + */ +static mbuf_t vboxNetFltDarwinMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG) +{ + /// @todo future? mbuf_how_t How = preemtion enabled ? MBUF_DONTWAIT : MBUF_WAITOK; + mbuf_how_t How = MBUF_WAITOK; + + /* + * We can't make use of the physical addresses on darwin because the way the + * mbuf / cluster stuffe works (see mbuf_data_to_physical and mcl_to_paddr). + * So, because we're lazy, we will ASSUME that all SGs coming from INTNET + * will only contain one single segment. + */ + Assert(pSG->cSegsUsed == 1); + Assert(pSG->cbTotal == pSG->aSegs[0].cb); + Assert(pSG->cbTotal > 0); + + /* + * We need some way of getting back to our instance data when + * the mbuf is freed, so use pvUserData for this. + * -- this is not relevant anylonger! -- + */ + Assert(!pSG->pvUserData || pSG->pvUserData == pThis); + Assert(!pSG->pvUserData2); + pSG->pvUserData = pThis; + + /* + * Allocate a packet and copy over the data. + * + * Using mbuf_attachcluster() here would've been nice but there are two + * issues with it: (1) it's 10.5.x only, and (2) the documentation indicates + * that it's not supposed to be used for really external buffers. The 2nd + * point might be argued against considering that the only m_clattach user + * is mallocs memory for the ext mbuf and not doing what's stated in the docs. + * However, it's hard to tell if these m_clattach buffers actually makes it + * to the NICs or not, and even if they did, the NIC would need the physical + * addresses for the pages they contain and might end up copying the data + * to a new mbuf anyway. + * + * So, in the end it's better to just do it the simple way that will work + * 100%, even if it involes some extra work (alloc + copy) we really wished + * to avoid. + */ + mbuf_t pPkt = NULL; + errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt); + if (!err) + { + /* Skip zero sized memory buffers (paranoia). */ + mbuf_t pCur = pPkt; + while (pCur && !mbuf_maxlen(pCur)) + pCur = mbuf_next(pCur); + Assert(pCur); + + /* Set the required packet header attributes. */ + mbuf_pkthdr_setlen(pPkt, pSG->cbTotal); + mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur)); + + /* Special case the single buffer copy. */ + if ( mbuf_next(pCur) + && mbuf_maxlen(pCur) >= pSG->cbTotal) + { + mbuf_setlen(pCur, pSG->cbTotal); + memcpy(mbuf_data(pCur), pSG->aSegs[0].pv, pSG->cbTotal); + } + else + { + /* Multi buffer copying. */ + size_t cbSrc = pSG->cbTotal; + uint8_t const *pbSrc = (uint8_t const *)pSG->aSegs[0].pv; + while (cbSrc > 0 && pCur) + { + size_t cb = mbuf_maxlen(pCur); + if (cbSrc < cb) + cb = cbSrc; + mbuf_setlen(pCur, cb); + memcpy(mbuf_data(pCur), pbSrc, cb); + + /* advance */ + pbSrc += cb; + cbSrc -= cb; + pCur = mbuf_next(pCur); + } + } + if (!err) + { + /* + * Tag the packet and return successfully. + */ + PVBOXNETFLTTAG pTagData; + err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData); + if (!err) + { + Assert(pSG->aSegs[0].cb >= sizeof(pTagData->EthHdr)); + memcpy(&pTagData->EthHdr, pSG->aSegs[0].pv, sizeof(pTagData->EthHdr)); + return pPkt; + } + + /* bailout: */ + AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err)); + } + + mbuf_freem(pPkt); + } + else + AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err)); + pSG->pvUserData = NULL; + + return NULL; +} + + +/** + * Calculates the number of segments required to represent the mbuf. + * + * @returns Number of segments. + * @param pThis The instance. + * @param pMBuf The mbuf. + * @param pvFrame The frame pointer, optional. + */ +DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame) +{ + NOREF(pThis); + + /* + * Count the buffers in the chain. + */ + unsigned cSegs = 0; + for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur)) + if (mbuf_len(pCur)) + cSegs++; + else if ( !cSegs + && pvFrame + && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf)) + cSegs++; + +#ifdef PADD_RUNT_FRAMES_FROM_HOST + /* + * Add one buffer if the total is less than the ethernet minimum 60 bytes. + * This may allocate a segment too much if the ethernet header is separated, + * but that shouldn't harm us much. + */ + if (mbuf_pkthdr_len(pMBuf) < 60) + cSegs++; +#endif + +#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE + /* maximize the number of segments. */ + cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs); +#endif + + return cSegs ? cSegs : 1; +} + + +/** + * Initializes a SG list from an mbuf. + * + * @returns Number of segments. + * @param pThis The instance. + * @param pMBuf The mbuf. + * @param pSG The SG. + * @param pvFrame The frame pointer, optional. + * @param cSegs The number of segments allocated for the SG. + * This should match the number in the mbuf exactly! + * @param fSrc The source of the frame. + */ +DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc) +{ + NOREF(pThis); + + pSG->pvOwnerData = NULL; + pSG->pvUserData = NULL; + pSG->pvUserData2 = NULL; + pSG->cUsers = 1; + pSG->fFlags = INTNETSG_FLAGS_TEMP; + pSG->cSegsAlloc = cSegs; + + /* + * Walk the chain and convert the buffers to segments. + */ + unsigned iSeg = 0; + pSG->cbTotal = 0; + for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur)) + { + size_t cbSeg = mbuf_len(pCur); + if (cbSeg) + { + void *pvSeg = mbuf_data(pCur); + + /* deal with pvFrame */ + if (!iSeg && pvFrame && pvFrame != pvSeg) + { + void *pvStart = mbuf_datastart(pMBuf); + uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart; + uintptr_t offSegEnd = offSeg + cbSeg; + Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd); + uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart; + if (RT_LIKELY(offFrame < offSeg)) + { + pvSeg = pvFrame; + cbSeg += offSeg - offFrame; + } + else + AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n", + pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf))); + pvFrame = NULL; + } + + AssertBreak(iSeg < cSegs); + pSG->cbTotal += cbSeg; + pSG->aSegs[iSeg].cb = cbSeg; + pSG->aSegs[iSeg].pv = pvSeg; + pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; + iSeg++; + } + /* The pvFrame might be in a now empty buffer. */ + else if ( !iSeg + && pvFrame + && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf)) + { + cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame; + pSG->cbTotal += cbSeg; + pSG->aSegs[iSeg].cb = cbSeg; + pSG->aSegs[iSeg].pv = pvFrame; + pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; + iSeg++; + pvFrame = NULL; + } + } + + Assert(iSeg && iSeg <= cSegs); + pSG->cSegsUsed = iSeg; + +#ifdef PADD_RUNT_FRAMES_FROM_HOST + /* + * Add a trailer if the frame is too small. + * + * Since we're getting to the packet before it is framed, it has not + * yet been padded. The current solution is to add a segment pointing + * to a buffer containing all zeros and pray that works for all frames... + */ + if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST)) + { + AssertReturnVoid(iSeg < cSegs); + + static uint8_t const s_abZero[128] = {0}; + pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; + pSG->aSegs[iSeg].pv = (void *)&s_abZero[0]; + pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal; + pSG->cbTotal = 60; + pSG->cSegsUsed++; + } +#endif + +#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE + /* + * Redistribute the segments. + */ + if (pSG->cSegsUsed < pSG->cSegsAlloc) + { + /* copy the segments to the end. */ + int iSrc = pSG->cSegsUsed; + int iDst = pSG->cSegsAlloc; + while (iSrc > 0) + { + iDst--; + iSrc--; + pSG->aSegs[iDst] = pSG->aSegs[iSrc]; + } + + /* create small segments from the start. */ + pSG->cSegsUsed = pSG->cSegsAlloc; + iSrc = iDst; + iDst = 0; + while ( iDst < iSrc + && iDst < pSG->cSegsAlloc) + { + pSG->aSegs[iDst].Phys = NIL_RTHCPHYS; + pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv; + pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE); + if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb) + { + pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb; + pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb; + } + else if (++iSrc >= pSG->cSegsAlloc) + { + pSG->cSegsUsed = iDst + 1; + break; + } + iDst++; + } + } +#endif + + AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg)); +} + + +/** + * + * @see iff_detached_func in the darwin kpi. + */ +static void vboxNetFltDarwinIffDetached(void *pvThis, ifnet_t pIfNet) +{ + PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis; + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + uint64_t NanoTS = RTTimeSystemNanoTS(); + LogFlow(("vboxNetFltDarwinIffDetached: pThis=%p NanoTS=%RU64 (%d)\n", + pThis, NanoTS, VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1)); + + Assert(!pThis->fDisconnectedFromHost); + Assert(!pThis->fRediscoveryPending); + + /* + * If we've put it into promiscuous mode, undo that now. If we don't + * the if_pcount will go all wrong when it's replugged. + */ + if (ASMAtomicXchgBool(&pThis->u.s.fSetPromiscuous, false)) + ifnet_set_promiscuous(pIfNet, 0); + + /* + * We carefully take the spinlock and increase the interface reference + * behind it in order to avoid problematic races with the detached callback. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + + pIfNet = (ifnet_t)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pIfNet); + int cPromisc = VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : - 1; + + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pIfNet, NULL); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pIfFilter, NULL); + ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false); + pThis->u.s.fSetPromiscuous = false; + ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, NanoTS); + ASMAtomicUoWriteBool(&pThis->fRediscoveryPending, false); + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true); + + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + if (pIfNet) + ifnet_release(pIfNet); + LogRel(("VBoxNetFlt: was detached from '%s' (%d)\n", pThis->szName, cPromisc)); +} + + +/** + * + * @see iff_ioctl_func in the darwin kpi. + */ +static errno_t vboxNetFltDarwinIffIoCtl(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, u_long uCmd, void *pvArg) +{ + PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis; + LogFlow(("vboxNetFltDarwinIffIoCtl: pThis=%p uCmd=%lx\n", pThis, uCmd)); + + /* + * Update fOtherPromiscuous. + */ + /** @todo we'll have to find the offset of if_pcount to get this right! */ + //if (uCmd == SIOCSIFFLAGS) + //{ + // + //} + + /* + * We didn't handle it, continue processing. + */ + NOREF(pThis); + NOREF(eProtocol); + NOREF(uCmd); + NOREF(pvArg); + return EOPNOTSUPP; +} + + +/** + * + * @see iff_event_func in the darwin kpi. + */ +static void vboxNetFltDarwinIffEvent(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, const struct kev_msg *pEvMsg) +{ + PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis; + LogFlow(("vboxNetFltDarwinIffEvent: pThis=%p\n", pThis)); + + NOREF(pThis); + NOREF(pIfNet); + NOREF(eProtocol); + NOREF(pEvMsg); + + /* + * Watch out for the interface going online / offline. + */ + if ( VALID_PTR(pThis) + && VALID_PTR(pEvMsg) + && pEvMsg->vendor_code == KEV_VENDOR_APPLE + && pEvMsg->kev_class == KEV_NETWORK_CLASS + && pEvMsg->kev_subclass == KEV_DL_SUBCLASS) + { + if (pThis->u.s.pIfNet == pIfNet) + { + if (pEvMsg->event_code == KEV_DL_LINK_ON) + { + if (ASMAtomicUoReadBool(&pThis->u.s.fNeedSetPromiscuous)) + { + /* failed to bring it online. */ + errno_t err = ifnet_set_promiscuous(pIfNet, 1); + if (!err) + { + ASMAtomicWriteBool(&pThis->u.s.fSetPromiscuous, true); + ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false); + Log(("vboxNetFltDarwinIffEvent: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet))); + } + else + Log(("vboxNetFltDarwinIffEvent: ifnet_set_promiscuous failed on %s, err=%d (%d)\n", pThis->szName, err, VBOX_GET_PCOUNT(pIfNet))); + } + else if ( ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous) + && !(ifnet_flags(pIfNet) & IFF_PROMISC)) + { + /* Try fix the inconsistency. */ + errno_t err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC); + if (!err) + err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL); + if (!err && (ifnet_flags(pIfNet) & IFF_PROMISC)) + Log(("vboxNetFltDarwinIffEvent: fixed IFF_PROMISC on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet))); + else + Log(("vboxNetFltDarwinIffEvent: failed to fix IFF_PROMISC on %s, err=%d flags=%#x (%d)\n", + pThis->szName, err, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet))); + } + else + Log(("vboxNetFltDarwinIffEvent: online, '%s'. flags=%#x (%d)\n", pThis->szName, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet))); + } + else if (pEvMsg->event_code == KEV_DL_LINK_OFF) + Log(("vboxNetFltDarwinIffEvent: %s goes down (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet))); + } + else + Log(("vboxNetFltDarwinIffEvent: pThis->u.s.pIfNet=%p pIfNet=%p (%d)\n", pThis->u.s.pIfNet, pIfNet, VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1)); + } + else if (VALID_PTR(pEvMsg)) + Log(("vboxNetFltDarwinIffEvent: vendor_code=%#x kev_class=%#x kev_subclass=%#x event_code=%#x\n", + pEvMsg->vendor_code, pEvMsg->kev_class, pEvMsg->kev_subclass, pEvMsg->event_code)); +} + + +/** + * Internal worker for vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput, + * + * @returns 0 or EJUSTRETURN. + * @param pThis The instance. + * @param pMBuf The mbuf. + * @param pvFrame The start of the frame, optional. + * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value. + * @param eProtocol The protocol. + */ +static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, + uint32_t fSrc, protocol_family_t eProtocol) +{ + /* + * Drop it immediately? + */ + Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n", + pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1)); + if (!pMBuf) + return 0; +#if 0 /* debugging lost icmp packets */ + if (mbuf_pkthdr_len(pMBuf) > 0x300) + { + uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf)); + Log3(("D=%.6Rhxs S=%.6Rhxs T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12)))); + } +#endif + if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame)) + return 0; + + /* + * Active? Retain the instance and increment the busy counter. + */ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + const bool fActive = ASMAtomicUoReadBool(&pThis->fActive); + if (fActive) + vboxNetFltRetain(pThis, true /* fBusy */); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + if (!fActive) + return 0; + + /* + * Finalize out-bound packets since the stack puts off finalizing + * TCP/IP checksums as long as possible. + * ASSUMES this only applies to outbound IP packets. + */ + if ( (fSrc & INTNETTRUNKDIR_HOST) + && eProtocol == PF_INET) + { + Assert(!pvFrame); + mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR)); + } + + /* + * Create a (scatter/)gather list for the mbuf and feed it to the internal network. + */ + bool fDropIt = false; + unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame); + if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS) + { + PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs])); + vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc); + + fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc); + if (fDropIt) + mbuf_freem(pMBuf); + } + + vboxNetFltRelease(pThis, true /* fBusy */); + + return fDropIt ? EJUSTRETURN : 0; +} + + +/** + * From the host. + * + * @see iff_output_func in the darwin kpi. + */ +static errno_t vboxNetFltDarwinIffOutput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf) +{ + /** @todo there was some note about the ethernet header here or something like that... */ + + NOREF(eProtocol); + NOREF(pIfNet); + return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, NULL, INTNETTRUNKDIR_HOST, eProtocol); +} + + +/** + * From the wire. + * + * @see iff_input_func in the darwin kpi. + */ +static errno_t vboxNetFltDarwinIffInput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf, char **ppchFrame) +{ + NOREF(eProtocol); + NOREF(pIfNet); + return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, *ppchFrame, INTNETTRUNKDIR_WIRE, eProtocol); +} + + +/** + * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered. + * + * @returns VBox status code. + * @param pThis The instance. + * @param fRediscovery If set we're doing a rediscovery attempt, so, don't + * flood the release log. + */ +static int vboxNetFltDarwinAttachToInterface(PVBOXNETFLTINS pThis, bool fRediscovery) +{ + LogFlow(("vboxNetFltDarwinAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName)); + + /* + * Locate the interface first. + * + * The pIfNet member is updated before iflt_attach is called and used + * to deal with the hypothetical case where someone rips out the + * interface immediately after our iflt_attach call. + */ + ifnet_t pIfNet = NULL; + errno_t err = ifnet_find_by_name(pThis->szName, &pIfNet); + if (err) + { + Assert(err == ENXIO); + if (!fRediscovery) + LogRel(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err)); + else + Log(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err)); + return VERR_INTNET_FLT_IF_NOT_FOUND; + } + + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pIfNet, pIfNet); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + /* + * Get the mac address while we still have a valid ifnet reference. + */ + err = ifnet_lladdr_copy_bytes(pIfNet, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac)); + if (!err) + { + /* + * Try attach the filter. + */ + struct iff_filter RegRec; + RegRec.iff_cookie = pThis; + RegRec.iff_name = "VBoxNetFlt"; + RegRec.iff_protocol = 0; + RegRec.iff_input = vboxNetFltDarwinIffInput; + RegRec.iff_output = vboxNetFltDarwinIffOutput; + RegRec.iff_event = vboxNetFltDarwinIffEvent; + RegRec.iff_ioctl = vboxNetFltDarwinIffIoCtl; + RegRec.iff_detached = vboxNetFltDarwinIffDetached; + interface_filter_t pIfFilter = NULL; + err = iflt_attach(pIfNet, &RegRec, &pIfFilter); + Assert(err || pIfFilter); + + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + pIfNet = (ifnet_t)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pIfNet); + if (pIfNet && !err) + { + ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pIfFilter, pIfFilter); + pIfNet = NULL; /* don't dereference it */ + } + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + } + + /* Release the interface on failure. */ + if (pIfNet) + ifnet_release(pIfNet); + + int rc = RTErrConvertFromErrno(err); + if (RT_SUCCESS(rc)) + LogRel(("VBoxFltDrv: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.Mac), &pThis->u.s.Mac)); + else + LogRel(("VBoxFltDrv: failed to attach to ifnet '%s' (err=%d)\n", pThis->szName, err)); + return rc; +} + + +bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + vboxNetFltDarwinAttachToInterface(pThis, true /* fRediscovery */); + return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); +} + + +int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst) +{ + int rc = VINF_SUCCESS; + ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis); + if (pIfNet) + { + /* + * Create a mbuf for the gather list and push it onto the wire. + */ + if (fDst & INTNETTRUNKDIR_WIRE) + { + mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); + if (pMBuf) + { + errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf); + if (err) + rc = RTErrConvertFromErrno(err); + } + else + rc = VERR_NO_MEMORY; + } + + /* + * Create a mbuf for the gather list and push it onto the host stack. + */ + if (fDst & INTNETTRUNKDIR_HOST) + { + mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); + if (pMBuf) + { + /* This is what IONetworkInterface::inputPacket does. */ + unsigned const cbEthHdr = 14; + mbuf_pkthdr_setheader(pMBuf, mbuf_data(pMBuf)); + mbuf_pkthdr_setlen(pMBuf, mbuf_pkthdr_len(pMBuf) - cbEthHdr); + mbuf_setdata(pMBuf, (uint8_t *)mbuf_data(pMBuf) + cbEthHdr, mbuf_len(pMBuf) - cbEthHdr); + mbuf_pkthdr_setrcvif(pMBuf, pIfNet); /* will crash without this. */ + + errno_t err = ifnet_input(pIfNet, pMBuf, NULL); + if (err) + rc = RTErrConvertFromErrno(err); + } + else + rc = VERR_NO_MEMORY; + } + + vboxNetFltDarwinReleaseIfNet(pThis, pIfNet); + } + + return rc; +} + + +bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis) +{ + bool fRc = false; + ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis); + if (pIfNet) + { + /* gather the data */ + uint16_t fIf = ifnet_flags(pIfNet); + unsigned cPromisc = VBOX_GET_PCOUNT(pIfNet); + bool fSetPromiscuous = ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous); + vboxNetFltDarwinReleaseIfNet(pThis, pIfNet); + + /* calc the return. */ + fRc = (fIf & IFF_PROMISC) + && cPromisc > fSetPromiscuous; + } + return fRc; +} + + +void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac) +{ + *pMac = pThis->u.s.Mac; +} + + +bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac) +{ + /* ASSUMES that the MAC address never changes. */ + return pThis->u.s.Mac.au16[0] == pMac->au16[0] + && pThis->u.s.Mac.au16[1] == pMac->au16[1] + && pThis->u.s.Mac.au16[2] == pMac->au16[2]; +} + + +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive) +{ + ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis); + if (pIfNet) + { + /* + * This api is a bit weird, the best reference is the code. + * + * Also, we have a bit or race conditions wrt the maintance of + * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous. + */ + unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pIfNet); + u_int16_t fIf; + if (fActive) + { + Assert(!pThis->u.s.fSetPromiscuous); + errno_t err = ENETDOWN; + ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, true); + + /* + * Try bring the interface up and running if it's down. + */ + fIf = ifnet_flags(pIfNet); + if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) + { + err = ifnet_set_flags(pIfNet, IFF_UP, IFF_UP); + errno_t err2 = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL); + if (!err) + err = err2; + fIf = ifnet_flags(pIfNet); + } + + /* + * Is it already up? If it isn't, leave it to the link event or + * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird). + */ + if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + { + err = ifnet_set_promiscuous(pIfNet, 1); + pThis->u.s.fSetPromiscuous = err == 0; + if (!err) + { + ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false); + + /* check if it actually worked, this stuff is not always behaving well. */ + if (!(ifnet_flags(pIfNet) & IFF_PROMISC)) + { + err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC); + if (!err) + err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL); + if (!err) + Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + else + Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n", + pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + } + } + else + Log(("VBoxNetFlt: ifnet_set_promiscuous -> err=%d grr! (%d->%d)\n", err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + } + else if (!err) + Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + if (err) + LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + } + else + { + ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false); + if (pThis->u.s.fSetPromiscuous) + { + errno_t err = ifnet_set_promiscuous(pIfNet, 0); + AssertMsg(!err, ("%d\n", err)); NOREF(err); + } + pThis->u.s.fSetPromiscuous = false; + + fIf = ifnet_flags(pIfNet); + Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet))); + } + + vboxNetFltDarwinReleaseIfNet(pThis, pIfNet); + } +} + + +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis) +{ + /* Nothing to do here. */ + return VINF_SUCCESS; +} + + +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis) +{ + /* Nothing to do here. */ + return VINF_SUCCESS; +} + + +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis) +{ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + interface_filter_t pIfFilter; + + /* + * Carefully obtain the interface filter reference and detach it. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + pIfFilter = (interface_filter_t)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pIfFilter); + if (pIfFilter) + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pIfFilter, NULL); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + if (pIfFilter) + iflt_detach(pIfFilter); +} + + +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis) +{ + return vboxNetFltDarwinAttachToInterface(pThis, false /* fRediscovery */); +} + + +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis) +{ + /* + * Init the darwin specific members. + */ + pThis->u.s.pIfNet = NULL; + pThis->u.s.pIfFilter = NULL; + pThis->u.s.fSetPromiscuous = false; + pThis->u.s.fNeedSetPromiscuous = false; + //pThis->u.s.Mac = {0}; + + return VINF_SUCCESS; +} + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh new file mode 100755 index 000000000..810189ab7 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh @@ -0,0 +1,114 @@ +#!/bin/bash +## @file +# For development. +# + +# +# Copyright (C) 2006-2008 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SCRIPT_NAME="loadusb" +XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1` + +DRVNAME="VBoxNetFlt.kext" +BUNDLE="org.virtualbox.kext.VBoxNetFlt" + +if [ "$XNU_VERSION" -ge "9" ]; then + DEP_DRVNAME="VBoxDrv.kext" +else + DEP_DRVNAME="VBoxDrvTiger.kext" +fi +DEP_BUNDLE="org.virtualbox.kext.VBoxDrv" + + +DIR=`dirname "$0"` +DIR=`cd "$DIR" && pwd` +DEP_DIR="$DIR/$DEP_DRVNAME" +DIR="$DIR/$DRVNAME" +if [ ! -d "$DIR" ]; then + echo "Cannot find $DIR or it's not a directory..." + exit 1; +fi +if [ ! -d "$DEP_DIR" ]; then + echo "Cannot find $DEP_DIR or it's not a directory... (dependency)" + exit 1; +fi +if [ -n "$*" ]; then + OPTS="$*" +else + OPTS="-t" +fi + +trap "sudo chown -R `whoami` $DIR $DEP_DIR; exit 1" INT + +# Try unload any existing instance first. +LOADED=`kextstat -b $BUNDLE -l` +if test -n "$LOADED"; then + echo "${SCRIPT_NAME}.sh: Unloading $BUNDLE..." + sudo kextunload -v 6 -b $BUNDLE + LOADED=`kextstat -b $BUNDLE -l` + if test -n "$LOADED"; then + echo "${SCRIPT_NAME}.sh: failed to unload $BUNDLE, see above..." + exit 1; + fi + echo "${SCRIPT_NAME}.sh: Successfully unloaded $BUNDLE" +fi + +set -e + +# Copy the .kext to the symbols directory and tweak the kextload options. +if test -n "$VBOX_DARWIN_SYMS"; then + echo "${SCRIPT_NAME}.sh: copying the extension the symbol area..." + rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME" + mkdir -p "$VBOX_DARWIN_SYMS" + cp -R "$DIR" "$VBOX_DARWIN_SYMS/" + OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ " + sync +fi + +# On smbfs, this might succeed just fine but make no actual changes, +# so we might have to temporarily copy the driver to a local directory. +sudo chown -R root:wheel "$DIR" "$DEP_DIR" +OWNER=`/usr/bin/stat -f "%u" "$DIR"` +if test "$OWNER" -ne 0; then + TMP_DIR=/tmp/${SCRIPT_NAME}.tmp + echo "${SCRIPT_NAME}.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME" + + # clean up first (no sudo rm) + if test -e "$TMP_DIR"; then + sudo chown -R `whoami` "$TMP_DIR" + rm -Rf "$TMP_DIR" + fi + + # make a copy and switch over DIR + mkdir -p "$TMP_DIR/" + sudo cp -Rp "$DIR" "$TMP_DIR/" + DIR="$TMP_DIR/$DRVNAME" + + # load.sh puts it here. + DEP_DIR="/tmp/loaddrv.tmp/$DEP_DRVNAME" + + # retry + sudo chown -R root:wheel "$DIR" "$DEP_DIR" +fi + +sudo chmod -R o-rwx "$DIR" +sync +echo "${SCRIPT_NAME}.sh: loading $DIR... (kextload $OPTS \"$DIR\")" +sudo kextload $OPTS -d "$DEP_DIR" "$DIR" +sync +sudo chown -R `whoami` "$DIR" "$DEP_DIR" +kextstat | grep org.virtualbox.kext + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile new file mode 100644 index 000000000..0f7b05556 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile @@ -0,0 +1,264 @@ +# +# Makefile for the VirtualBox Linux Host Network Filter Driver. +# (For 2.6.x this file must be called 'Makefile'!) +# + +# +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +# +# First, figure out which architecture we're targeting and the build type. +# (We have to support basic cross building (ARCH=i386|x86_64).) +# While at it, warn about BUILD_* vars found to help with user problems. +# +ifneq ($(filter-out amd64 x86,$(BUILD_TARGET_ARCH)),) + $(warning Ignoring unknown BUILD_TARGET_ARCH value '$(BUILD_TARGET_ARCH)'.) + BUILD_TARGET_ARCH := +endif +ifeq ($(BUILD_TARGET_ARCH),) + ifeq ($(ARCH),x86_64) + BUILD_TARGET_ARCH := amd64 + else + ifeq ($(ARCH),i386) + BUILD_TARGET_ARCH := x86 + else + ifeq ($(filter-out x86_64 amd64 AMD64,$(shell uname -m)),) + BUILD_TARGET_ARCH := amd64 + else + BUILD_TARGET_ARCH := x86 + endif + endif + endif +else + $(warning Using BUILD_TARGET_ARCH='$(BUILD_TARGET_ARCH)' from the $(origin BUILD_TARGET_ARCH).) +endif + +ifneq ($(filter-out release profile debug strict,$(BUILD_TYPE)),) + $(warning Ignoring unknown BUILD_TYPE value '$(BUILD_TYPE)'.) + BUILD_TYPE := +endif +ifeq ($(BUILD_TYPE),) + BUILD_TYPE := release +else + $(warning Using BUILD_TYPE='$(BUILD_TYPE)' from the $(origin BUILD_TYPE).) +endif + +# override is required by the Debian guys +override MODULE = vboxnetflt +OBJS = \ + linux/VBoxNetFlt-linux.o \ + VBoxNetFlt.o \ + SUPR0IdcClient.o \ + SUPR0IdcClientComponent.o \ + SUPR0IdcClient-linux.o \ + r0drv/alloc-r0drv.o \ + r0drv/initterm-r0drv.o \ + r0drv/memobj-r0drv.o \ + r0drv/mpnotification-r0drv.o \ + r0drv/powernotification-r0drv.o \ + r0drv/linux/assert-r0drv-linux.o \ + r0drv/linux/alloc-r0drv-linux.o \ + r0drv/linux/initterm-r0drv-linux.o \ + r0drv/linux/memobj-r0drv-linux.o \ + r0drv/linux/mp-r0drv-linux.o \ + r0drv/linux/mpnotification-r0drv-linux.o \ + r0drv/linux/process-r0drv-linux.o \ + r0drv/linux/semevent-r0drv-linux.o \ + r0drv/linux/semeventmulti-r0drv-linux.o \ + r0drv/linux/semfastmutex-r0drv-linux.o \ + r0drv/linux/spinlock-r0drv-linux.o \ + r0drv/linux/thread-r0drv-linux.o \ + r0drv/linux/thread2-r0drv-linux.o \ + r0drv/linux/time-r0drv-linux.o \ + common/err/RTErrConvertFromErrno.o \ + common/err/RTErrConvertToErrno.o \ + common/log/log.o \ + common/log/logellipsis.o \ + common/log/logrel.o \ + common/log/logrelellipsis.o \ + common/log/logcom.o \ + common/log/logformat.o \ + common/string/strformat.o \ + common/string/strformatrt.o \ + common/string/strformattype.o \ + common/string/strprintf.o \ + common/string/strtonum.o \ + r0drv/linux/RTLogWriteDebugger-r0drv-linux.o \ + generic/RTAssertShouldPanic-generic.o \ + generic/RTLogWriteStdErr-stub-generic.o \ + generic/RTLogWriteStdOut-stub-generic.o \ + generic/RTLogWriteUser-generic.o \ + generic/uuid-generic.o \ + VBox/log-vbox.o \ + VBox/strformat-vbox.o +ifeq ($(BUILD_TARGET_ARCH),x86) +OBJS += math/gcc/divdi3.o \ + math/gcc/moddi3.o \ + math/gcc/qdivrem.o \ + math/gcc/udivdi3.o \ + math/gcc/divdi3.o \ + math/gcc/umoddi3.o +endif +ifeq ($(BUILD_TARGET_ARCH),amd64) +OBJS += alloc/heapsimple.o +endif + +ifneq ($(MAKECMDGOALS),clean) + +ifeq ($(KERNELRELEASE),) + + # + # building from this directory + # + + # kernel base directory + ifndef KERN_DIR + # build for the current kernel, version check + KERN_DIR := /lib/modules/$(shell uname -r)/build + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + KERN_DIR := /usr/src/linux + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + $(error Error: unable to find the sources of your current Linux kernel. \ + Specify KERN_DIR=<directory> and run Make again) + endif + $(warning Warning: using /usr/src/linux as the source directory of your \ + Linux kernel. If this is not correct, specify \ + KERN_DIR=<directory> and run Make again.) + endif + # check if versions match -- works only for later 2.6 kernels + VBOX_KERN_VER := $(shell $(MAKE) -sC $(KERN_DIR) kernelrelease 2> /dev/null || true) + ifneq ($(VBOX_KERN_VER),) + ifneq ($(VBOX_KERN_VER),$(shell uname -r)) + $(error Error: /usr/src/linux (version $(VBOX_KERN_VER)) does not match \ + the current kernel (version $(shell uname -r))) + endif + endif + else + # build for a dedicated kernel, no version check + ifneq ($(shell if test -d $(KERN_DIR); then echo yes; fi),yes) + $(error Error: KERN_DIR does not point to a directory) + endif + endif + + # includes + ifndef KERN_INCL + KERN_INCL = $(KERN_DIR)/include + endif + ifneq ($(shell if test -d $(KERN_INCL); then echo yes; fi),yes) + $(error Error: unable to find the include directory for your current Linux \ + kernel. Specify KERN_INCL=<directory> and run Make again) + endif + + # module install dir, only for current kernel + ifneq ($(filter install install_rpm,$(MAKECMDGOALS)),) + ifndef MODULE_DIR + MODULE_DIR_TST := /lib/modules/$(shell uname -r) + ifeq ($(shell if test -d $(MODULE_DIR_TST); then echo yes; fi),yes) + MODULE_DIR := $(MODULE_DIR_TST)/misc + else + $(error Unable to find the folder to install the support driver to) + endif + endif # MODULE_DIR unspecified + endif + +else # neq($(KERNELRELEASE),) + + # + # building from kbuild (make -C <kernel_directory> M=`pwd`) + # + +endif # neq($(KERNELRELEASE),) + +# debug - show guesses. +ifdef DEBUG +$(warning dbg: KERN_DIR = $(KERN_DIR)) +$(warning dbg: KERN_INCL = $(KERN_INCL)) +$(warning dbg: MODULE_DIR = $(MODULE_DIR)) +endif + +KBUILD_VERBOSE ?= 1 + +# +# Compiler options +# +ifndef INCL + INCL := $(addprefix -I,$(KERN_INCL) $(EXTRA_INCL)) + ifndef KBUILD_EXTMOD + KBUILD_EXTMOD := $(shell pwd) + endif + INCL += $(addprefix -I$(KBUILD_EXTMOD),/ /include /r0drv/linux) + export INCL +endif +KFLAGS := -D__KERNEL__ -DMODULE -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -DVBOX_WITH_HARDENING +ifdef VBOX_REDHAT_KABI + KFLAGS += -DVBOX_REDHAT_KABI +endif +ifeq ($(BUILD_TARGET_ARCH),amd64) + KFLAGS += -DRT_ARCH_AMD64 +else + KFLAGS += -DRT_ARCH_X86 +endif +# must be consistent with Config.kmk! +KFLAGS += -DVBOX_WITH_64_BITS_GUESTS +ifeq ($(BUILD_TYPE),debug) + KFLAGS += -DDEBUG -DDEBUG_$(USER) -g + # IPRT_DEBUG_SEMS indicates thread wrt sems state via the comm field. + KFLAGS += -DIPRT_DEBUG_SEMS +endif + +# By default we use remap_pfn_range() kernel API to make kernel pages +# visible for userland. Unfortuately, it leads to situation that +# during debug session all structures on that page (such as PVM pointer) +# are not accessible to the debugger (see #3214). +# This code enables experimental support +# for vm_insert_page() kernel API, allowing to export kernel pages +# to the userland in more debugger-friendly way. Due to stability +# concerns, not enabled by default yet. +ifdef VBOX_USE_INSERT_PAGE + KFLAGS += -DVBOX_USE_INSERT_PAGE +endif + +MODULE_EXT := ko +$(MODULE)-y := $(OBJS) + +# build defs +EXTRA_CFLAGS += $(INCL) $(KFLAGS) $(KDEBUG) + +all: $(MODULE) + +obj-m += $(MODULE).o + +$(MODULE): + $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C $(KERN_DIR) SUBDIRS=$(CURDIR) SRCROOT=$(CURDIR) modules + +install: $(MODULE) + @mkdir -p $(MODULE_DIR); \ + install -m 0664 -o root -g root $(MODULE).$(MODULE_EXT) $(MODULE_DIR); \ + PATH="$(PATH):/bin:/sbin" depmod -ae; \ + rm -f /etc/vbox/module_not_compiled + +install_rpm: $(MODULE) + @mkdir -p $(MODULE_DIR); \ + install -m 0664 $(MODULE).$(MODULE_EXT) $(MODULE_DIR) + +endif # eq($(MAKECMDGOALS),clean) + +# important: Don't remove Module.symvers! DKMS does 'make clean' before building ... +clean: + for f in . linux r0drv r0drv/linux VBox common/err common/string common/log generic math/gcc; \ + do rm -f $$f/*.o $$f/.*.cmd $$f/.*.flags; done + rm -rf .vboxnetflt* .tmp_ver* vboxnetflt.* Modules.symvers modules.order diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c new file mode 100644 index 000000000..ba74ed7f1 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c @@ -0,0 +1,1099 @@ +/* $Id: VBoxNetFlt-linux.c 16098 2009-01-20 19:22:12Z vboxsync $ */ +/** @file + * VBoxNetFlt - Network Filter Driver (Host), Linux Specific Code. + */ + +/* + * Copyright (C) 2006-2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "the-linux-kernel.h" +#include "version-generated.h" +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> + +#define LOG_GROUP LOG_GROUP_NET_FLT_DRV +#include <VBox/log.h> +#include <VBox/err.h> +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/log.h> +#include <iprt/mp.h> +#include <iprt/mem.h> +#include <iprt/time.h> + +#define VBOXNETFLT_OS_SPECFIC 1 +#include "../VBoxNetFltInternal.h" + +#define VBOX_FLT_NB_TO_INST(pNB) ((PVBOXNETFLTINS)((uint8_t *)pNB - \ + RT_OFFSETOF(VBOXNETFLTINS, u.s.Notifier))) +#define VBOX_FLT_PT_TO_INST(pPT) ((PVBOXNETFLTINS)((uint8_t *)pPT - \ + RT_OFFSETOF(VBOXNETFLTINS, u.s.PacketType))) +#define VBOX_FLT_XT_TO_INST(pXT) ((PVBOXNETFLTINS)((uint8_t *)pXT - \ + RT_OFFSETOF(VBOXNETFLTINS, u.s.XmitTask))) + +#define VBOX_GET_PCOUNT(pDev) (pDev->promiscuity) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb_reset_network_header(skb) +# define VBOX_SKB_RESET_MAC_HDR(skb) skb_reset_mac_header(skb) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */ +# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb->nh.raw = skb->data +# define VBOX_SKB_RESET_MAC_HDR(skb) skb->mac.raw = skb->data +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */ +# define CHECKSUM_PARTIAL CHECKSUM_HW +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0) +# else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */ +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7) +# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0) +# else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 7) */ +# define VBOX_SKB_CHECKSUM_HELP(skb) (!skb_checksum_help(skb)) +# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 7) */ +# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */ +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) +# define VBOX_SKB_IS_GSO(skb) skb_is_gso(skb) + /* No features, very dumb device */ +# define VBOX_SKB_GSO_SEGMENT(skb) skb_gso_segment(skb, 0) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */ +# define VBOX_SKB_IS_GSO(skb) false +# define VBOX_SKB_GSO_SEGMENT(skb) NULL +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */ + +#ifndef NET_IP_ALIGN +# define NET_IP_ALIGN 2 +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) +unsigned dev_get_flags(const struct net_device *dev) +{ + unsigned flags; + + flags = (dev->flags & ~(IFF_PROMISC | + IFF_ALLMULTI | + IFF_RUNNING)) | + (dev->gflags & (IFF_PROMISC | + IFF_ALLMULTI)); + + if (netif_running(dev) && netif_carrier_ok(dev)) + flags |= IFF_RUNNING; + + return flags; +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) */ + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int VBoxNetFltLinuxInit(void); +static void VBoxNetFltLinuxUnload(void); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * The (common) global data. + */ +#ifdef RT_ARCH_AMD64 +/** + * Memory for the executable memory heap (in IPRT). + */ +extern uint8_t g_abExecMemory[4096]; /* cannot donate less than one page */ +__asm__(".section execmemory, \"awx\", @progbits\n\t" + ".align 32\n\t" + ".globl g_abExecMemory\n" + "g_abExecMemory:\n\t" + ".zero 4096\n\t" + ".type g_abExecMemory, @object\n\t" + ".size g_abExecMemory, 4096\n\t" + ".text\n\t"); +#endif + +static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals; + +module_init(VBoxNetFltLinuxInit); +module_exit(VBoxNetFltLinuxUnload); + +MODULE_AUTHOR("Sun Microsystems, Inc."); +MODULE_DESCRIPTION("VirtualBox Network Filter Driver"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +# define xstr(s) str(s) +# define str(s) #s +MODULE_VERSION(VBOX_VERSION_STRING " (" xstr(INTNETTRUNKIFPORT_VERSION) ")"); +#endif + +/** + * The (common) global data. + */ +static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals; + + +/** + * Initialize module. + * + * @returns appropriate status code. + */ +static int __init VBoxNetFltLinuxInit(void) +{ + int rc; + /* + * Initialize IPRT. + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { +#ifdef RT_ARCH_AMD64 + rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory)); + printk("VBoxNetFlt: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]); + if (RT_FAILURE(rc)) + { + printk("VBoxNetFlt: failed to donate exec memory, no logging will be available.\n"); + } +#endif + Log(("VBoxNetFltLinuxInit\n")); + + /* + * Initialize the globals and connect to the support driver. + * + * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv) + * for establishing the connect to the support driver. + */ + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals); + if (RT_SUCCESS(rc)) + { + LogRel(("VBoxNetFlt: Successfully started.\n")); + return 0; + } + + LogRel(("VBoxNetFlt: failed to initialize device extension (rc=%d)\n", rc)); + RTR0Term(); + } + else + LogRel(("VBoxNetFlt: failed to initialize IPRT (rc=%d)\n", rc)); + + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + return -RTErrConvertToErrno(rc); +} + + +/** + * Unload the module. + * + * @todo We have to prevent this if we're busy! + */ +static void __exit VBoxNetFltLinuxUnload(void) +{ + int rc; + Log(("VBoxNetFltLinuxUnload\n")); + Assert(vboxNetFltCanUnload(&g_VBoxNetFltGlobals)); + + /* + * Undo the work done during start (in reverse order). + */ + rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltGlobals); + AssertRC(rc); NOREF(rc); + + RTR0Term(); + + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + + Log(("VBoxNetFltLinuxUnload - done\n")); +} + + +/** + * Reads and retains the host interface handle. + * + * @returns The handle, NULL if detached. + * @param pThis + */ +DECLINLINE(struct net_device *) vboxNetFltLinuxRetainNetDev(PVBOXNETFLTINS pThis) +{ +#if 0 + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + struct net_device *pDev = NULL; + + Log(("vboxNetFltLinuxRetainNetDev\n")); + /* + * Be careful here to avoid problems racing the detached callback. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)) + { + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + if (pDev) + { + dev_hold(pDev); + Log(("vboxNetFltLinuxRetainNetDev: Device %p(%s) retained. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + } + } + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + Log(("vboxNetFltLinuxRetainNetDev - done\n")); + return pDev; +#else + return (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); +#endif +} + + +/** + * Release the host interface handle previously retained + * by vboxNetFltLinuxRetainNetDev. + * + * @param pThis The instance. + * @param pDev The vboxNetFltLinuxRetainNetDev + * return value, NULL is fine. + */ +DECLINLINE(void) vboxNetFltLinuxReleaseNetDev(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ +#if 0 + Log(("vboxNetFltLinuxReleaseNetDev\n")); + NOREF(pThis); + if (pDev) + { + dev_put(pDev); + Log(("vboxNetFltLinuxReleaseNetDev: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + } + Log(("vboxNetFltLinuxReleaseNetDev - done\n")); +#endif +} + +#define VBOXNETFLT_CB_TAG 0xA1C9D7C3 +#define VBOXNETFLT_SKB_TAG(skb) (*(uint32_t*)&((skb)->cb[sizeof((skb)->cb)-sizeof(uint32_t)])) + +/** + * Checks whether this is an mbuf created by vboxNetFltLinuxMBufFromSG, + * i.e. a buffer which we're pushing and should be ignored by the filter callbacks. + * + * @returns true / false accordingly. + * @param pBuf The sk_buff. + */ +DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf) +{ + return VBOXNETFLT_SKB_TAG(pBuf) == VBOXNETFLT_CB_TAG ; +} + + +/** + * Internal worker that create a linux sk_buff for a + * (scatter/)gather list. + * + * @returns Pointer to the sk_buff. + * @param pThis The instance. + * @param pSG The (scatter/)gather list. + */ +static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, bool fDstWire) +{ + struct sk_buff *pPkt; + struct net_device *pDev; + /* + * Because we're lazy, we will ASSUME that all SGs coming from INTNET + * will only contain one single segment. + */ + if (pSG->cSegsUsed != 1 || pSG->cbTotal != pSG->aSegs[0].cb) + { + LogRel(("VBoxNetFlt: Dropped multi-segment(%d) packet coming from internal network.\n", pSG->cSegsUsed)); + return NULL; + } + if (pSG->cbTotal == 0) + { + LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n")); + return NULL; + } + + /* + * Allocate a packet and copy over the data. + * + */ + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + pPkt = dev_alloc_skb(pSG->cbTotal + NET_IP_ALIGN); + if (pPkt) + { + pPkt->dev = pDev; + /* Align IP header on 16-byte boundary: 2 + 14 (ethernet hdr size). */ + skb_reserve(pPkt, NET_IP_ALIGN); + skb_put(pPkt, pSG->cbTotal); + memcpy(pPkt->data, pSG->aSegs[0].pv, pSG->cbTotal); + /* Set protocol and packet_type fields. */ + pPkt->protocol = eth_type_trans(pPkt, pDev); + pPkt->ip_summed = CHECKSUM_NONE; + if (fDstWire) + { + VBOX_SKB_RESET_NETWORK_HDR(pPkt); + /* Restore ethernet header back. */ + skb_push(pPkt, ETH_HLEN); + VBOX_SKB_RESET_MAC_HDR(pPkt); + } + VBOXNETFLT_SKB_TAG(pPkt) = VBOXNETFLT_CB_TAG; + + return pPkt; + } + else + Log(("vboxNetFltLinuxSkBufFromSG: Failed to allocate sk_buff(%u).\n", pSG->cbTotal)); + pSG->pvUserData = NULL; + + return NULL; +} + + +/** + * Initializes a SG list from an sk_buff. + * + * @returns Number of segments. + * @param pThis The instance. + * @param pBuf The sk_buff. + * @param pSG The SG. + * @param pvFrame The frame pointer, optional. + * @param cSegs The number of segments allocated for the SG. + * This should match the number in the mbuf exactly! + * @param fSrc The source of the frame. + */ +DECLINLINE(void) vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc) +{ + int i; + NOREF(pThis); + + Assert(!skb_shinfo(pBuf)->frag_list); + pSG->pvOwnerData = NULL; + pSG->pvUserData = NULL; + pSG->pvUserData2 = NULL; + pSG->cUsers = 1; + pSG->fFlags = INTNETSG_FLAGS_TEMP; + pSG->cSegsAlloc = cSegs; + + if (fSrc & INTNETTRUNKDIR_WIRE) + { + /* + * The packet came from wire, ethernet header was removed by device driver. + * Restore it. + */ + skb_push(pBuf, ETH_HLEN); + } + pSG->cbTotal = pBuf->len; +#ifdef VBOXNETFLT_SG_SUPPORT + pSG->aSegs[0].cb = skb_headlen(pBuf); + pSG->aSegs[0].pv = pBuf->data; + pSG->aSegs[0].Phys = NIL_RTHCPHYS; + + for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++) + { + skb_frag_t *pFrag = &skb_shinfo(pBuf)->frags[i]; + pSG->aSegs[i+1].cb = pFrag->size; + pSG->aSegs[i+1].pv = kmap(pFrag->page); + printk("%p = kmap()\n", pSG->aSegs[i+1].pv); + pSG->aSegs[i+1].Phys = NIL_RTHCPHYS; + } + pSG->cSegsUsed = ++i; +#else + pSG->aSegs[0].cb = pBuf->len; + pSG->aSegs[0].pv = pBuf->data; + pSG->aSegs[0].Phys = NIL_RTHCPHYS; + pSG->cSegsUsed = i = 1; +#endif + + +#ifdef PADD_RUNT_FRAMES_FROM_HOST + /* + * Add a trailer if the frame is too small. + * + * Since we're getting to the packet before it is framed, it has not + * yet been padded. The current solution is to add a segment pointing + * to a buffer containing all zeros and pray that works for all frames... + */ + if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST)) + { + static uint8_t const s_abZero[128] = {0}; + + AssertReturnVoid(i < cSegs); + + pSG->aSegs[i].Phys = NIL_RTHCPHYS; + pSG->aSegs[i].pv = (void *)&s_abZero[0]; + pSG->aSegs[i].cb = 60 - pSG->cbTotal; + pSG->cbTotal = 60; + pSG->cSegsUsed++; + } +#endif + Log4(("vboxNetFltLinuxSkBufToSG: allocated=%d, segments=%d frags=%d next=%p frag_list=%p pkt_type=%x fSrc=%x\n", + pSG->cSegsAlloc, pSG->cSegsUsed, skb_shinfo(pBuf)->nr_frags, pBuf->next, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, fSrc)); + for (i = 0; i < pSG->cSegsUsed; i++) + Log4(("vboxNetFltLinuxSkBufToSG: #%d: cb=%d pv=%p\n", + i, pSG->aSegs[i].cb, pSG->aSegs[i].pv)); +} + +/** + * Packet handler, + * + * @returns 0 or EJUSTRETURN. + * @param pThis The instance. + * @param pMBuf The mbuf. + * @param pvFrame The start of the frame, optional. + * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value. + * @param eProtocol The protocol. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) +static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf, + struct net_device *pSkbDev, + struct packet_type *pPacketType, + struct net_device *pOrigDev) +#else +static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf, + struct net_device *pSkbDev, + struct packet_type *pPacketType) +#endif +{ + PVBOXNETFLTINS pThis; + struct net_device *pDev; + LogFlow(("vboxNetFltLinuxPacketHandler: pBuf=%p pSkbDev=%p pPacketType=%p\n", + pBuf, pSkbDev, pPacketType)); + /* + * Drop it immediately? + */ + if (!pBuf) + return 0; + + pThis = VBOX_FLT_PT_TO_INST(pPacketType); + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + if (pThis->u.s.pDev != pSkbDev) + { + Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis)); + return 0; + } + + Log4(("vboxNetFltLinuxPacketHandler: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb)); + if (vboxNetFltLinuxSkBufIsOur(pBuf)) + { + Log2(("vboxNetFltLinuxPacketHandler: got our own sk_buff, drop it.\n")); + dev_kfree_skb(pBuf); + return 0; + } + +#ifndef VBOXNETFLT_SG_SUPPORT + { + /* + * Get rid of fragmented packets, they cause too much trouble. + */ + struct sk_buff *pCopy = skb_copy(pBuf, GFP_ATOMIC); + kfree_skb(pBuf); + if (!pCopy) + { + LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n")); + return 0; + } + pBuf = pCopy; + } +#endif + + /* Add the packet to transmit queue and schedule the bottom half. */ + skb_queue_tail(&pThis->u.s.XmitQueue, pBuf); + schedule_work(&pThis->u.s.XmitTask); + Log4(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n", + &pThis->u.s.XmitTask, pBuf)); + /* It does not really matter what we return, it is ignored by the kernel. */ + return 0; +} + +static unsigned vboxNetFltLinuxSGSegments(PVBOXNETFLTINS pThis, struct sk_buff *pBuf) +{ +#ifdef VBOXNETFLT_SG_SUPPORT + unsigned cSegs = 1 + skb_shinfo(pBuf)->nr_frags; +#else + unsigned cSegs = 1; +#endif +#ifdef PADD_RUNT_FRAMES_FROM_HOST + /* + * Add a trailer if the frame is too small. + */ + if (pBuf->len < 60) + cSegs++; +#endif + return cSegs; +} + +/* WARNING! This function should only be called after vboxNetFltLinuxSkBufToSG()! */ +static void vboxNetFltLinuxFreeSkBuff(struct sk_buff *pBuf, PINTNETSG pSG) +{ +#ifdef VBOXNETFLT_SG_SUPPORT + int i; + + for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++) + { + printk("kunmap(%p)\n", pSG->aSegs[i+1].pv); + kunmap(pSG->aSegs[i+1].pv); + } +#endif + + dev_kfree_skb(pBuf); +} + +#ifndef LOG_ENABLED +#define vboxNetFltDumpPacket(a, b, c, d) +#else +static void vboxNetFltDumpPacket(PINTNETSG pSG, bool fEgress, const char *pszWhere, int iIncrement) +{ + uint8_t *pInt, *pExt; + static int iPacketNo = 1; + iPacketNo += iIncrement; + if (fEgress) + { + pExt = pSG->aSegs[0].pv; + pInt = pExt + 6; + } + else + { + pInt = pSG->aSegs[0].pv; + pExt = pInt + 6; + } + Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x" + " %s (%s)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes) packet #%u\n", + pInt[0], pInt[1], pInt[2], pInt[3], pInt[4], pInt[5], + fEgress ? "-->" : "<--", pszWhere, + pExt[0], pExt[1], pExt[2], pExt[3], pExt[4], pExt[5], + pSG->cbTotal, iPacketNo)); + Log3(("%.*Rhxd\n", pSG->aSegs[0].cb, pSG->aSegs[0].pv)); +} +#endif + +static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc) +{ + unsigned cSegs = vboxNetFltLinuxSGSegments(pThis, pBuf); + if (cSegs < MAX_SKB_FRAGS) + { + uint8_t *pTmp; + PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs])); + if (!pSG) + { + Log(("VBoxNetFlt: Failed to allocate SG buffer.\n")); + return VERR_NO_MEMORY; + } + vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc); + + pTmp = pSG->aSegs[0].pv; + vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1); + pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc); + Log4(("VBoxNetFlt: Dropping the sk_buff.\n")); + vboxNetFltLinuxFreeSkBuff(pBuf, pSG); + } + + return VINF_SUCCESS; +} + +static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf) +{ + uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE; + + if (VBOX_SKB_IS_GSO(pBuf)) + { + /* Need to segment the packet */ + struct sk_buff *pNext, *pSegment; + //Log2(("vboxNetFltLinuxForwardToIntNet: cb=%u gso_size=%u gso_segs=%u gso_type=%u\n", + // pBuf->len, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type)); + + for (pSegment = VBOX_SKB_GSO_SEGMENT(pBuf); pSegment; pSegment = pNext) + { + pNext = pSegment->next; + pSegment->next = 0; + vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc); + } + dev_kfree_skb(pBuf); + } + else + { + if (pBuf->ip_summed == CHECKSUM_PARTIAL) + if (VBOX_SKB_CHECKSUM_HELP(pBuf)) + { + LogRel(("VBoxNetFlt: Failed to compute checksum, dropping the packet.\n")); + dev_kfree_skb(pBuf); + return; + } + vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc); + } + /* + * Create a (scatter/)gather list for the sk_buff and feed it to the internal network. + */ +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +static void vboxNetFltLinuxXmitTask(struct work_struct *pWork) +#else +static void vboxNetFltLinuxXmitTask(void *pWork) +#endif +{ + struct sk_buff *pBuf; + bool fActive; + PVBOXNETFLTINS pThis; + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + + Log4(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork)); + pThis = VBOX_FLT_XT_TO_INST(pWork); + /* + * Active? Retain the instance and increment the busy counter. + */ + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + fActive = ASMAtomicUoReadBool(&pThis->fActive); + if (fActive) + vboxNetFltRetain(pThis, true /* fBusy */); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + if (!fActive) + return; + + while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != 0) + vboxNetFltLinuxForwardToIntNet(pThis, pBuf); + + vboxNetFltRelease(pThis, true /* fBusy */); +} + +/** + * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered. + * + * @returns VBox status code. + * @param pThis The instance. + * @param fRediscovery If set we're doing a rediscovery attempt, so, don't + * flood the release log. + */ +static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ + struct packet_type *pt; + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + + LogFlow(("vboxNetFltLinuxAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName)); + + if (!pDev) + { + Log(("VBoxNetFlt: failed to find device '%s'\n", pThis->szName)); + return VERR_INTNET_FLT_IF_NOT_FOUND; + } + + dev_hold(pDev); + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, pDev); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) retained. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + Log(("vboxNetFltLinuxAttachToInterface: Got pDev=%p pThis=%p pThis->u.s.pDev=%p\n", pDev, pThis, ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev))); + /* + * Get the mac address while we still have a valid ifnet reference. + */ + memcpy(&pThis->u.s.Mac, pDev->dev_addr, sizeof(pThis->u.s.Mac)); + + pt = &pThis->u.s.PacketType; + pt->type = __constant_htons(ETH_P_ALL); + pt->dev = pDev; + pt->func = vboxNetFltLinuxPacketHandler; + dev_add_pack(pt); + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + if (pDev) + { + ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false); + ASMAtomicUoWriteBool(&pThis->u.s.fRegistered, true); + pDev = NULL; /* don't dereference it */ + } + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + Log(("vboxNetFltLinuxAttachToInterface: this=%p: Packet handler installed.\n", pThis)); + + /* Release the interface on failure. */ + if (pDev) + { + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, NULL); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + dev_put(pDev); + Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + } + + LogRel(("VBoxNetFlt: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.Mac), &pThis->u.s.Mac)); + return VINF_SUCCESS; +} + + +static int vboxNetFltLinuxUnregisterDevice(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + + Assert(!pThis->fDisconnectedFromHost); + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + ASMAtomicWriteBool(&pThis->u.s.fRegistered, false); + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true); + ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, NULL); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + dev_remove_pack(&pThis->u.s.PacketType); + skb_queue_purge(&pThis->u.s.XmitQueue); + Log(("vboxNetFltLinuxUnregisterDevice: this=%p: Packet handler removed, xmit queue purged.\n", pThis)); + Log(("vboxNetFltLinuxUnregisterDevice: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + dev_put(pDev); + + return NOTIFY_OK; +} + +static int vboxNetFltLinuxDeviceIsUp(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ + /* Check if we are not suspended and promiscuous mode has not been set. */ + if (ASMAtomicUoReadBool(&pThis->fActive) && !ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet)) + { + /* Note that there is no need for locking as the kernel got hold of the lock already. */ + dev_set_promiscuity(pDev, 1); + ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true); + Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); + } + else + Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); + return NOTIFY_OK; +} + +static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ + /* Undo promiscuous mode if we has set it. */ + if (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet)) + { + /* Note that there is no need for locking as the kernel got hold of the lock already. */ + dev_set_promiscuity(pDev, -1); + ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false); + Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); + } + else + Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); + return NOTIFY_OK; +} + +static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr) + +{ + int rc = NOTIFY_OK; +#ifdef DEBUG + char *pszEvent = "<unknown>"; +#endif + struct net_device *pDev = (struct net_device *)ptr; + PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self); + +#ifdef DEBUG + switch (ulEventType) + { + case NETDEV_REGISTER: pszEvent = "NETDEV_REGISTER"; break; + case NETDEV_UNREGISTER: pszEvent = "NETDEV_UNREGISTER"; break; + case NETDEV_UP: pszEvent = "NETDEV_UP"; break; + case NETDEV_DOWN: pszEvent = "NETDEV_DOWN"; break; + case NETDEV_REBOOT: pszEvent = "NETDEV_REBOOT"; break; + case NETDEV_CHANGENAME: pszEvent = "NETDEV_CHANGENAME"; break; + case NETDEV_CHANGE: pszEvent = "NETDEV_CHANGE"; break; + case NETDEV_CHANGEMTU: pszEvent = "NETDEV_CHANGEMTU"; break; + case NETDEV_CHANGEADDR: pszEvent = "NETDEV_CHANGEADDR"; break; + case NETDEV_GOING_DOWN: pszEvent = "NETDEV_GOING_DOWN"; break; + } + Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n", + pszEvent, ulEventType, pDev->name, pDev, pThis, ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev))); +#endif + if (ulEventType == NETDEV_REGISTER && !strcmp(pDev->name, pThis->szName)) + { + vboxNetFltLinuxAttachToInterface(pThis, pDev); + } + else + { + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + if (pDev != ptr) + return NOTIFY_OK; + rc = NOTIFY_OK; + switch (ulEventType) + { + case NETDEV_UNREGISTER: + rc = vboxNetFltLinuxUnregisterDevice(pThis, pDev); + break; + case NETDEV_UP: + rc = vboxNetFltLinuxDeviceIsUp(pThis, pDev); + break; + case NETDEV_GOING_DOWN: + rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev); + break; + case NETDEV_CHANGENAME: + break; + } + } + + return rc; +} + +bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); +} + +int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst) +{ + struct net_device * pDev; + int err; + int rc = VINF_SUCCESS; + + LogFlow(("vboxNetFltPortOsXmit: pThis=%p (%s)\n", pThis, pThis->szName)); + + pDev = vboxNetFltLinuxRetainNetDev(pThis); + if (pDev) + { + /* + * Create a sk_buff for the gather list and push it onto the wire. + */ + if (fDst & INTNETTRUNKDIR_WIRE) + { + struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, true); + if (pBuf) + { + vboxNetFltDumpPacket(pSG, true, "wire", 1); + Log4(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb)); + Log4(("vboxNetFltPortOsXmit: dev_queue_xmit(%p)\n", pBuf)); + err = dev_queue_xmit(pBuf); + if (err) + rc = RTErrConvertFromErrno(err); + } + else + rc = VERR_NO_MEMORY; + } + + /* + * Create a sk_buff for the gather list and push it onto the host stack. + */ + if (fDst & INTNETTRUNKDIR_HOST) + { + struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, false); + if (pBuf) + { + vboxNetFltDumpPacket(pSG, true, "host", (fDst & INTNETTRUNKDIR_WIRE) ? 0 : 1); + Log4(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb)); + Log4(("vboxNetFltPortOsXmit: netif_rx_ni(%p)\n", pBuf)); + err = netif_rx_ni(pBuf); + if (err) + rc = RTErrConvertFromErrno(err); + } + else + rc = VERR_NO_MEMORY; + } + + vboxNetFltLinuxReleaseNetDev(pThis, pDev); + } + + return rc; +} + + +bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis) +{ + bool fRc = false; + struct net_device * pDev = vboxNetFltLinuxRetainNetDev(pThis); + if (pDev) + { + fRc = !!(pDev->promiscuity - (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet) & 1)); + LogFlow(("vboxNetFltPortOsIsPromiscuous: returns %d, pDev->promiscuity=%d, fPromiscuousSet=%d\n", + fRc, pDev->promiscuity, pThis->u.s.fPromiscuousSet)); + vboxNetFltLinuxReleaseNetDev(pThis, pDev); + } + return fRc; +} + + +void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac) +{ + *pMac = pThis->u.s.Mac; +} + + +bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac) +{ + /* ASSUMES that the MAC address never changes. */ + return pThis->u.s.Mac.au16[0] == pMac->au16[0] + && pThis->u.s.Mac.au16[1] == pMac->au16[1] + && pThis->u.s.Mac.au16[2] == pMac->au16[2]; +} + + +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive) +{ + struct net_device * pDev; + + LogFlow(("vboxNetFltPortOsSetActive: pThis=%p (%s), fActive=%s\n", + pThis, pThis->szName, fActive?"true":"false")); + + pDev = vboxNetFltLinuxRetainNetDev(pThis); + if (pDev) + { + /* + * This api is a bit weird, the best reference is the code. + * + * Also, we have a bit or race conditions wrt the maintance of + * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous. + */ + u_int16_t fIf; +#ifdef LOG_ENABLED + unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pDev); +#endif + if (fActive) + { + Assert(!pThis->u.s.fPromiscuousSet); + +#if 0 + /* + * Try bring the interface up and running if it's down. + */ + fIf = dev_get_flags(pDev); + if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) + { + rtnl_lock(); + int err = dev_change_flags(pDev, fIf | IFF_UP); + rtnl_unlock(); + fIf = dev_get_flags(pDev); + } + + /* + * Is it already up? If it isn't, leave it to the link event or + * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird). + */ + if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING) + && !ASMAtomicReadBool(&pThis->u.s.fPromiscuousSet)) + { +#endif + rtnl_lock(); + dev_set_promiscuity(pDev, 1); + rtnl_unlock(); + pThis->u.s.fPromiscuousSet = true; + Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); +#if 0 + /* check if it actually worked, this stuff is not always behaving well. */ + if (!(dev_get_flags(pDev) & IFF_PROMISC)) + { + err = dev_change_flags(pDev, fIf | IFF_PROMISC); + if (!err) + Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pDev))); + else + Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n", + pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pDev))); + } +#endif +#if 0 + } + else if (!err) + Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pDev))); + if (err) + LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pDev))); +#endif + } + else + { + if (pThis->u.s.fPromiscuousSet) + { + rtnl_lock(); + dev_set_promiscuity(pDev, -1); + rtnl_unlock(); + Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev))); + } + pThis->u.s.fPromiscuousSet = false; + + fIf = dev_get_flags(pDev); + Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pDev))); + } + + vboxNetFltLinuxReleaseNetDev(pThis, pDev); + } +} + + +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis) +{ + /* Nothing to do here. */ + return VINF_SUCCESS; +} + + +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis) +{ + /* Nothing to do here. */ + return VINF_SUCCESS; +} + + +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis) +{ + struct net_device *pDev; + bool fRegistered; + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev); + fRegistered = ASMAtomicUoReadBool(&pThis->u.s.fRegistered); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + if (fRegistered) + { + dev_remove_pack(&pThis->u.s.PacketType); + skb_queue_purge(&pThis->u.s.XmitQueue); + Log(("vboxNetFltOsDeleteInstance: this=%p: Packet handler removed, xmit queue purged.\n", pThis)); + Log(("vboxNetFltOsDeleteInstance: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt))); + dev_put(pDev); + } + Log(("vboxNetFltOsDeleteInstance: this=%p: Notifier removed.\n", pThis)); + unregister_netdevice_notifier(&pThis->u.s.Notifier); +} + + +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis) +{ + int err; + pThis->u.s.Notifier.notifier_call = vboxNetFltLinuxNotifierCallback; + err = register_netdevice_notifier(&pThis->u.s.Notifier); + if (err) + return VERR_INTNET_FLT_IF_FAILED; + if (!pThis->u.s.fRegistered) + { + unregister_netdevice_notifier(&pThis->u.s.Notifier); + LogRel(("VBoxNetFlt: failed to find %s.\n", pThis->szName)); + return VERR_INTNET_FLT_IF_NOT_FOUND; + } + Log(("vboxNetFltOsInitInstance: this=%p: Notifier installed.\n", pThis)); + return pThis->fDisconnectedFromHost ? VERR_INTNET_FLT_IF_FAILED : VINF_SUCCESS; +} + +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis) +{ + /* + * Init the linux specific members. + */ + pThis->u.s.pDev = NULL; + pThis->u.s.fRegistered = false; + pThis->u.s.fPromiscuousSet = false; + memset(&pThis->u.s.PacketType, 0, sizeof(pThis->u.s.PacketType)); + skb_queue_head_init(&pThis->u.s.XmitQueue); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask); +#else + INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask, &pThis->u.s.XmitTask); +#endif + + return VINF_SUCCESS; +} + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/dkms.conf b/src/VBox/HostDrivers/VBoxNetFlt/linux/dkms.conf new file mode 100644 index 000000000..d20fef952 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/dkms.conf @@ -0,0 +1,7 @@ +BUILT_MODULE_NAME=vboxnetflt +DEST_MODULE_LOCATION=/kernel/misc +PACKAGE_NAME=vboxnetflt +PACKAGE_VERSION=_VERSION_ +AUTOINSTALL=yes +CLEAN="make -C $dkms_tree/$module/$module_version/build clean" +PRE_BUILD="do_Module.symvers vboxdrv restore $dkms_tree/$module/$module_version/build/Module.symvers" diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt new file mode 100644 index 000000000..95819176e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt @@ -0,0 +1,137 @@ +#!/bin/sh +# +# Shared file between Makefile.kmk and export_modules +# +# Copyright (C) 2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +## @todo +# At the moment we'll be exporting everything we need here, later +# we'll be linking against IPRT in vboxdrv like Darwin and Solaris does. + +VBOX_VBOXNETFLT_SOURCES=" \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \ + ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \ + ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \ + ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \ + ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \ + ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \ + ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \ + ${PATH_ROOT}/include/iprt/process.h=>include/iprt/process.h \ + ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \ + ${PATH_ROOT}/include/iprt/spinlock.h=>include/iprt/spinlock.h \ + ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \ + ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \ + ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \ + ${PATH_ROOT}/include/iprt/thread.h=>include/iprt/thread.h \ + ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \ + ${PATH_ROOT}/include/iprt/timer.h=>include/iprt/timer.h \ + ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \ + ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \ + ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \ + ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \ + ${PATH_ROOT}/include/VBox/stam.h=>include/VBox/stam.h \ + ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \ + ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c=>linux/VBoxNetFlt-linux.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c=>VBoxNetFlt.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h=>VBoxNetFltInternal.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c=>SUPR0IdcClient-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/alloc/heapsimple.cpp=>alloc/heapsimple.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertFromErrno.cpp=>common/err/RTErrConvertFromErrno.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertToErrno.cpp=>common/err/RTErrConvertToErrno.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/log.cpp=>common/log/log.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logellipsis.cpp=>common/log/logellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logrel.cpp=>common/log/logrel.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logrelellipsis.cpp=>common/log/logrelellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logformat.cpp=>common/log/logformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/initterm.h=>include/internal/initterm.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/magics.h=>include/internal/magics.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/memobj.h=>include/internal/memobj.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/string.h=>include/internal/string.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/thread.h=>include/internal/thread.h \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp=>generic/RTAssertShouldPanic-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp=>generic/RTLogWriteStdErr-stub-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp=>generic/RTLogWriteStdOut-stub-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp=>generic/RTLogWriteUser-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/uuid-generic.cpp=>generic/uuid-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.cpp=>r0drv/alloc-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/alloc-r0drv.h=>r0drv/alloc-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/initterm-r0drv.cpp=>r0drv/initterm-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/mp-r0drv.h=>r0drv/mp-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/mpnotification-r0drv.c=>r0drv/mpnotification-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/power-r0drv.h=>r0drv/power-r0drv.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/powernotification-r0drv.c=>r0drv/powernotification-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c=>r0drv/linux/RTLogWriteDebugger-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c=>r0drv/linux/assert-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c=>r0drv/linux/alloc-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c=>r0drv/linux/initterm-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c=>r0drv/linux/memobj-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c=>r0drv/linux/mp-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c=>r0drv/linux/mpnotification-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c=>r0drv/linux/process-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c=>r0drv/linux/semevent-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c=>r0drv/linux/semeventmulti-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c=>r0drv/linux/semfastmutex-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c=>r0drv/linux/spinlock-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/string.h=>r0drv/linux/string.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c=>r0drv/linux/thread-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c=>r0drv/linux/thread2-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c=>r0drv/linux/time-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/memobj-r0drv.cpp=>r0drv/memobj-r0drv.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/log-vbox.cpp=>VBox/log-vbox.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/strformat-vbox.cpp=>VBox/strformat-vbox.c \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ +" + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c new file mode 100644 index 000000000..f69fa8f60 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c @@ -0,0 +1,3319 @@ +/* $Id: VBoxNetFlt-solaris.c 16082 2009-01-20 12:47:18Z vboxsync $ */ +/** @file + * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code. + */ + +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#if defined(DEBUG_ramshankar) && !defined(LOG_ENABLED) +# define LOG_ENABLED +#endif + +#define LOG_GROUP LOG_GROUP_NET_FLT_DRV +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/cdefs.h> +#include <VBox/version.h> +#include <iprt/string.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/alloca.h> +#include <iprt/net.h> +#include <iprt/mem.h> +#include <iprt/thread.h> +#include <iprt/spinlock.h> +#include <iprt/crc32.h> + +#include <inet/ip.h> +#include <net/if.h> +#include <sys/socket.h> +#include <sys/kstr.h> +#include <sys/file.h> +#include <sys/sockio.h> +#include <sys/strsubr.h> +#include <sys/pathname.h> +#include <sys/t_kuser.h> + +#include <sys/types.h> +#include <sys/dlpi.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ethernet.h> +#include <sys/stat.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/strsun.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunldi.h> + +/* + * Experimental: Using netinfo interfaces and queuing out packets. + * This is for playing better with IPFilter. + */ +#undef VBOXNETFLT_SOLARIS_USE_NETINFO +#ifdef VBOXNETFLT_SOLARIS_USE_NETINFO +# include <sys/neti.h> +#endif + +// Workaround for very strange define in sys/user.h +// #define u (curproc->p_user) /* user is now part of proc structure */ +#ifdef u +#undef u +#endif + +#define VBOXNETFLT_OS_SPECFIC 1 +#include "../VBoxNetFltInternal.h" + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +#define VBOXSOLQUOTE2(x) #x +#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x) +/** The module name. */ +#define DEVICE_NAME "vboxflt" +/** The module descriptions as seen in 'modinfo'. */ +#define DEVICE_DESC_DRV "VirtualBox NetDrv" +#define DEVICE_DESC_MOD "VirtualBox NetMod" + +/** @todo Remove the below hackery once done! */ +#if defined(DEBUG_ramshankar) && defined(LOG_ENABLED) +# undef Log +# define Log LogRel +# undef LogFlow +# define LogFlow LogRel +#endif + +/** Maximum loopback packet queue size per interface */ +#define VBOXNETFLT_LOOPBACK_SIZE 32 + +/******************************************************************************* +* Global Functions * +*******************************************************************************/ +/** + * Stream Driver hooks. + */ +static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult); +static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd); +static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd); + +/** + * Stream Module hooks. + */ +static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred); +static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fFile, cred_t *pCred); +static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg); +static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg); + +/** + * OS specific hooks invoked from common VBoxNetFlt ring-0. + */ +bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis); +void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac); +bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac); +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive); +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis); +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis); +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis); +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis); +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis); + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Streams: module info. + */ +static struct module_info g_VBoxNetFltSolarisModInfo = +{ + 0xbad, /* module id */ + DEVICE_NAME, + 0, /* min. packet size */ + INFPSZ, /* max. packet size */ + 0, /* hi-water mask */ + 0 /* lo-water mask */ +}; + +/** + * Streams: read queue hooks. + */ +static struct qinit g_VBoxNetFltSolarisReadQ = +{ + VBoxNetFltSolarisModReadPut, + NULL, /* service */ + VBoxNetFltSolarisModOpen, + VBoxNetFltSolarisModClose, + NULL, /* admin (reserved) */ + &g_VBoxNetFltSolarisModInfo, + NULL /* module stats */ +}; + +/** + * Streams: write queue hooks. + */ +static struct qinit g_VBoxNetFltSolarisWriteQ = +{ + VBoxNetFltSolarisModWritePut, + NULL, /* service */ + NULL, /* open */ + NULL, /* close */ + NULL, /* admin (reserved) */ + &g_VBoxNetFltSolarisModInfo, + NULL /* module stats */ +}; + +/** + * Streams: IO stream tab. + */ +static struct streamtab g_VBoxNetFltSolarisStreamTab = +{ + &g_VBoxNetFltSolarisReadQ, + &g_VBoxNetFltSolarisWriteQ, + NULL, /* muxread init */ + NULL /* muxwrite init */ +}; + +/** + * cb_ops: driver char/block entry points + */ +static struct cb_ops g_VBoxNetFltSolarisCbOps = +{ + nulldev, /* cb open */ + nulldev, /* cb close */ + nodev, /* b strategy */ + nodev, /* b dump */ + nodev, /* b print */ + nodev, /* cb read */ + nodev, /* cb write */ + nodev, /* cb ioctl */ + nodev, /* c devmap */ + nodev, /* c mmap */ + nodev, /* c segmap */ + nochpoll, /* c poll */ + ddi_prop_op, /* property ops */ + &g_VBoxNetFltSolarisStreamTab, + D_NEW | D_MP | D_MTQPAIR, /* compat. flag */ + CB_REV /* revision */ +}; + +/** + * dev_ops: driver entry/exit and other ops. + */ +static struct dev_ops g_VBoxNetFltSolarisDevOps = +{ + DEVO_REV, /* driver build revision */ + 0, /* ref count */ + VBoxNetFltSolarisGetInfo, + nulldev, /* identify */ + nulldev, /* probe */ + VBoxNetFltSolarisAttach, + VBoxNetFltSolarisDetach, + nodev, /* reset */ + &g_VBoxNetFltSolarisCbOps, + (struct bus_ops *)0, + nodev /* power */ +}; + +/** + * modldrv: export driver specifics to kernel + */ +static struct modldrv g_VBoxNetFltSolarisDriver = +{ + &mod_driverops, /* extern from kernel */ + DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV), + &g_VBoxNetFltSolarisDevOps +}; + +/** + * fmodsw: streams module ops + */ +static struct fmodsw g_VBoxNetFltSolarisModOps = +{ + DEVICE_NAME, + &g_VBoxNetFltSolarisStreamTab, + D_NEW | D_MP | D_MTQPAIR +}; + +/** + * modlstrmod: streams module specifics to kernel + */ +static struct modlstrmod g_VBoxNetFltSolarisModule = +{ + &mod_strmodops, /* extern from kernel */ + DEVICE_DESC_MOD " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV), + &g_VBoxNetFltSolarisModOps +}; + +/** + * modlinkage: export install/remove/info to the kernel + */ +static struct modlinkage g_VBoxNetFltSolarisModLinkage = +{ + MODREV_1, /* loadable module system revision */ + &g_VBoxNetFltSolarisDriver, /* streams driver framework */ + &g_VBoxNetFltSolarisModule, /* streams module framework */ + NULL /* terminate array of linkage structures */ +}; + +struct vboxnetflt_state_t; + +/** + * vboxnetflt_dladdr_t: DL SAP address format + */ +typedef struct vboxnetflt_dladdr_t +{ + ether_addr_t Mac; + uint16_t SAP; +} vboxnetflt_dladdr_t; + +#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t) + +/** + * which stream is this? + */ +typedef enum VBOXNETFLTSTREAMTYPE +{ + kUndefined = 0, + kIp4Stream = 0x1b, + kIp6Stream = 0xcc, + kArpStream = 0xab, + kPromiscStream = 0xdf +} VBOXNETFLTSTREAMTYPE; + +/** + * loopback packet identifier + */ +typedef struct VBOXNETFLTPACKETID +{ + struct VBOXNETFLTPACKETID *pNext; + uint16_t cbPacket; + uint16_t Checksum; + RTMAC SrcMac; + RTMAC DstMac; +} VBOXNETFLTPACKETID; +typedef struct VBOXNETFLTPACKETID *PVBOXNETFLTPACKETID; + +/** + * vboxnetflt_stream_t: per-stream data (multiple streams per interface) + */ +typedef struct vboxnetflt_stream_t +{ + int DevMinor; /* minor device no. (for clone) */ + queue_t *pReadQueue; /* read side queue */ + struct vboxnetflt_stream_t *pNext; /* next stream in list */ + PVBOXNETFLTINS volatile pThis; /* the backend instance */ + VBOXNETFLTSTREAMTYPE Type; /* the type of the stream */ +} vboxnetflt_stream_t; + +/** + * vboxnetflt_promisc_stream_t: per-interface dedicated stream data + */ +typedef struct vboxnetflt_promisc_stream_t +{ + vboxnetflt_stream_t Stream; /* The generic stream */ + bool fPromisc; /* cached promiscous value */ + bool fRawMode; /* whether raw mode request was successful */ + uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */ + size_t cLoopback; /* loopback queue size list */ + PVBOXNETFLTPACKETID pHead; /* loopback packet identifier head */ + PVBOXNETFLTPACKETID pTail; /* loopback packet identifier tail */ +} vboxnetflt_promisc_stream_t; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream); +/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */ + +static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue); +static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg); +static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP); +static int vboxNetFltSolarisNotifyReq(queue_t *pQueue); + +static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg); +static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg); + +static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg); +static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg); +static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg); + +static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst); +static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg); +static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc); +static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg); +static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg); +static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Global device info handle. */ +static dev_info_t *g_pVBoxNetFltSolarisDip = NULL; + +/** The (common) global data. */ +static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals; + +/** The list of all opened streams. */ +vboxnetflt_stream_t *g_VBoxNetFltSolarisStreams; + +/** Global mutex protecting open/close. */ +static RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX; + +/** + * g_VBoxNetFltInstance is the current PVBOXNETFLTINS to be associated with the stream being created + * in ModOpen. This is just shared global data between the dynamic attach and the ModOpen procedure. + */ +PVBOXNETFLTINS volatile g_VBoxNetFltSolarisInstance; + +/** Goes along with the instance to determine type of stream being opened/created. */ +VBOXNETFLTSTREAMTYPE volatile g_VBoxNetFltSolarisStreamType; + +/** GCC C++ hack. */ +unsigned __gxx_personality_v0 = 0xdecea5ed; + + +/** + * Kernel entry points + */ +int _init(void) +{ + LogFlow((DEVICE_NAME ":_init\n")); + + /* + * Prevent module autounloading. + */ + modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage); + if (pModCtl) + pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD; + else + LogRel((DEVICE_NAME ":failed to disable autounloading!\n")); + + /* + * Initialize IPRT. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize Solaris specific globals here. + */ + g_VBoxNetFltSolarisStreams = NULL; + g_VBoxNetFltSolarisInstance = NULL; + + int rc = RTSemFastMutexCreate(&g_VBoxNetFltSolarisMtx); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the globals and connect to the support driver. + * + * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv) + * for establishing the connect to the support driver. + */ + memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals)); + rc = vboxNetFltInitGlobals(&g_VBoxNetFltSolarisGlobals); + if (RT_SUCCESS(rc)) + { + rc = mod_install(&g_VBoxNetFltSolarisModLinkage); + if (!rc) + return rc; + + LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc)); + vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals); + } + else + LogRel((DEVICE_NAME ":failed to initialize globals.\n")); + + RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx); + g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX; + } + + RTR0Term(); + } + else + LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc)); + + memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals)); + return -1; +} + + +int _fini(void) +{ + int rc; + LogFlow((DEVICE_NAME ":_fini\n")); + + /* + * Undo the work done during start (in reverse order). + */ + rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals); + if (RT_FAILURE(rc)) + { + LogRel((DEVICE_NAME ":_fini - busy!\n")); + return EBUSY; + } + + if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX) + { + RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx); + g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX; + } + + RTR0Term(); + + return mod_remove(&g_VBoxNetFltSolarisModLinkage); +} + + +int _info(struct modinfo *pModInfo) +{ + LogFlow((DEVICE_NAME ":_info\n")); + + int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo); + + LogFlow((DEVICE_NAME ":_info returns %d\n", rc)); + return rc; +} + + +/** + * Attach entry point, to attach a device to the system or resume it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (attach/resume). + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd) +{ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd)); + + switch (enmCmd) + { + case DDI_ATTACH: + { + int instance = ddi_get_instance(pDip); + int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, CLONE_DEV); + if (rc == DDI_SUCCESS) + { + g_pVBoxNetFltSolarisDip = pDip; + ddi_report_dev(pDip); + return DDI_SUCCESS; + } + else + LogRel((DEVICE_NAME ":VBoxNetFltSolarisAttach failed to create minor node. rc%d\n", rc)); + return DDI_FAILURE; + } + + case DDI_RESUME: + { + /* Nothing to do here... */ + return DDI_SUCCESS; + } + } + return DDI_FAILURE; +} + + +/** + * Detach entry point, to detach a device to the system or suspend it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (detach/suspend). + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd) +{ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd)); + + switch (enmCmd) + { + case DDI_DETACH: + { + int instance = ddi_get_instance(pDip); + ddi_remove_minor_node(pDip, NULL); + return DDI_SUCCESS; + } + + case DDI_RESUME: + { + /* Nothing to do here... */ + return DDI_SUCCESS; + } + } + return DDI_FAILURE; +} + + +/** + * Info entry point, called by solaris kernel for obtaining driver info. + * + * @param pDip The module structure instance (do not use). + * @param enmCmd Information request type. + * @param pvArg Type specific argument. + * @param ppvResult Where to store the requested info. + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppResult) +{ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd, + getminor((dev_t)pvArg))); + + switch (enmCmd) + { + case DDI_INFO_DEVT2DEVINFO: + { + *ppResult = g_pVBoxNetFltSolarisDip; + return DDI_SUCCESS; + } + + case DDI_INFO_DEVT2INSTANCE: + { + int instance = getminor((dev_t)pvArg); + *ppResult = (void *)(uintptr_t)instance; + return DDI_SUCCESS; + } + } + + return DDI_FAILURE; +} + + +/** + * Stream module open entry point, initializes the queue and allows streams processing. + * + * @param pQueue Pointer to the read queue (cannot be NULL). + * @param pDev Pointer to the dev_t associated with the driver at the end of the stream. + * @param fOpenMode Open mode (always 0 for streams driver, thus ignored). + * @param fStreamMode Stream open mode. + * @param pCred Pointer to user credentials. + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fOpenMode, int fStreamMode, cred_t *pCred) +{ + Assert(pQueue); + + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev, + fOpenMode, fStreamMode)); + + int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx); + AssertRCReturn(rc, rc); + + /* + * Already open? + */ + if (pQueue->q_ptr) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid open.\n")); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENOENT; + } + + /* + * Check for the VirtualBox instance. + */ + PVBOXNETFLTINS pThis = g_VBoxNetFltSolarisInstance; + if (!pThis) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n")); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENOENT; + } + + /* + * Check VirtualBox stream type. + */ + if (g_VBoxNetFltSolarisStreamType == kUndefined) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode.\n")); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENOENT; + } + + /* + * Get minor number. For clone opens provide a new dev_t. + */ + minor_t DevMinor = 0; + vboxnetflt_stream_t *pStream = NULL; + vboxnetflt_stream_t **ppPrevStream = &g_VBoxNetFltSolarisStreams; + if (fStreamMode == CLONEOPEN) + { + for (; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext) + { + if (DevMinor < pStream->DevMinor) + break; + DevMinor++; + } + *pDev = makedevice(getmajor(*pDev), DevMinor); + } + else + DevMinor = getminor(*pDev); + + if (g_VBoxNetFltSolarisStreamType == kPromiscStream) + { + vboxnetflt_promisc_stream_t *pPromiscStream = RTMemAlloc(sizeof(vboxnetflt_promisc_stream_t)); + if (RT_UNLIKELY(!pPromiscStream)) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate promiscuous stream data.\n")); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENOMEM; + } + + pPromiscStream->fPromisc = false; + pPromiscStream->fRawMode = false; + pPromiscStream->ModeReqId = 0; + pPromiscStream->pHead = NULL; + pPromiscStream->pTail = NULL; + pPromiscStream->cLoopback = 0; + pStream = (vboxnetflt_stream_t *)pPromiscStream; + } + else + { + /* + * Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data. + */ + pStream = RTMemAlloc(sizeof(vboxnetflt_stream_t)); + if (RT_UNLIKELY(!pStream)) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n")); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENOMEM; + } + } + pStream->DevMinor = DevMinor; + pStream->pReadQueue = pQueue; + + /* + * Pick up the current global VBOXNETFLTINS instance as + * the one that we will associate this stream with. + */ + ASMAtomicUoWritePtr((void * volatile *)&pStream->pThis, pThis); + pStream->Type = g_VBoxNetFltSolarisStreamType; + switch (pStream->Type) + { + case kIp4Stream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvIp4Stream, pStream); break; + case kIp6Stream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvIp6Stream, pStream); break; + case kArpStream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvArpStream, pStream); break; + case kPromiscStream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvPromiscStream, pStream); break; + default: /* Heh. */ + { + AssertRelease(pStream->Type); + break; + } + } + + pQueue->q_ptr = pStream; + WR(pQueue)->q_ptr = pStream; + + /* + * Link it to the list of streams. + */ + pStream->pNext = *ppPrevStream; + *ppPrevStream = pStream; + + /* + * Release global lock, & do not hold locks across putnext calls. + */ + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + + qprocson(pQueue); + + /* + * Don't hold the spinlocks across putnext calls as it could + * (and does mostly) re-enter the put procedure on the same thread. + */ + if (pStream->Type == kPromiscStream) + { + vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream; + + /* + * Bind to SAP 0 (DL_ETHER). + * Note: We don't support DL_TPR (token passing ring) SAP as that is unnecessary asynchronous + * work to get DL_INFO_REQ acknowledgements and determine SAP based on the Mac Type etc. + * Besides TPR doesn't really exist anymore practically as far as I know. + */ + int rc = vboxNetFltSolarisBindReq(pStream->pReadQueue, 0 /* SAP */); + if (RT_LIKELY(RT_SUCCESS(rc))) + { + /* + * Request the physical address (we cache the acknowledgement). + */ + rc = vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue); + if (RT_LIKELY(RT_SUCCESS(rc))) + { + /* + * Ask for DLPI link notifications, don't bother check for errors here. + */ + vboxNetFltSolarisNotifyReq(pStream->pReadQueue); + + /* + * Enable raw mode. + */ + rc = vboxNetFltSolarisSetRawMode(pPromiscStream); + if (RT_FAILURE(rc)) + LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisBindReq failed rc=%Rrc.\n", rc)); + } + + NOREF(fOpenMode); + NOREF(pCred); + + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen returns 0, DevMinor=%d pQueue=%p\n", DevMinor, pStream->pReadQueue)); + + return 0; +} + + +/** + * Stream module close entry point, undoes the work done on open and closes the stream. + * + * @param pQueue Pointer to the read queue (cannot be NULL). + * @param fOpenMode Open mode (always 0 for streams driver, thus ignored). + * @param pCred Pointer to user credentials. + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fOpenMode, cred_t *pCred) +{ + Assert(pQueue); + + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode)); + + int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx); + AssertRCReturn(rc, rc); + + vboxnetflt_stream_t *pStream = NULL; + vboxnetflt_stream_t **ppPrevStream = NULL; + + /* + * Get instance data. + */ + pStream = (vboxnetflt_stream_t *)pQueue->q_ptr; + if (RT_UNLIKELY(!pStream)) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModClose failed to get stream.\n")); + vboxNetFltRelease(pStream->pThis, false /* fBusy */); + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + return ENXIO; + } + + if (pStream->Type == kPromiscStream) + { + flushq(pQueue, FLUSHALL); + flushq(WR(pQueue), FLUSHALL); + } + + qprocsoff(pQueue); + + if (pStream->Type == kPromiscStream) + { + vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream; + + int rc = RTSemFastMutexRequest(pStream->pThis->u.s.hFastMtx); + AssertRCReturn(rc, rc); + + /* + * Free-up loopback buffers. + */ + PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead; + while (pCur) + { + PVBOXNETFLTPACKETID pNext = pCur->pNext; + RTMemFree(pCur); + pCur = pNext; + } + pPromiscStream->pHead = NULL; + pPromiscStream->pTail = NULL; + pPromiscStream->cLoopback = 0; + + RTSemFastMutexRelease(pStream->pThis->u.s.hFastMtx); + } + + /* + * Unlink it from the list of streams. + */ + for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext) + if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr) + break; + *ppPrevStream = pStream->pNext; + + /* + * Delete the stream. + */ + switch (pStream->Type) + { + case kIp4Stream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvIp4Stream, NULL); break; + case kIp6Stream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvIp6Stream, NULL); break; + case kArpStream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvArpStream, NULL); break; + case kPromiscStream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvPromiscStream, NULL); break; + default: /* Heh. */ + { + AssertRelease(pStream->Type); + break; + } + } + + vboxNetFltRelease(pStream->pThis, false /* fBusy */); + RTMemFree(pStream); + pQueue->q_ptr = NULL; + WR(pQueue)->q_ptr = NULL; + + NOREF(fOpenMode); + NOREF(pCred); + + RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx); + + return 0; +} + + +/** + * Read side put procedure for processing messages in the read queue. + * All streams, bound and unbound share this read procedure. + * + * @param pQueue Pointer to the read queue. + * @param pMsg Pointer to the message. + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg) +{ + if (!pMsg) + return 0; + + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n", pQueue, pMsg)); + + bool fSendUpstream = true; + vboxnetflt_stream_t *pStream = pQueue->q_ptr; + PVBOXNETFLTINS pThis = NULL; + + /* + * In the unlikely case where VirtualBox crashed and this filter + * is somehow still in the host stream we must try not to panic the host. + */ + if ( pStream + && pStream->Type == kPromiscStream) + { + fSendUpstream = false; + pThis = ASMAtomicUoReadPtr((void * volatile *)&pStream->pThis); + if (RT_LIKELY(pThis)) + { + /* + * Retain the instance if we're filtering regardless of we are active or not + * The reason being even when we are inactive we reference the instance (e.g + * the promiscuous OFF acknowledgement case). + */ + RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; + RTSpinlockAcquire(pThis->hSpinlock, &Tmp); + const bool fActive = ASMAtomicUoReadBool(&pThis->fActive); + vboxNetFltRetain(pThis, true /* fBusy */); + RTSpinlockRelease(pThis->hSpinlock, &Tmp); + + vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream; + + switch (DB_TYPE(pMsg)) + { + case M_DATA: + { + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n")); + + if ( fActive + && pPromiscStream->fRawMode) + { + vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg); + } + break; + } + + case M_PROTO: + case M_PCPROTO: + { + union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr; + t_uscalar_t Prim = pPrim->dl_primitive; + + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim)); + switch (Prim) + { + case DL_NOTIFY_IND: + { + if (MBLKL(pMsg) < DL_NOTIFY_IND_SIZE) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d got=%d\n", + DL_NOTIFY_IND_SIZE, MBLKL(pMsg))); + break; + } + + dl_notify_ind_t *pNotifyInd = (dl_notify_ind_t *)pMsg->b_rptr; + switch (pNotifyInd->dl_notification) + { + case DL_NOTE_PHYS_ADDR: + { + if (pNotifyInd->dl_data != DL_CURR_PHYS_ADDR) + break; + + size_t cOffset = pNotifyInd->dl_addr_offset; + size_t cbAddr = pNotifyInd->dl_addr_length; + + if (!cOffset || !cbAddr) + { + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. Invalid offset/addr.\n")); + fSendUpstream = false; + break; + } + + bcopy(pMsg->b_rptr + cOffset, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac)); + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. New Mac=%.*Rhxs\n", + sizeof(pThis->u.s.Mac), &pThis->u.s.Mac)); + break; + } + + case DL_NOTE_LINK_UP: + { + const bool fDisconnected = ASMAtomicUoReadBool(&pThis->fActive); + if (fDisconnected) + { + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false); + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_UP.\n")); + } + break; + } + + case DL_NOTE_LINK_DOWN: + { + const bool fDisconnected = ASMAtomicUoReadBool(&pThis->fActive); + if (!fDisconnected) + { + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true); + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_DOWN.\n")); + } + break; + } + } + break; + } + + case DL_BIND_ACK: + { + /* + * Swallow our bind request acknowledgement. + */ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_BIND_ACK. Bound to requested SAP!\n")); + break; + } + + case DL_PHYS_ADDR_ACK: + { + /* + * Swallow our physical address request acknowledgement. + */ + vboxNetFltSolarisCachePhysAddr(pThis, pMsg); + break; + } + + case DL_OK_ACK: + { + /* + * Swallow our fake promiscous request acknowledgement. + */ + dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr; + if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ) + { + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n")); + pPromiscStream->fPromisc = true; + } + else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ) + { + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n")); + pPromiscStream->fPromisc = false; + } + break; + } + } + break; + } + + case M_IOCACK: + { + /* + * Swallow our fake raw/fast path mode request acknowledgement. + */ + struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr; + if (pIOC->ioc_id == pPromiscStream->ModeReqId) + { + pPromiscStream->fRawMode = true; + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n", + pPromiscStream->fRawMode ? "ON" : "OFF")); + } + break; + } + + case M_IOCNAK: + { + /* + * Swallow our fake raw/fast path mode request not acknowledged. + */ + struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr; + if (pIOC->ioc_id == pPromiscStream->ModeReqId) + { + pPromiscStream->fRawMode = false; + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: WARNING! Mode not acknowledged. RawMode is %s\n", + pPromiscStream->fRawMode ? "ON" : "OFF")); + } + break; + } + + case M_FLUSH: + { + /* + * We must support flushing queues. + */ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n")); + if (*pMsg->b_rptr & FLUSHR) + flushq(pQueue, FLUSHALL); + break; + } + } + + vboxNetFltRelease(pThis, true /* fBusy */); + } + else + LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n")); + } + + if (fSendUpstream) + { + /* + * Don't queue up things here, can cause bad things to happen when the system + * is under heavy loads and we need to jam across high priority messages which + * if it's not done properly will end up in an infinite loop. + */ + putnext(pQueue, pMsg); + } + else + { + /* + * We need to free up the message if we don't pass it through. + */ + freemsg(pMsg); + } + + return 0; +} + + +/** + * Write side put procedure for processing messages in the write queue. + * All streams, bound and unbound share this write procedure. + * + * @param pQueue Pointer to the write queue. + * @param pMsg Pointer to the message. + * + * @returns corresponding solaris error code. + */ +static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg) +{ + LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg)); + + putnext(pQueue, pMsg); + return 0; +} + + +/** + * Put the stream in raw mode. + * + * @returns VBox status code. + * @param pQueue Pointer to the read queue. + */ +static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pPromiscStream=%p\n", pPromiscStream)); + + mblk_t *pRawMsg = NULL; + pRawMsg = mkiocb(DLIOCRAW); + if (RT_UNLIKELY(!pRawMsg)) + return VERR_NO_MEMORY; + + queue_t *pQueue = pPromiscStream->Stream.pReadQueue; + if (!pQueue) + return VERR_INVALID_POINTER; + + struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr; + pPromiscStream->ModeReqId = pIOC->ioc_id; + pIOC->ioc_count = 0; + + qreply(pQueue, pRawMsg); + return VINF_SUCCESS; +} + + +#if 0 +/** + * Put the stream back in fast path mode. + * + * @returns VBox status code. + * @param pQueue Pointer to the read queue. + */ +static int vboxNetFltSolarisSetFastMode(queue_t *pQueue) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetFastMode pQueue=%p\n", pQueue)); + + mblk_t *pFastMsg = mkiocb(DL_IOC_HDR_INFO); + if (RT_UNLIKELY(!pFastMsg)) + return VERR_NO_MEMORY; + + vboxnetflt_stream_t *pStream = pQueue->q_ptr; + struct iocblk *pIOC = (struct iocblk *)pFastMsg->b_rptr; + pStream->ModeReqId = pIOC->ioc_id; + + size_t cbReq = sizeof(dl_unitdata_req_t) + sizeof(vboxnetflt_dladdr_t); + mblk_t *pDataReqMsg = allocb(cbReq, BPRI_MED); + if (RT_UNLIKELY(!pDataReqMsg)) + return VERR_NO_MEMORY; + + DB_TYPE(pDataReqMsg) = M_PROTO; + dl_unitdata_req_t *pDataReq = (dl_unitdata_req_t *)pDataReqMsg->b_rptr; + pDataReq->dl_primitive = DL_UNITDATA_REQ; + pDataReq->dl_dest_addr_length = sizeof(vboxnetflt_dladdr_t); + pDataReq->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + pDataReq->dl_priority.dl_min = 0; + pDataReq->dl_priority.dl_max = 0; + + bzero(pDataReqMsg->b_rptr + sizeof(dl_unitdata_req_t), sizeof(vboxnetflt_dladdr_t)); + pDataReqMsg->b_wptr = pDataReqMsg->b_rptr + cbReq; + + /* + * Link the data format request message into the header ioctl message. + */ + pFastMsg->b_cont = pDataReqMsg; + pIOC->ioc_count = msgdsize(pDataReqMsg); + + qreply(pQueue, pFastMsg); + return VINF_SUCCESS; +} +#endif + + +/** + * Send fake promiscous mode requests downstream. + * + * @param pQueue Pointer to the read queue. + * @param fPromisc Whether to enable promiscous mode or not. + * @param PromiscLevel Promiscous level; DL_PROMISC_PHYS/SAP/MULTI. + * + * @returns VBox error code. + */ +static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisPromiscReq pQueue=%p fPromisc=%d\n", pQueue, fPromisc)); + + t_uscalar_t Cmd; + size_t cbReq = 0; + if (fPromisc) + { + Cmd = DL_PROMISCON_REQ; + cbReq = DL_PROMISCON_REQ_SIZE; + } + else + { + Cmd = DL_PROMISCOFF_REQ; + cbReq = DL_PROMISCOFF_REQ_SIZE; + } + + mblk_t *pPromiscPhysMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd); + if (RT_UNLIKELY(!pPromiscPhysMsg)) + return VERR_NO_MEMORY; + + mblk_t *pPromiscSapMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd); + if (RT_UNLIKELY(!pPromiscSapMsg)) + { + freemsg(pPromiscPhysMsg); + return VERR_NO_MEMORY; + } + + if (fPromisc) + { + ((dl_promiscon_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS; + ((dl_promiscon_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP; + } + else + { + ((dl_promiscoff_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS; + ((dl_promiscoff_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP; + } + + qreply(pQueue, pPromiscPhysMsg); + qreply(pQueue, pPromiscSapMsg); + + return VINF_SUCCESS; +} + + +/** + * Send a fake physical address request downstream. + * + * @returns VBox status code. + * @param pQueue Pointer to the read queue. + * @param pMsg Pointer to the request message. + */ +static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisPhysAddrReq pQueue=%p\n", pQueue)); + + t_uscalar_t Cmd = DL_PHYS_ADDR_REQ; + size_t cbReq = DL_PHYS_ADDR_REQ_SIZE; + mblk_t *pPhysAddrMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd); + if (RT_UNLIKELY(!pPhysAddrMsg)) + return VERR_NO_MEMORY; + + dl_phys_addr_req_t *pPhysAddrReq = (dl_phys_addr_req_t *)pPhysAddrMsg->b_rptr; + pPhysAddrReq->dl_addr_type = DL_CURR_PHYS_ADDR; + + qreply(pQueue, pPhysAddrMsg); + return VINF_SUCCESS; +} + + +/** + * Cache the MAC address into the VirtualBox instance given a physical + * address acknowledgement message. + * + * @param pThis The instance. + * @param pMsg Pointer to the physical address acknowledgement message. + */ +static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr pThis=%p pMsg=%p\n", pThis, pMsg)); + + AssertCompile(sizeof(RTMAC) == ETHERADDRL); + dl_phys_addr_ack_t *pPhysAddrAck = (dl_phys_addr_ack_t *)pMsg->b_rptr; + if (pPhysAddrAck->dl_addr_length == sizeof(pThis->u.s.Mac)) + { + bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac)); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n", sizeof(pThis->u.s.Mac), + &pThis->u.s.Mac)); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL, + pPhysAddrAck->dl_addr_length)); + } +} + + +/** + * Prepare DLPI bind request to a SAP. + * + * @returns VBox status code. + * @param pQueue Pointer to the read queue. + * @param SAP The SAP to bind the stream to. + */ +static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisBindReq SAP=%d\n", SAP)); + + mblk_t *pBindMsg = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); + if (RT_UNLIKELY(!pBindMsg)) + return VERR_NO_MEMORY; + + dl_bind_req_t *pBindReq = (dl_bind_req_t *)pBindMsg->b_rptr; + pBindReq->dl_sap = SAP; + pBindReq->dl_max_conind = 0; + pBindReq->dl_conn_mgmt = 0; + pBindReq->dl_xidtest_flg = 0; + pBindReq->dl_service_mode = DL_CLDLS; + + qreply(pQueue, pBindMsg); + return VINF_SUCCESS; +} + + +/** + * Prepare DLPI notifications request. + * + * @returns VBox status code. + * @param pQueue Pointer to the read queue. + */ +static int vboxNetFltSolarisNotifyReq(queue_t *pQueue) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisNotifyReq\n")); + + mblk_t *pNotifyMsg = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ); + if (RT_UNLIKELY(!pNotifyMsg)) + return VERR_NO_MEMORY; + + dl_notify_req_t *pNotifyReq = (dl_notify_req_t *)pNotifyMsg->b_rptr; + pNotifyReq->dl_notifications = DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_PHYS_ADDR; + + qreply(pQueue, pNotifyMsg); + return VINF_SUCCESS; +} + + +/** + * Opens the required device and returns the vnode_t associated with it. + * We require this for the funny attach/detach routine. + * + * @returns VBox status code. + * @param pszDev The device path. + * @param ppVNode Where to store the vnode_t pointer associated with the opened device. + * @param ppVNodeHeld Where to store the vnode_t required during closing of the device. + * @param ppUser Open handle required while closing the device. + */ +static int vboxNetFltSolarisOpenDev(char *pszDev, vnode_t **ppVNode, vnode_t **ppVNodeHeld, TIUSER **ppUser) +{ + int rc; + vnode_t *pVNodeHeld = NULL; + rc = lookupname(pszDev, UIO_SYSSPACE, FOLLOW, NULLVPP, &pVNodeHeld); + if (!rc) + { + TIUSER *pUser; + rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred); + if (!rc) + { + *ppVNode = pUser->fp->f_vnode; + *ppVNodeHeld = pVNodeHeld; + *ppUser = pUser; + return VINF_SUCCESS; + } + VN_RELE(pVNodeHeld); + } + return VERR_PATH_NOT_FOUND; +} + + +/** + * Close the device opened using vboxNetFltSolarisOpenDev. + * + * @param pVNodeHeld Pointer to the held vnode of the device. + * @param pUser Pointer to the file handle. + */ +static void vboxNetFltSolarisCloseDev(vnode_t *pVNodeHeld, TIUSER *pUser) +{ + t_kclose(pUser, 0); + VN_RELE(pVNodeHeld); +} + + +/** + * Get the logical interface flags from the stream. + * + * @returns VBox status code. + * @param hDevice Layered device handle. + * @param pInterface Pointer to the interface. + */ +static int vboxNetFltSolarisGetIfFlags(ldi_handle_t hDevice, struct lifreq *pInterface) +{ + struct strioctl IOCReq; + int rc; + int ret; + IOCReq.ic_cmd = SIOCGLIFFLAGS; + IOCReq.ic_timout = 40; + IOCReq.ic_len = sizeof(struct lifreq); + IOCReq.ic_dp = (caddr_t)pInterface; + rc = ldi_ioctl(hDevice, I_STR, (intptr_t)&IOCReq, FKIOCTL, kcred, &ret); + if (!rc) + return VINF_SUCCESS; + + return RTErrConvertFromErrno(rc); +} + + +/** + * Sets the multiplexor ID from the interface. + * + * @returns VBox status code. + * @param pVNode Pointer to the device vnode. + * @param pInterface Pointer to the interface. + */ +static int vboxNetFltSolarisSetMuxId(vnode_t *pVNode, struct lifreq *pInterface) +{ + struct strioctl IOCReq; + int rc; + int ret; + IOCReq.ic_cmd = SIOCSLIFMUXID; + IOCReq.ic_timout = 40; + IOCReq.ic_len = sizeof(struct lifreq); + IOCReq.ic_dp = (caddr_t)pInterface; + + rc = strioctl(pVNode, I_STR, (intptr_t)&IOCReq, 0, K_TO_K, kcred, &ret); + if (!rc) + return VINF_SUCCESS; + + return RTErrConvertFromErrno(rc); +} + + +/** + * Get the multiplexor file descriptor of the lower stream. + * + * @returns VBox status code. + * @param MuxId The multiplexor ID. + * @param pFd Where to store the lower stream file descriptor. + */ +static int vboxNetFltSolarisMuxIdToFd(vnode_t *pVNode, int MuxId, int *pFd) +{ + int ret; + int rc = strioctl(pVNode, _I_MUXID2FD, (intptr_t)MuxId, 0, K_TO_K, kcred, &ret); + if (!rc) + { + *pFd = ret; + return VINF_SUCCESS; + } + + return RTErrConvertFromErrno(rc); +} + + +/** + * Relinks the lower and the upper IPv4 stream. + * + * @returns VBox status code. + * @param pVNode Pointer to the device vnode. + * @param pInterface Pointer to the interface. + * @param IpMuxFd The IP multiplexor ID. + * @param ArpMuxFd The ARP multiplexor ID. + */ +static int vboxNetFltSolarisRelinkIp4(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode, + pInterface, IpMuxFd, ArpMuxFd)); + + int NewIpMuxId; + int NewArpMuxId; + int rc = strioctl(pVNode, I_PLINK, (intptr_t)IpMuxFd, 0, K_TO_K, kcred, &NewIpMuxId); + int rc2 = strioctl(pVNode, I_PLINK, (intptr_t)ArpMuxFd, 0, K_TO_K, kcred, &NewArpMuxId); + if ( !rc + && !rc2) + { + pInterface->lifr_ip_muxid = NewIpMuxId; + pInterface->lifr_arp_muxid = NewArpMuxId; + rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to set new Mux Id.\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to link.\n")); + + return VERR_GENERAL_FAILURE; +} + + +/** + * Relinks the lower and the upper IPv6 stream. + * + * @returns VBox status code. + * @param pVNode Pointer to the device vnode. + * @param pInterface Pointer to the interface. + * @param Ip6MuxFd The IPv6 multiplexor ID. + */ +static int vboxNetFltSolarisRelinkIp6(vnode_t *pVNode, struct lifreq *pInterface, int Ip6MuxFd) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd)); + + int NewIp6MuxId; + int rc = strioctl(pVNode, I_PLINK, (intptr_t)Ip6MuxFd, 0, K_TO_K, kcred, &NewIp6MuxId); + if (!rc) + { + pInterface->lifr_ip_muxid = NewIp6MuxId; + rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to set new Mux Id.\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to link.\n")); + + return VERR_GENERAL_FAILURE; +} + + +/** + * Dynamically find the position on the host stack where to attach/detach ourselves. + * + * @returns VBox status code. + * @param pVNode Pointer to the lower stream vnode. + * @param pModPos Where to store the module position. + */ +static int vboxNetFltSolarisDetermineModPos(bool fAttach, vnode_t *pVNode, int *pModPos) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos)); + + int cMod; + int rc = strioctl(pVNode, I_LIST, (intptr_t)NULL, 0, K_TO_K, kcred, &cMod); + if (!rc) + { + if (cMod < 1) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: too few modules on host interface. cMod=%d\n")); + return VERR_OUT_OF_RANGE; + } + + /* + * While attaching we make sure we are at the bottom most of the stack, excepting + * the host driver. + */ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: cMod=%d\n", cMod)); + if (fAttach) + { + *pModPos = cMod - 1; + return VINF_SUCCESS; + } + + /* + * Detaching is a bit more complicated; since user could have altered the stack positions + * we take the safe approach by finding our position. + */ + struct str_list StrList; + StrList.sl_nmods = cMod; + StrList.sl_modlist = RTMemAllocZ(cMod * sizeof(struct str_list)); + if (RT_UNLIKELY(!StrList.sl_modlist)) + { + LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to alloc memory for StrList.\n")); + return VERR_NO_MEMORY; + } + + /* + * Get the list of all modules on the stack. + */ + int ret; + rc = strioctl(pVNode, I_LIST, (intptr_t)&StrList, 0, K_TO_K, kcred, &ret); + if (!rc) + { + /* + * Find our filter. + */ + for (int i = 0; i < StrList.sl_nmods; i++) + { + if (!strcmp(DEVICE_NAME, StrList.sl_modlist[i].l_name)) + { + LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i)); + *pModPos = i; + RTMemFree(StrList.sl_modlist); + return VINF_SUCCESS; + } + } + + LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n")); + + RTMemFree(StrList.sl_modlist); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc)); + return VERR_GENERAL_FAILURE; +} + + +/** + * Opens up dedicated stream on top of the interface. + * As a side-effect, the stream gets opened during + * the I_PUSH phase. + * + * @param pThis The instance. + */ +static int vboxNetFltSolarisOpenStream(PVBOXNETFLTINS pThis) +{ + ldi_ident_t DevId; + DevId = ldi_ident_from_anon(); + int ret; + + /** @todo support DLPI style 2.*/ + /* + * Try style-1 open first. + */ + char szDev[128]; + RTStrPrintf(szDev, sizeof(szDev), "/dev/net/%s", pThis->szName); + int rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId); + if ( rc + && rc == ENODEV) /* ENODEV is returned when resolvepath fails, not ENOENT */ + { + /* + * Fallback to non-ClearView style-1 open. + */ + RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pThis->szName); + rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId); + } + + ldi_ident_release(DevId); + if (rc) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to open '%s' rc=%d pszName='%s'\n", szDev, rc, pThis->szName)); + return VERR_INTNET_FLT_IF_FAILED; + } + + rc = ldi_ioctl(pThis->u.s.hIface, I_FIND, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret); + if (!rc) + { + if (!ret) + { + g_VBoxNetFltSolarisInstance = pThis; + g_VBoxNetFltSolarisStreamType = kPromiscStream; + + rc = ldi_ioctl(pThis->u.s.hIface, I_PUSH, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret); + + g_VBoxNetFltSolarisInstance = NULL; + g_VBoxNetFltSolarisStreamType = kUndefined; + + if (!rc) + return VINF_SUCCESS; + + LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to push filter onto host interface '%s'\n", pThis->szName)); + } + else + return VINF_SUCCESS; + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to search for filter in interface '%s'.\n", pThis->szName)); + + return VERR_INTNET_FLT_IF_FAILED; +} + + +/** + * Closes the interface, thereby closing the dedicated stream. + * + * @param pThis The instance. + */ +static void vboxNetFltSolarisCloseStream(PVBOXNETFLTINS pThis) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisCloseStream pThis=%p\n")); + + vboxNetFltRetain(pThis, false /* fBusy */); + + ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred); +} + + +/** + * Dynamically attach under IPv4 and ARP streams on the host stack. + * + * @returns VBox status code. + * @param pThis The instance. + * @param fAttach Is this an attach or detach. + */ +static int vboxNetFltSolarisAttachIp4(PVBOXNETFLTINS pThis, bool fAttach) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp4 pThis=%p fAttach=%d\n", pThis, fAttach)); + + /* + * Statuatory Warning: Hackish code ahead. + */ + char *pszModName = DEVICE_NAME; + + struct lifreq Ip4Interface; + bzero(&Ip4Interface, sizeof(Ip4Interface)); + Ip4Interface.lifr_addr.ss_family = AF_INET; + strncpy(Ip4Interface.lifr_name, pThis->szName, sizeof(Ip4Interface.lifr_name)); + + struct strmodconf StrMod; + StrMod.mod_name = pszModName; + StrMod.pos = -1; /* this is filled in later. */ + + struct strmodconf ArpStrMod; + bcopy(&StrMod, &ArpStrMod, sizeof(StrMod)); + + int rc; + int rc2; + int ret; + ldi_ident_t DeviceIdent = ldi_ident_from_anon(); + ldi_handle_t Ip4DevHandle; + ldi_handle_t ArpDevHandle; + + /* + * Open the IP and ARP streams as layered devices. + */ + rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &Ip4DevHandle, DeviceIdent); + if (rc) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName)); + ldi_ident_release(DeviceIdent); + return VERR_INTNET_FLT_IF_FAILED; + } + + rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ArpDevHandle, DeviceIdent); + if (rc) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName)); + ldi_ident_release(DeviceIdent); + ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred); + return VERR_INTNET_FLT_IF_FAILED; + } + + ldi_ident_release(DeviceIdent); + + /* + * Obtain the interface flags from IPv4. + */ + rc = vboxNetFltSolarisGetIfFlags(Ip4DevHandle, &Ip4Interface); + if (RT_SUCCESS(rc)) + { + /* + * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform + * things that are not possible from the layered interface. + */ + vnode_t *pUdp4VNode = NULL; + vnode_t *pUdp4VNodeHeld = NULL; + TIUSER *pUdp4User = NULL; + rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pUdp4VNode, &pUdp4VNodeHeld, &pUdp4User); + if (RT_SUCCESS(rc)) + { + /* + * Get the multiplexor IDs. + */ + rc = ldi_ioctl(Ip4DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip4Interface, FKIOCTL, kcred, &ret); + if (!rc) + { + /* + * Get the multiplex file descriptor to the lower streams. Generally this is lost + * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack. + */ + int Ip4MuxFd; + int ArpMuxFd; + rc = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_ip_muxid, &Ip4MuxFd); + rc2 = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_arp_muxid, &ArpMuxFd); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(rc2)) + { + /* + * We need to I_PUNLINK on these multiplexor IDs before we can start + * operating on the lower stream as insertions are direct operations on the lower stream. + */ + int ret; + rc = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret); + rc2 = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret); + if ( !rc + && !rc2) + { + /* + * Obtain the vnode from the useless userland file descriptor. + */ + file_t *pIpFile = getf(Ip4MuxFd); + file_t *pArpFile = getf(ArpMuxFd); + if ( pIpFile + && pArpFile + && pArpFile->f_vnode + && pIpFile->f_vnode) + { + vnode_t *pIp4VNode = pIpFile->f_vnode; + vnode_t *pArpVNode = pArpFile->f_vnode; + + /* + * Find the position on the host stack for attaching/detaching ourselves. + */ + rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp4VNode, &StrMod.pos); + rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pArpVNode, &ArpStrMod.pos); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(rc2)) + { + /* + * Inject/Eject from the host IP stack. + */ + if (!fAttach) + vboxNetFltRetain(pThis, false /* fBusy */); + + /* + * Set global data which will be grabbed by ModOpen. + * There is a known (though very unlikely) race here because + * of the inability to pass user data while inserting. + */ + g_VBoxNetFltSolarisInstance = pThis; + g_VBoxNetFltSolarisStreamType = kIp4Stream; + + rc = strioctl(pIp4VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, + kcred, &ret); + + g_VBoxNetFltSolarisInstance = NULL; + g_VBoxNetFltSolarisStreamType = kUndefined; + + if (!rc) + { + if (!fAttach) + vboxNetFltRetain(pThis, false /* fBusy */); + + /* + * Inject/Eject from the host ARP stack. + */ + g_VBoxNetFltSolarisInstance = pThis; + g_VBoxNetFltSolarisStreamType = kArpStream; + + rc = strioctl(pArpVNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K, + kcred, &ret); + + g_VBoxNetFltSolarisInstance = NULL; + g_VBoxNetFltSolarisStreamType = kUndefined; + + if (!rc) + { + /* + * Our job's not yet over; we need to relink the upper and lower streams + * otherwise we've pretty much screwed up the host interface. + */ + rc = vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd); + if (RT_SUCCESS(rc)) + { + /* + * Close the devices ONLY during the return from function case; otherwise + * we end up close twice which is an instant kernel panic. + */ + vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User); + ldi_close(ArpDevHandle, FREAD | FWRITE, kcred); + ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Success! %s %s@(IPv4:%d Arp:%d) " + "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name, + StrMod.pos, ArpStrMod.pos, fAttach ? "to" : "from", pThis->szName)); + return VINF_SUCCESS; + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Relinking failed. Mode=%s rc=%d.\n", + fAttach ? "inject" : "eject", rc)); + } + + /* + * Try failing gracefully during attach. + */ + if (fAttach) + strioctl(pArpVNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the ARP stack. rc=%d\n", + fAttach ? "inject into" : "eject from", rc)); + } + + if (fAttach) + strioctl(pIp4VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret); + + vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd); + + if (!fAttach) + vboxNetFltRelease(pThis, false /* fBusy */); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the IP stack. rc=%d\n", + fAttach ? "inject into" : "eject from", rc)); + } + + g_VBoxNetFltSolarisInstance = NULL; + g_VBoxNetFltSolarisStreamType = kUndefined; + + if (!fAttach) + vboxNetFltRelease(pThis, false /* fBusy */); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc, rc2)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get vnode from MuxFd.\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get MuxFd from MuxId. rc=%d rc2=%d\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get Mux Ids. rc=%d\n", rc)); + vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open UDP. rc=%d\n", rc)); + } + else + { + /* + * This would happen for interfaces that are not plumbed. + */ + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Warning: seems '%s' is unplumbed.\n", pThis->szName)); + rc = VINF_SUCCESS; + } + + ldi_close(ArpDevHandle, FREAD | FWRITE, kcred); + ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred); + + if (RT_SUCCESS(rc)) + return rc; + + return VERR_INTNET_FLT_IF_FAILED; +} + + +/** + * Dynamically attach under IPv6 on the host stack. + * + * @returns VBox status code. + * @param pThis The instance. + * @param fAttach Is this an attach or detach. + */ +static int vboxNetFltSolarisAttachIp6(PVBOXNETFLTINS pThis, bool fAttach) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6 pThis=%p fAttach=%d\n", pThis, fAttach)); + + /* + * Statuatory Warning: Hackish code ahead. + */ + char *pszModName = DEVICE_NAME; + + struct lifreq Ip6Interface; + bzero(&Ip6Interface, sizeof(Ip6Interface)); + Ip6Interface.lifr_addr.ss_family = AF_INET6; + strncpy(Ip6Interface.lifr_name, pThis->szName, sizeof(Ip6Interface.lifr_name)); + + struct strmodconf StrMod; + StrMod.mod_name = pszModName; + StrMod.pos = -1; /* this is filled in later. */ + + int rc; + int rc2; + int ret; + ldi_ident_t DeviceIdent = ldi_ident_from_anon(); + ldi_handle_t Ip6DevHandle; + ldi_handle_t Udp6DevHandle; + + /* + * Open the IPv6 stream as a layered devices. + */ + rc = ldi_open_by_name(IP6_DEV_NAME, FREAD | FWRITE, kcred, &Ip6DevHandle, DeviceIdent); + ldi_ident_release(DeviceIdent); + if (rc) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open the IPv6 stream on '%s'.\n", pThis->szName)); + return VERR_INTNET_FLT_IF_FAILED; + } + + /* + * Obtain the interface flags from IPv6. + */ + rc = vboxNetFltSolarisGetIfFlags(Ip6DevHandle, &Ip6Interface); + if (RT_SUCCESS(rc)) + { + /* + * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform + * things that are not possible from the layered interface. + */ + vnode_t *pUdp6VNode = NULL; + vnode_t *pUdp6VNodeHeld = NULL; + TIUSER *pUdp6User = NULL; + rc = vboxNetFltSolarisOpenDev(UDP6_DEV_NAME, &pUdp6VNode, &pUdp6VNodeHeld, &pUdp6User); + if (RT_SUCCESS(rc)) + { + /* + * Get the multiplexor IDs. + */ + rc = ldi_ioctl(Ip6DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip6Interface, FKIOCTL, kcred, &ret); + if (!rc) + { + /* + * Get the multiplex file descriptor to the lower streams. Generally this is lost + * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack. + */ + int Ip6MuxFd; + rc = vboxNetFltSolarisMuxIdToFd(pUdp6VNode, Ip6Interface.lifr_ip_muxid, &Ip6MuxFd); + if (RT_SUCCESS(rc)) + { + /* + * We need to I_PUNLINK on these multiplexor IDs before we can start + * operating on the lower stream as insertions are direct operations on the lower stream. + */ + int ret; + rc = strioctl(pUdp6VNode, I_PUNLINK, (intptr_t)Ip6Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret); + if (!rc) + { + /* + * Obtain the vnode from the useless userland file descriptor. + */ + file_t *pIpFile = getf(Ip6MuxFd); + if ( pIpFile + && pIpFile->f_vnode) + { + vnode_t *pIp6VNode = pIpFile->f_vnode; + + /* + * Find the position on the host stack for attaching/detaching ourselves. + */ + rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp6VNode, &StrMod.pos); + if (RT_SUCCESS(rc)) + { + if (!fAttach) + vboxNetFltRetain(pThis, false /* fBusy */); + + /* + * Set global data which will be grabbed by ModOpen. + * There is a known (though very unlikely) race here because + * of the inability to pass user data while inserting. + */ + g_VBoxNetFltSolarisInstance = pThis; + g_VBoxNetFltSolarisStreamType = kIp6Stream; + + /* + * Inject/Eject from the host IPv6 stack. + */ + rc = strioctl(pIp6VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, + kcred, &ret); + if (!rc) + { + g_VBoxNetFltSolarisInstance = NULL; + g_VBoxNetFltSolarisStreamType = kUndefined; + + /* + * Our job's not yet over; we need to relink the upper and lower streams + * otherwise we've pretty much screwed up the host interface. + */ + rc = vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd); + if (RT_SUCCESS(rc)) + { + /* + * Close the devices ONLY during the return from function case; otherwise + * we end up close twice which is an instant kernel panic. + */ + vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User); + ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Success! %s %s@(IPv6:%d) " + "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name, + StrMod.pos, fAttach ? "to" : "from", pThis->szName)); + return VINF_SUCCESS; + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Relinking failed. Mode=%s rc=%d.\n", + fAttach ? "inject" : "eject", rc)); + } + + if (fAttach) + strioctl(pIp6VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret); + + vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to %s the IP stack. rc=%d\n", + fAttach ? "inject into" : "eject from", rc)); + if (!fAttach) + vboxNetFltRelease(pThis, false /* fBusy */); + } + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d rc2=%d\n", rc, rc2)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get vnode from MuxFd.\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d rc2=%d\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get Mux Ids. rc=%d\n", rc)); + vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open UDP. rc=%d\n", rc)); + } + else + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get IPv6 flags.\n", pThis->szName)); + + ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred); + + if (RT_SUCCESS(rc)) + return rc; + + return VERR_INTNET_FLT_IF_FAILED; +} + + +/** + * Wrapper for attaching ourselves to the interface. + * + * @returns VBox status code. + * @param pThis The instance. + */ +static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis) +{ + int rc = vboxNetFltSolarisOpenStream(pThis); + if (RT_SUCCESS(rc)) + { + rc = vboxNetFltSolarisAttachIp4(pThis, true /* fAttach */); + if (RT_SUCCESS(rc)) + { + vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */); + /* Ignore Ipv6 binding errors as it's optional. */ + + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false); + } + else + vboxNetFltSolarisCloseStream(pThis); + } + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * Wrapper for detaching ourselves from the interface. + * + * @returns VBox status code. + * @param pThis The instance. + * @remarks Owns the globals mutex, so re-requesting it anytime during this phase + * would panic the system (e.g. in vboxNetFltSolarisFindInstance). + */ +static int vboxNetFltSolarisDetachFromInterface(PVBOXNETFLTINS pThis) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetachFromInterface pThis=%p\n", pThis)); + + ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true); + vboxNetFltSolarisCloseStream(pThis); + int rc = vboxNetFltSolarisAttachIp4(pThis, false /* fAttach */); + if (pThis->u.s.pvIp6Stream) + rc = vboxNetFltSolarisAttachIp6(pThis, false /* fAttach */); + + return rc; +} + + +/** + * Create a solaris message block from the SG list. + * + * @returns Solaris message block. + * @param pThis The instance. + * @param pSG Pointer to the scatter-gather list. + */ +static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n")); + + mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_MED); + if (RT_UNLIKELY(!pMsg)) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal)); + return NULL; + } + + /* + * Single buffer copy. Maybe later explore the + * need/possibility for using a mblk_t chain rather. + */ + for (unsigned i = 0; i < pSG->cSegsUsed; i++) + { + if (pSG->aSegs[i].pv) + { + bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb); + pMsg->b_wptr += pSG->aSegs[i].cb; + } + } + DB_TYPE(pMsg) = M_DATA; + return pMsg; +} + + +/** + * Calculate the number of segments required for this message block. + * + * @returns Number of segments. + * @param pThis The instance + * @param pMsg Pointer to the data message. + */ +static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg) +{ + unsigned cSegs = 0; + for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont) + if (MBLKL(pCur)) + cSegs++; + +#ifdef PADD_RUNT_FRAMES_FROM_HOST + if (msgdsize(pMsg) < 60) + cSegs++; +#endif + + NOREF(pThis); + return RT_MAX(cSegs, 1); +} + + +/** + * Initializes an SG list from the given message block. + * + * @returns VBox status code. + * @param pThis The instance. + * @param pMsg Pointer to the data message. + The caller must ensure it's not a control message block. + * @param pSG Pointer to the SG. + * @param cSegs Number of segments in the SG. + * This should match the number in the message block exactly! + * @param fSrc The source of the message. + */ +static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs)); + + pSG->pvOwnerData = NULL; + pSG->pvUserData = NULL; + pSG->pvUserData2 = NULL; + pSG->cUsers = 1; + pSG->cbTotal = 0; + pSG->fFlags = INTNETSG_FLAGS_TEMP; + pSG->cSegsAlloc = cSegs; + + /* + * Convert the message block to segments. + */ + mblk_t *pCur = pMsg; + unsigned iSeg = 0; + while (pCur) + { + size_t cbSeg = MBLKL(pCur); + if (cbSeg) + { + void *pvSeg = pCur->b_rptr; + pSG->aSegs[iSeg].pv = pvSeg; + pSG->aSegs[iSeg].cb = cbSeg; + pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; + pSG->cbTotal += cbSeg; + iSeg++; + } + pCur = pCur->b_cont; + } + pSG->cSegsUsed = iSeg; + +#ifdef PADD_RUNT_FRAMES_FROM_HOST + if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST)) + { + LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n")); + + static uint8_t const s_abZero[128] = {0}; + pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; + pSG->aSegs[iSeg].pv = (void *)&s_abZero[0]; + pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal; + pSG->cbTotal = 60; + pSG->cSegsUsed++; + } +#endif + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg))); + return VINF_SUCCESS; +} + + +/** + * Converts raw mode M_DATA messages to M_PROTO DL_UNITDATA_IND format. + * + * @returns VBox status code. + * @param pMsg Pointer to the raw message. + * @param pDlpiMsg Where to store the M_PROTO message. + * + * @remarks The original raw message would be no longer valid and will be + * linked as part of the new DLPI message. Callers must take care + * not to use the raw message if this routine is successful. + */ +static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData pMsg=%p\n", pMsg)); + + if (DB_TYPE(pMsg) != M_DATA) + return VERR_NO_MEMORY; + + size_t cbMsg = sizeof(dl_unitdata_ind_t) + 2 * sizeof(vboxnetflt_dladdr_t); + mblk_t *pDlpiMsg = allocb(cbMsg, BPRI_MED); + if (RT_UNLIKELY(!pDlpiMsg)) + return VERR_NO_MEMORY; + + DB_TYPE(pDlpiMsg) = M_PROTO; + dl_unitdata_ind_t *pDlpiData = (dl_unitdata_ind_t *)pDlpiMsg->b_rptr; + pDlpiData->dl_primitive = DL_UNITDATA_IND; + pDlpiData->dl_dest_addr_length = VBOXNETFLT_DLADDRL; + pDlpiData->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); + pDlpiData->dl_src_addr_length = VBOXNETFLT_DLADDRL; + pDlpiData->dl_src_addr_offset = VBOXNETFLT_DLADDRL + sizeof(dl_unitdata_ind_t); + + PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr; + + vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset); + pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType); + bcopy(&pEthHdr->DstMac, &pDlAddr->Mac, sizeof(RTMAC)); + + pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_src_addr_offset); + pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType); + bcopy(&pEthHdr->SrcMac, &pDlAddr->Mac, sizeof(RTMAC)); + + pDlpiMsg->b_wptr = pDlpiMsg->b_rptr + cbMsg; + + /* Make the message point to the protocol header */ + pMsg->b_rptr += sizeof(RTNETETHERHDR); + + pDlpiMsg->b_cont = pMsg; + *ppDlpiMsg = pDlpiMsg; + return VINF_SUCCESS; +} + + +/** + * Converts DLPI M_PROTO messages to the raw mode M_DATA format. + * + * @returns VBox status code. + * @param pMsg Pointer to the M_PROTO message. + * @param ppRawMsg Where to store the converted message. + * + * @remarks If successful, the original pMsg is no longer valid, it will be deleted. + * Callers must take care not to continue to use pMsg after a successful + * call to this conversion routine. + */ +static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw pMsg=%p\n", pMsg)); + + if ( !pMsg->b_cont + || DB_TYPE(pMsg) != M_PROTO) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw invalid input message.\n")); + return VERR_NET_PROTOCOL_ERROR; + } + + /* + * Upstream consumers send/receive packets in the fast path mode. + * We of course need to convert them into raw ethernet frames. + */ + RTNETETHERHDR EthHdr; + union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr; + switch (pPrim->dl_primitive) + { + case DL_UNITDATA_IND: + { + /* + * Receive side. + */ + dl_unitdata_ind_t *pDlpiMsg = (dl_unitdata_ind_t *)pMsg->b_rptr; + bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac)); + bcopy(pMsg->b_rptr + pDlpiMsg->dl_src_addr_offset, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac)); + + vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset); + EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP); + + break; + } + + case DL_UNITDATA_REQ: + { + /* + * Send side. + */ + dl_unitdata_req_t *pDlpiMsg = (dl_unitdata_req_t *)pMsg->b_rptr; + + bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac)); + bcopy(&pThis->u.s.Mac, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac)); + + vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset); + EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP); + + break; + } + + default: + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw Unknown M_PROTO. This shouldn't be happening!!")); + return VERR_NET_PROTOCOL_ERROR; + } + } + + /* + * Let us just link it as a mblk_t chain rather than re-copy the entire message. + * The vboxNetFltSolarisMBlkToSG function will handle chained mblk_t's. + */ + size_t cbLen = sizeof(EthHdr); + mblk_t *pEtherMsg = allocb(cbLen, BPRI_MED); + if (RT_UNLIKELY(!pEtherMsg)) + return VERR_NO_MEMORY; + + DB_TYPE(pEtherMsg) = M_DATA; + bcopy(&EthHdr, pEtherMsg->b_wptr, sizeof(EthHdr)); + pEtherMsg->b_wptr += cbLen; + + pEtherMsg->b_cont = pMsg->b_cont; + + /* + * Change the chained blocks to type M_DATA. + */ + for (mblk_t *pTmp = pEtherMsg->b_cont; pTmp; pTmp = pTmp->b_cont) + DB_TYPE(pTmp) = M_DATA; + + pMsg->b_cont = NULL; + freemsg(pMsg); + + *ppRawMsg = pEtherMsg; + return VINF_SUCCESS; +} + + +/** + * Initializes a packet identifier. + * + * @param pTag Pointer to the packed identifier. + * @param pMsg Pointer to the message to be identified. + * + * @remarks Warning!!! This function assumes 'pMsg' is an unchained message. + */ +static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg) +{ + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr; + size_t cbMsg = MBLKL(pMsg); + + pTag->cbPacket = cbMsg; + pTag->Checksum = RTCrc32(pMsg->b_rptr, cbMsg); + bcopy(&pEthHdr->SrcMac, &pTag->SrcMac, sizeof(RTMAC)); + bcopy(&pEthHdr->DstMac, &pTag->DstMac, sizeof(RTMAC)); +} + + +/** + * Queues a packet for loopback elimination. + * + * @returns VBox status code. + * @param pThis The instance. + * @param pPromiscStream Pointer to the promiscuous stream. + * @param pMsg Pointer to the message. + */ +static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg) +{ + Assert(pThis); + Assert(pMsg); + Assert(DB_TYPE(pMsg) == M_DATA); + Assert(pPromiscStream); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg)); + + if (RT_UNLIKELY(pMsg->b_cont)) + { + /* + * We don't currently make chained messages in on Xmit + * so this only needs to be supported when we do that. + */ + return VERR_NOT_SUPPORTED; + } + + size_t cbMsg = MBLKL(pMsg); + if (RT_UNLIKELY(cbMsg < sizeof(RTNETETHERHDR))) + return VERR_NET_MSG_SIZE; + + int rc = RTSemFastMutexRequest(pThis->u.s.hFastMtx); + AssertRCReturn(rc, rc); + + PVBOXNETFLTPACKETID pCur = NULL; + if (pPromiscStream->cLoopback < VBOXNETFLT_LOOPBACK_SIZE + || ( pPromiscStream->pHead + && pPromiscStream->pHead->cbPacket == 0)) + { + do + { + if (!pPromiscStream->pHead) + { + pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID)); + if (RT_UNLIKELY(!pCur)) + { + rc = VERR_NO_MEMORY; + break; + } + + vboxNetFltSolarisInitPacketId(pCur, pMsg); + + pCur->pNext = NULL; + pPromiscStream->pHead = pCur; + pPromiscStream->pTail = pCur; + pPromiscStream->cLoopback++; + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback initialized head. checksum=%u.\n", + pPromiscStream->pHead->Checksum)); + break; + } + else if ( pPromiscStream->pHead + && pPromiscStream->pHead->cbPacket == 0) + { + pCur = pPromiscStream->pHead; + vboxNetFltSolarisInitPacketId(pCur, pMsg); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback re-used head checksum=%u cLoopback=%d.\n", + pCur->Checksum, pPromiscStream->cLoopback)); + break; + } + else + { + pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID)); + if (RT_UNLIKELY(!pCur)) + { + rc = VERR_NO_MEMORY; + break; + } + + vboxNetFltSolarisInitPacketId(pCur, pMsg); + + pCur->pNext = pPromiscStream->pHead; + pPromiscStream->pHead = pCur; + pPromiscStream->cLoopback++; + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum, + pPromiscStream->cLoopback)); + break; + } + } while (0); + } + else + { + /* + * Maximum loopback queue size reached. Re-use tail as head. + */ + Assert(pPromiscStream->pHead); + Assert(pPromiscStream->pTail); + + /* + * Find tail's previous item. + */ + PVBOXNETFLTPACKETID pPrev = NULL; + pCur = pPromiscStream->pHead; + + /** @todo consider if this is worth switching to a double linked list... */ + while (pCur != pPromiscStream->pTail) + { + pPrev = pCur; + pCur = pCur->pNext; + } + + pPromiscStream->pTail = pPrev; + pPromiscStream->pTail->pNext = NULL; + pCur->pNext = pPromiscStream->pHead; + pPromiscStream->pHead = pCur; + + vboxNetFltSolarisInitPacketId(pCur, pMsg); + LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum, + pPromiscStream->cLoopback)); + } + + RTSemFastMutexRelease(pThis->u.s.hFastMtx); + + return rc; +} + + +/** + * Checks if the packet is enqueued for loopback as our own packet. + * + * @returns If it's our packet, returns true after dequeuing it, otherwise false. + * @param pThis The instance. + * @param pPromiscStream Pointer to the promiscuous stream. + * @param pMsg Pointer to the message. + */ +static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg) +{ + Assert(pThis); + Assert(pPromiscStream); + Assert(pMsg); + Assert(DB_TYPE(pMsg) == M_DATA); + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk pThis=%p pMsg=%p\n", pThis, pMsg)); + + if (pMsg->b_cont) + { + /** Handle this when Xmit makes chained messages */ + return false; + } + + size_t cbMsg = MBLKL(pMsg); + if (cbMsg < sizeof(RTNETETHERHDR)) + return false; + + int rc = RTSemFastMutexRequest(pThis->u.s.hFastMtx); + AssertRCReturn(rc, rc); + + PVBOXNETFLTPACKETID pPrev = NULL; + PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead; + bool fIsOurPacket = false; + while (pCur) + { + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr; + if ( pCur->cbPacket != cbMsg + || pCur->SrcMac.au8[0] != pEthHdr->SrcMac.au8[0] + || pCur->SrcMac.au8[1] != pEthHdr->SrcMac.au8[1] + || pCur->SrcMac.au8[2] != pEthHdr->SrcMac.au8[2] + || pCur->SrcMac.au8[3] != pEthHdr->SrcMac.au8[3] + || pCur->SrcMac.au8[4] != pEthHdr->SrcMac.au8[4] + || pCur->SrcMac.au8[5] != pEthHdr->SrcMac.au8[5] + || pCur->DstMac.au8[0] != pEthHdr->DstMac.au8[0] + || pCur->DstMac.au8[1] != pEthHdr->DstMac.au8[1] + || pCur->DstMac.au8[2] != pEthHdr->DstMac.au8[2] + || pCur->DstMac.au8[3] != pEthHdr->DstMac.au8[3] + || pCur->DstMac.au8[4] != pEthHdr->DstMac.au8[4] + || pCur->DstMac.au8[5] != pEthHdr->DstMac.au8[5]) + { + pPrev = pCur; + pCur = pCur->pNext; + continue; + } + + uint16_t Checksum = RTCrc32(pMsg->b_rptr, cbMsg); + if (pCur->Checksum != Checksum) + { + pPrev = pCur; + pCur = pCur->pNext; + continue; + } + + /* + * Yes, it really is our own packet, mark it as handled + * and move it as a "free slot" to the head and return success. + */ + pCur->cbPacket = 0; + if (pPrev) + { + if (!pCur->pNext) + pPromiscStream->pTail = pPrev; + + pPrev->pNext = pCur->pNext; + pCur->pNext = pPromiscStream->pHead; + pPromiscStream->pHead = pCur; + } + fIsOurPacket = true; + + LogFlow((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum, + pPromiscStream->cLoopback)); + break; + } + + RTSemFastMutexRelease(pThis->u.s.hFastMtx); + return fIsOurPacket; +} + + +/** + * Worker for routing messages from the wire or from the host. + * + * @returns VBox status code. + * @param pThis The instance. + * @param pStream Pointer to the stream. + * @param pQueue Pointer to the read queue. + * @param pOrigMsg Pointer to the message. + */ +static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pMsg=%p\n", pThis, pMsg)); + + AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR)); + Assert(pStream->Type == kPromiscStream); + + vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream); + if (RT_UNLIKELY(!pPromiscStream)) + { + LogRel((DEVICE_NAME ":Promiscuous stream missing!! Failing to receive packet.\n")); + return VERR_INVALID_POINTER; + } + + /* + * Don't loopback packets we transmit to the wire. + */ + /** @todo maybe we need not check for loopback for INTNETTRUNKDIR_HOST case? */ + if (vboxNetFltSolarisIsOurMBlk(pThis, pPromiscStream, pMsg)) + { + LogFlow((DEVICE_NAME ":Avoiding packet loopback.\n")); + return VINF_SUCCESS; + } + + /* + * Figure out the source of the packet based on the source Mac address. + */ + uint32_t fSrc = INTNETTRUNKDIR_WIRE; + PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr; + if (vboxNetFltPortOsIsHostMac(pThis, &pEthHdr->SrcMac)) + fSrc = INTNETTRUNKDIR_HOST; + + /* + * Afaik; we no longer need to worry about incorrect checksums because we now use + * a dedicated stream and don't intercept packets under IP/ARP which might be doing + * checksum offloading. + */ +#if 0 + if (fSrc & INTNETTRUNKDIR_HOST) + { + mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg); + if (pCorrectedMsg) + pMsg = pCorrectedMsg; + } + vboxNetFltSolarisAnalyzeMBlk(pMsg); +#endif + + /* + * Route all received packets into the internal network. + */ + unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg); + PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs])); + int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc); + if (RT_SUCCESS(rc)) + pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc); + else + LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc)); + + return VINF_SUCCESS; +} + + +/** + * Finalize the message to be fed into the internal network. + * Verifies and tries to fix checksums for TCP, UDP and IP. + * + * @returns Corrected message or NULL if no change was required. + * @param pMsg Pointer to the message block. + * This must not be DLPI linked messages, must be M_DATA. + * + * @remarks If this function returns a checksum adjusted message, the + * passed in input message has been freed and should not be + * referenced anymore by the caller. + */ +static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisFixChecksums pMsg=%p\n")); + + Assert(DB_TYPE(pMsg) == M_DATA); + + if (MBLKL(pMsg) < sizeof(RTNETETHERHDR)) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums Packet shorter than ethernet header size!\n")); + return NULL; + } + + PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr; + if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)) + { + /* + * Check if we have a complete packet or being fed a chain. + */ + size_t cbIpPacket = 0; + mblk_t *pFullMsg = NULL; + if (pMsg->b_cont) + { + LogFlow((DEVICE_NAME ":Chained mblk_t.\n")); + + /* + * Handle chain by making a packet copy to verify if the IP checksum is correct. + * Contributions to calculating IP checksums from a chained message block with + * odd/non-pulled up sizes are welcome. + */ + size_t cbFullMsg = msgdsize(pMsg); + mblk_t *pFullMsg = allocb(cbFullMsg, BPRI_MED); + LogFlow((DEVICE_NAME ":msgdsize returns %d\n", cbFullMsg)); + if (RT_UNLIKELY(!pFullMsg)) + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg)); + return NULL; + } + + for (mblk_t *pTmp = pMsg; pTmp; pTmp = pTmp->b_cont) + { + if (DB_TYPE(pTmp) == M_DATA) + { + bcopy(pTmp->b_rptr, pFullMsg->b_wptr, MBLKL(pTmp)); + pFullMsg->b_wptr += MBLKL(pTmp); + } + } + + DB_TYPE(pFullMsg) = M_DATA; + pEthHdr = (PRTNETETHERHDR)pFullMsg->b_rptr; + cbIpPacket = MBLKL(pFullMsg) - sizeof(RTNETETHERHDR); + } + else + cbIpPacket = MBLKL(pMsg) - sizeof(RTNETETHERHDR); + + /* + * Check if the IP checksum is valid. + */ + uint8_t *pbProtocol = (uint8_t *)(pEthHdr + 1); + PRTNETIPV4 pIpHdr = (PRTNETIPV4)pbProtocol; + size_t cbPayload = cbIpPacket - (pIpHdr->ip_hl << 2); + bool fChecksumAdjusted = false; + if (RTNetIPv4IsHdrValid(pIpHdr, cbPayload, cbPayload)) + { + pbProtocol += (pIpHdr->ip_hl << 2); + + /* + * Fix up TCP/UDP and IP checksums if they're incomplete/invalid. + */ + if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP) + { + PRTNETTCP pTcpHdr = (PRTNETTCP)pbProtocol; + uint16_t TcpChecksum = RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL); + if (pTcpHdr->th_sum != TcpChecksum) + { + pTcpHdr->th_sum = TcpChecksum; + fChecksumAdjusted = true; + LogFlow((DEVICE_NAME ":fixed TCP checksum.\n")); + } + } + else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP) + { + PRTNETUDP pUdpHdr = (PRTNETUDP)pbProtocol; + uint16_t UdpChecksum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1); + + if (pUdpHdr->uh_sum != UdpChecksum) + { + pUdpHdr->uh_sum = UdpChecksum; + fChecksumAdjusted = true; + LogFlow((DEVICE_NAME ":Fixed UDP checksum.")); + } + } + } + + if (fChecksumAdjusted) + { + /* + * If we made a copy and the checksum is corrected on the copy, + * free the original, return the checksum fixed copy. + */ + if (pFullMsg) + { + freemsg(pMsg); + return pFullMsg; + } + + return pMsg; + } + + /* + * If we made a copy and the checksum is NOT corrected, free the copy, + * and return NULL. + */ + if (pFullMsg) + freemsg(pFullMsg); + + return NULL; + } + + return NULL; +} + + +/** + * Simple packet dump, used for internal debugging. + * + * @param pMsg Pointer to the message to analyze and dump. + */ +static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg) +{ + LogFlow((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg)); + + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr; + uint8_t *pb = pMsg->b_rptr; + if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)) + { + PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1); + size_t cbLen = MBLKL(pMsg) - sizeof(*pEthHdr); + if (!pMsg->b_cont) + { + if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP) + LogFlow((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12)))); + else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP) + LogFlow((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6)); + else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP) + { + PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl); + if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67 + && RT_BE2H_U16(pUdpHdr->uh_dport) == 68) + { + LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6, + RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1)))); + } + } + } + else + { + LogFlow((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n")); + } + } + else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN)) + { + typedef struct VLANHEADER + { + int Pcp:3; + int Cfi:1; + int Vid:12; + } VLANHEADER; + + VLANHEADER *pVlanHdr = (VLANHEADER *)(pMsg->b_rptr + sizeof(RTNETETHERHDR)); + LogFlow((DEVICE_NAME ":VLAN Pcp=%d Cfi=%d Id=%d\n", pVlanHdr->Pcp, pVlanHdr->Cfi, pVlanHdr->Vid >> 4)); + LogFlow((DEVICE_NAME "%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); + } + else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP)) + { + PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1); + LogFlow((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper)); + } + else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)) + { + LogFlow((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6)); + } + else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1) + || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2) + || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3)) + { + LogFlow((DEVICE_NAME ":IPX packet.\n")); + } + else + { + LogFlow((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac, + &pEthHdr->SrcMac)); + /* LogFlow((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */ + } +} + + +/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */ +bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis) +{ + /* + * There is no easy way of obtaining the global host side promiscuous counter. + * Currently we just return false. + */ + return false; +} + + +void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac) +{ + LogFlow((DEVICE_NAME ":vboxNetFltPortOsGetMacAddress pThis=%p\n", pThis)); + *pMac = pThis->u.s.Mac; +} + + +bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac) +{ + /* + * MAC address change acknowledgements are intercepted on the read side + * hence theoritically we are always update to date with any changes. + */ + return pThis->u.s.Mac.au16[0] == pMac->au16[0] + && pThis->u.s.Mac.au16[1] == pMac->au16[1] + && pThis->u.s.Mac.au16[2] == pMac->au16[2]; +} + + +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive) +{ + LogFlow((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive)); + + /* + * Enable/disable promiscuous mode. + */ + vboxnetflt_stream_t *pStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream); + if (pStream) + { + if (pStream->pReadQueue) + { + int rc = vboxNetFltSolarisPromiscReq(pStream->pReadQueue, fActive); + if (RT_FAILURE(rc)) + LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive failed to request promiscuous mode! rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive queue not found!\n")); + } + else + LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive stream not found!\n")); +} + + +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis) +{ + LogFlow((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis)); + + vboxNetFltSolarisDetachFromInterface(pThis); + + if (pThis->u.s.hFastMtx != NIL_RTSEMFASTMUTEX) + { + RTSemFastMutexDestroy(pThis->u.s.hFastMtx); + pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX; + } + return VINF_SUCCESS; +} + + +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis) +{ + /* Nothing to do here. */ + return VINF_SUCCESS; +} + + +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis) +{ + LogFlow((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis)); + /* Nothing to do here. */ +} + + +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis) +{ + LogFlow((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n")); + + /* + * Mutex used for loopback lockouts. + */ + int rc = RTSemFastMutexCreate(&pThis->u.s.hFastMtx); + if (RT_SUCCESS(rc)) + { + rc = vboxNetFltSolarisAttachToInterface(pThis); + if (RT_SUCCESS(rc)) + return rc; + + LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface failed. rc=%Rrc\n", rc)); + RTSemFastMutexDestroy(pThis->u.s.hFastMtx); + pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX; + } + else + LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to create mutex. rc=%Rrc\n", rc)); + + return rc; +} + + +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis) +{ + /* + * Init. the solaris specific data. + */ + pThis->u.s.pvIp4Stream = NULL; + pThis->u.s.pvIp6Stream = NULL; + pThis->u.s.pvArpStream = NULL; + pThis->u.s.pvPromiscStream = NULL; + pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX; + bzero(&pThis->u.s.Mac, sizeof(pThis->u.s.Mac)); + return VINF_SUCCESS; +} + + +bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + /* + * We don't support interface rediscovery on Solaris hosts because the + * filter is very tightly bound to the stream. + */ + return false; +} + + +int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst) +{ + LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst)); + + int rc = VINF_SUCCESS; + if (fDst & INTNETTRUNKDIR_WIRE) + { +#ifdef VBOXNETFLT_SOLARIS_USE_NETINFO + /* + * @todo try find a way for IPFilter to accept ethernet frames (currently silently drops them). + */ + mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst); + if (RT_LIKELY(pMsg)) + { + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr; + unsigned uProtocol; + if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)) + uProtocol = AF_INET6; + else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)) + uProtocol = AF_INET; + + /* + * Queue out using netinfo. + */ + netstack_t *pNetStack = netstack_get_current(); + if (pNetStack) + { + net_data_t pNetData = net_lookup_impl(NHF_INET, pNetStack); + if (pNetData) + { + phy_if_t pInterface = net_phylookup(pNetData, pThis->szName); + if (pInterface) + { + net_inject_t InjectData; + InjectData.ni_packet = pMsg; + InjectData.ni_physical = pInterface; + bzero(&InjectData.ni_addr, sizeof(InjectData.ni_addr)); + InjectData.ni_addr.ss_family = uProtocol; + + /* + * Queue out rather than direct out transmission. + */ + int rc = net_inject(pNetData, NI_QUEUE_OUT, &InjectData); + if (!rc) + rc = VINF_SUCCESS; + else + { + LogRel((DEVICE_NAME ":queuing IP packet for transmission failed. rc=%d\n", rc)); + rc = VERR_NET_IO_ERROR; + } + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to lookup physical interface.\n")); + rc = VERR_NET_IO_ERROR; + } + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to get IP hooks.\n")); + rc = VERR_NET_IO_ERROR; + } + netstack_rele(pNetStack); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to get current net stack.\n")); + rc = VERR_NET_IO_ERROR; + } + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n")); + rc = VERR_NO_MEMORY; + } +#else + vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream); + if (RT_LIKELY(pPromiscStream)) + { + mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst); + if (RT_LIKELY(pMsg)) + { + LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n")); + + vboxNetFltSolarisQueueLoopback(pThis, pPromiscStream, pMsg); + putnext(WR(pPromiscStream->Stream.pReadQueue), pMsg); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n")); + rc = VERR_NO_MEMORY; + } + } +#endif + } + + if (fDst & INTNETTRUNKDIR_HOST) + { + /* + * For unplumbed interfaces we would not be bound to IP or ARP. + * We either bind to both or neither; so atomic reading one should be sufficient. + */ + vboxnetflt_stream_t *pIp4Stream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvIp4Stream); + if (!pIp4Stream) + return rc; + + /* + * Create a message block and send it up the host stack (upstream). + */ + mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst); + if (RT_LIKELY(pMsg)) + { + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr; + + /* + * Send message up ARP stream. + */ + if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP)) + { + LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n")); + + vboxnetflt_stream_t *pArpStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvArpStream); + if (pArpStream) + { + /* + * Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path. + */ + mblk_t *pDlpiMsg; + int rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg); + if (RT_SUCCESS(rc)) + { + pMsg = pDlpiMsg; + + queue_t *pArpReadQueue = pArpStream->pReadQueue; + putnext(pArpReadQueue, pMsg); + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed!\n")); + freemsg(pMsg); + rc = VERR_NO_MEMORY; + } + } + else + freemsg(pMsg); /* Should really never happen... */ + } + else + { + vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvIp6Stream); + if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6) + && pIp6Stream) + { + /* + * Send messages up IPv6 stream. + */ + LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv6\n")); + + pMsg->b_rptr += sizeof(RTNETETHERHDR); + queue_t *pIp6ReadQueue = pIp6Stream->pReadQueue; + putnext(pIp6ReadQueue, pMsg); + } + else + { + /* + * Send messages up IPv4 stream. + */ + LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv4\n")); + + pMsg->b_rptr += sizeof(RTNETETHERHDR); + queue_t *pIp4ReadQueue = pIp4Stream->pReadQueue; + putnext(pIp4ReadQueue, pMsg); + } + } + } + else + { + LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed.\n")); + rc = VERR_NO_MEMORY; + } + } + + return rc; +} + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf new file mode 100644 index 000000000..f00841d3e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf @@ -0,0 +1,25 @@ +# +# Sun xVM VirtualBox +# Solaris Host VBoxFlt Configuration +# +# Copyright (C) 2008 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +# This needs to go into /platform/i86pc/kernel/drv, +# while the 64-bit driver object goes into the amd64 +# subdirectory (32-bit drivers goes into the same +# directory). +# +name="vboxflt" parent="pseudo" instance=0; diff --git a/src/VBox/HostDrivers/VBoxTAP/Makefile.kmk b/src/VBox/HostDrivers/VBoxTAP/Makefile.kmk new file mode 100644 index 000000000..6b4f99a60 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/Makefile.kmk @@ -0,0 +1,83 @@ +# $Id: Makefile.kmk 12252 2008-09-09 01:58:29Z vboxsync $ +## @file +# Sub-Makefile for the Windows Network Driver. +# + +# +# Copyright (C) 2006-2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + + +# +# VBoxTap +# +SYSMODS.win += VBoxTAP +VBoxTAP_TEMPLATE = VBOXR0DRV +ifdef VBOX_SIGNING_MODE + VBoxTAP_NOINST = true +endif +VBoxTAP_SDKS = W2K3DDK WINPSDKINCS +VBoxTAP_LDFLAGS.x86 = -Entry:DriverEntry@8 +VBoxTAP_LDFLAGS.amd64 = -Entry:DriverEntry +VBoxTAP_SOURCES = \ + tapdrvr.c \ + VBoxTAP.rc +VBoxTAP_LIBS = \ + $(PATH_LIB)/RuntimeR0$(VBOX_SUFF_LIB) \ + $(PATH_SDK_W2K3DDK_LIB)/ntoskrnl.lib \ + $(PATH_SDK_W2K3DDK_LIB)/hal.lib \ + $(PATH_SDK_W2K3DDK_LIB)/ndis.lib \ + $(PATH_SDK_W2K3DDK_LIB)/ntstrsafe.lib \ + $(PATH_SDK_W2K3DDK_LIB)/BufferOverflowK.lib + + +# +# Install the .inf. +# +INSTALLS.win += VBoxTAP-inf +VBoxTAP-inf_INST = $(INST_BIN) +VBoxTAP-inf_MODE = a+r,u+w +VBoxTAP-inf_SOURCES = \ + $(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.inf +VBoxTAP-inf_CLEAN = $(VBoxTAP-inf_SOURCES) +VBoxTAP-inf_BLDDIRS = $(PATH_TARGET)/VBoxTAPCat.dir + +$(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.inf: $(PATH_SUB_CURRENT)/VBoxTAP.inf $(MAKEFILE_CURRENT) | $$(call DIRDEP,$$(@D)) + $(call MSG_GENERATE,VBoxTAP-inf,$@,$<) + $(call VBOX_EDIT_INF_FN,$<,$@) + +ifdef VBOX_SIGNING_MODE +VBoxTAP-inf_SOURCES += \ + $(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.cat \ + $(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.sys + +$(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.sys: $$(TARGET_VBoxTAP) | $$(call DIRDEP,$$(@D)) + $(INSTALL) -m 644 $< $(@D) + +$(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.cat: \ + $(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.inf \ + $(PATH_TARGET)/VBoxTAPCat.dir/VBoxTAP.sys + $(call MSG_TOOL,Inf2Cat,VBoxTAP-inf,$@,$<) + $(INSTALL) -m 644 $(TARGET_VBoxTAP) $(@D) + $(call VBOX_MAKE_CAT_FN, $(@D),$@) +endif # signing + + +# generate rules +include $(KBUILD_PATH)/subfooter.kmk + diff --git a/src/VBox/HostDrivers/VBoxTAP/VBoxTAP.inf b/src/VBox/HostDrivers/VBoxTAP/VBoxTAP.inf new file mode 100644 index 000000000..4dd5c25f1 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/VBoxTAP.inf @@ -0,0 +1,164 @@ +; +; VirtualBox Host Interface Networking Driver +; +; +; Copyright (C) 2006-2007 Sun Microsystems, Inc. +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +; Clara, CA 95054 USA or visit http://www.sun.com if you need +; additional information or have any questions. +; + +[Version] + Signature = "$Windows NT$" +;cat CatalogFile = VBoxTAP.cat + ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} + Provider = %Provider% + Class = Net + +; This version number should match the version +; number given in SOURCES. +;; @todo: edit this too? what's the above comment about? + DriverVer=02/19/2007,8.00.00.0005 + +[Strings] +; Note; there are hardcoded checks for these strings!! + DeviceDescription = "VirtualBox TAP Adapter" + Provider = "Sun Microsystems, Inc." + +;---------------------------------------------------------------- +; Manufacturer + Product Section (Done) +;---------------------------------------------------------------- +[Manufacturer] +;x86 %Provider% = VBoxTAP +;amd64 %Provider% = VBoxTAP, NTamd64 + +;x86 [VBoxTAP] +;amd64 [VBoxTAP.NTamd64] + %DeviceDescription% = VBoxTAP.ndi, VBoxTAP + +;--------------------------------------------------------------- +; Driver Section (Done) +;--------------------------------------------------------------- + +;----------------- Characteristics ------------ +; NCF_PHYSICAL = 0x04 +; NCF_VIRTUAL = 0x01 +; NCF_SOFTWARE_ENUMERATED = 0x02 +; NCF_HIDDEN = 0x08 +; NCF_NO_SERVICE = 0x10 +; NCF_HAS_UI = 0x80 +;----------------- Characteristics ------------ + +[VBoxTAP.ndi] + CopyFiles = VBoxTAP.driver, VBoxTAP.files + AddReg = VBoxTAP.reg + AddReg = VBoxTAP.params.reg + Characteristics = 0x81 ; NCF_PHYSICAL | NCF_HAS_UI + BusType = 1 + +[VBoxTAP.ndi.Services] + AddService = VBoxTAP, 2, VBoxTAP.service + +[VBoxTAP.reg] + HKR, Ndi, Service, 0, "VBoxTAP" + HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" + HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + HKR, , Manufacturer, 0, "%Provider%" + HKR, , ProductName, 0, "%DeviceDescription%" + +[VBoxTAP.params.reg] + HKR, Ndi\params\MTU, ParamDesc, 0, "MTU" + HKR, Ndi\params\MTU, Type, 0, "int" + HKR, Ndi\params\MTU, Default, 0, "1500" + HKR, Ndi\params\MTU, Optional, 0, "0" + HKR, Ndi\params\MTU, Min, 0, "100" + HKR, Ndi\params\MTU, Max, 0, "16384" + HKR, Ndi\params\MTU, Step, 0, "1" + HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status" + HKR, Ndi\params\MediaStatus, Type, 0, "enum" + HKR, Ndi\params\MediaStatus, Default, 0, "0" + HKR, Ndi\params\MediaStatus, Optional, 0, "0" + HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled" + HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected" + HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address" + HKR, Ndi\params\MAC, Type, 0, "edit" + HKR, Ndi\params\MAC, Optional, 0, "1" + HKR, Ndi\params\AllowNonAdmin, ParamDesc, 0, "Non-Admin Access" + HKR, Ndi\params\AllowNonAdmin, Type, 0, "enum" + HKR, Ndi\params\AllowNonAdmin, Default, 0, "1" + HKR, Ndi\params\AllowNonAdmin, Optional, 0, "0" + HKR, Ndi\params\AllowNonAdmin\enum, "0", 0, "Not Allowed" + HKR, Ndi\params\AllowNonAdmin\enum, "1", 0, "Allowed" + + +;---------------------------------------------------------------- +; Service Section +;---------------------------------------------------------------- + +;---------- Service Type ------------- +; SERVICE_KERNEL_DRIVER = 0x01 +; SERVICE_WIN32_OWN_PROCESS = 0x10 +;---------- Service Type ------------- + +;---------- Start Mode --------------- +; SERVICE_BOOT_START = 0x0 +; SERVICE_SYSTEM_START = 0x1 +; SERVICE_AUTO_START = 0x2 +; SERVICE_DEMAND_START = 0x3 +; SERVICE_DISABLED = 0x4 +;---------- Start Mode --------------- + +[VBoxTAP.service] + DisplayName = %DeviceDescription% + ServiceType = 1 + StartType = 3 + ErrorControl = 1 + LoadOrderGroup = NDIS + ServiceBinary = %12%\VBoxTAP.sys + +;----------------------------------------------------------------- +; File Installation +;----------------------------------------------------------------- + +;----------------- Copy Flags ------------ +; COPYFLG_NOSKIP = 0x02 +; COPYFLG_NOVERSIONCHECK = 0x04 +;----------------- Copy Flags ------------ + +; SourceDisksNames +; diskid = description[, [tagfile] [, <unused>, subdir]] +; 1 = "Intel Driver Disk 1",e100bex.sys,, + +[SourceDisksNames] + 1 = %DeviceDescription%, VBoxTAP.sys + +; SourceDisksFiles +; filename_on_source = diskID[, [subdir][, size]] +; e100bex.sys = 1,, ; on distribution disk 1 + +[SourceDisksFiles] +VBoxTAP.sys = 1 + +[DestinationDirs] + VBoxTAP.files = 11 + VBoxTAP.driver = 12 + +[VBoxTAP.files] +; TapPanel.cpl,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK +; cipsrvr.exe,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK + +[VBoxTAP.driver] + VBoxTAP.sys,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK + +;--------------------------------------------------------------- +; End +;--------------------------------------------------------------- diff --git a/src/VBox/HostDrivers/VBoxTAP/common.h b/src/VBox/HostDrivers/VBoxTAP/common.h new file mode 100644 index 000000000..d782043c3 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/common.h @@ -0,0 +1,77 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=============================================== +// This file is included both by OpenVPN and +// the TAP-Win32 driver and contains definitions +// common to both. +//=============================================== + +//============= +// TAP IOCTLs +//============= + +#ifndef VBOX +#define TAP_CONTROL_CODE(request,method) \ + CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) + +#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) +#endif + +//================= +// Registry keys +//================= + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +//====================== +// Filesystem prefixes +//====================== + +#define USERMODEDEVICEDIR "\\\\.\\Global\\" +#define SYSDEVICEDIR "\\Device\\" +#define USERDEVICEDIR "\\DosDevices\\Global\\" +#define TAPSUFFIX ".tap" + +//========================================================= +// TAP_COMPONENT_ID -- This string defines the TAP driver +// type -- different component IDs can reside in the system +// simultaneously. +//========================================================= + +#define TAP_COMPONENT_ID "VBoxTAP" diff --git a/src/VBox/HostDrivers/VBoxTAP/config-win32.h b/src/VBox/HostDrivers/VBoxTAP/config-win32.h new file mode 100644 index 000000000..1c9eb77cf --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/config-win32.h @@ -0,0 +1,309 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Configuration header for Win32 using the mingw environment. + * Manually edited based on linux version as generated by autoconf. + * + * config-win32.h is normally generated by copying + * config-win32.h.in -> config-win32.h and replacing + * [ampersand] VERSION [ampersand] + * with the appropriate version #. This is normally + * done automatically by configure.ac + */ + +#include <windows.h> +#include <winsock2.h> + +#define sleep(x) Sleep((x)*1000) + +#define random rand +#define srandom srand + +typedef unsigned long in_addr_t; + +#ifndef _SSIZE_T_ +#define _SSIZE_T_ + typedef unsigned int ssize_t; +#endif + +/* Append a label to program startup title */ +//#define DEBUG_LABEL "DEBUG1" + +/* Should we print debug info from driver? */ +//#define TAP_WIN32_DEBUG + +/* + * Minimum TAP-Win32 version number expected by userspace + * + * The TAP-Win32 version number is defined in tap-win32/SOURCES + */ +#define TAP_WIN32_MIN_MAJOR 8 +#define TAP_WIN32_MIN_MINOR 4 + +/* Allow --askpass and --auth-user-pass passwords to be read from a file */ +/* #undef ENABLE_PASSWORD_SAVE */ + +/* Enable client/server capability */ +#define ENABLE_CLIENT_SERVER 1 + +/* Enable client capability only */ +/* #undef ENABLE_CLIENT_ONLY */ + +/* Enable management server capability */ +#define ENABLE_MANAGEMENT 1 + +/* Enable HTTP proxy support */ +#define ENABLE_HTTP_PROXY 1 + +/* Enable Socks proxy support */ +#define ENABLE_SOCKS 1 + +/* Enable internal fragmentation support */ +#define ENABLE_FRAGMENT 1 + +/* Enable smaller executable size */ +/* #undef ENABLE_SMALL */ + +/* Enable debugging support */ +#define ENABLE_DEBUG 1 + +/* if defined, will allow usage of the --plugin directive */ +#define USE_LOAD_LIBRARY + +/* Dimension size to use for empty array declaration */ +#define EMPTY_ARRAY_SIZE 0 + +/* Define to 1 if you have the <openssl/engine.h> header file. */ +#define HAVE_OPENSSL_ENGINE_H 1 + +/* Define to 1 if you have the `ENGINE_load_builtin_engines' function. */ +#define HAVE_ENGINE_LOAD_BUILTIN_ENGINES 1 + +/* Define to 1 if you have the `ENGINE_register_all_complete' function. */ +#define HAVE_ENGINE_REGISTER_ALL_COMPLETE 1 + +/* Define to 1 if you have the `ENGINE_cleanup' function. */ +#define HAVE_ENGINE_CLEANUP 1 + +/* gettimeofday() is implemented in otime.c for Windows */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the 'chsize' function. */ +#define HAVE_CHSIZE 1 + +/* Define to 1 if you have the `chdir' function. */ +#define HAVE_CHDIR 1 + +/* Define to 1 if your compiler supports GNU GCC-style variadic macros */ +#ifndef _MSC_VER /* Defines MSFT compiler version. Defined as 1200 for MSVC++ 6.0. */ +#define HAVE_CPP_VARARG_MACRO_GCC 1 +#endif + +/* Define to 1 if you have the <ctype.h> header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `EVP_CIPHER_CTX_set_key_length' function. */ +#define HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getsockopt' function. */ +#define HAVE_GETSOCKOPT 1 + +/* Define to 1 if you have the `inet_ntoa' function. */ +#define HAVE_INET_NTOA 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `setsockopt' function. */ +#define HAVE_SETSOCKOPT 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#ifndef _MSC_VER +#define HAVE_STDINT_H 1 +#endif + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `system' function. */ +#define HAVE_SYSTEM 1 + +/* Define to 1 if you have the <sys/file.h> header file. */ +#ifndef _MSC_VER +#define HAVE_SYS_FILE_H 1 +#endif + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#ifndef _MSC_VER +#define HAVE_SYS_TIME_H 1 +#endif + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `time' function. */ +#define HAVE_TIME 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#ifndef _MSC_VER +#define HAVE_UNISTD_H 1 +#endif + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Special Windows version of getpass() defined in io.c */ +#define HAVE_GETPASS 1 + +/* Name of package */ +#define PACKAGE "openvpn" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "openvpn-users@lists.sourceforge.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "OpenVPN" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "openvpn" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "@VERSION@" /* AUTO_VERSION */ + +/* Define to the full name and version of this package. */ +#ifdef DEBUG_LABEL +#define PACKAGE_STRING "OpenVPN " PACKAGE_VERSION " " DEBUG_LABEL +#else +#define PACKAGE_STRING "OpenVPN " PACKAGE_VERSION +#endif + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* The size of a `unsigned int', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_INT 4 + +/* The size of a `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* A string representing our target */ +#ifdef _MSC_VER +#define TARGET_ALIAS "Win32-MSVC++" +#else +#define TARGET_ALIAS "Win32-MinGW" +#endif + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#ifndef _MSC_VER +#define TIME_WITH_SYS_TIME 1 +#endif + +/* Use OpenSSL crypto library */ +#define USE_CRYPTO 1 + +/* Use LZO compression library */ +#define USE_LZO 1 + +/* Use OpenSSL SSL library */ +#define USE_SSL 1 + +/* Version number of package */ +#define VERSION PACKAGE_VERSION + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#define inline __inline + +/* type to use in place of socklen_t if not defined */ +#define socklen_t unsigned int + +/* 32-bit unsigned type */ +#define uint32_t unsigned int + +/* 16-bit unsigned type */ +#define uint16_t unsigned short + +/* 8-bit unsigned type */ +#define uint8_t unsigned char + +/* Route command */ +#define ROUTE_PATH "route" + +/* Windows doesn't support PTHREAD yet */ +#ifdef USE_PTHREAD +#error The Windows version of OpenVPN does not support PTHREAD yet +#endif + +#ifdef _MSC_VER +/* MSVC++ hacks */ +#include <io.h> +#include <direct.h> +#define vsnprintf _vsnprintf +#define vsnwprintf _vsnwprintf +#define snwprintf _snwprintf +#define write _write +#define open _open +#define read _read +#define close _close +#define chdir _chdir +#define S_IRUSR 0 +#define S_IWUSR 0 +typedef int intptr_t; +#undef S_NORMAL +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/constants.h b/src/VBox/HostDrivers/VBoxTAP/constants.h new file mode 100644 index 000000000..a014971ca --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/constants.h @@ -0,0 +1,55 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//==================================================================== +// Product and Version public settings +//==================================================================== + +#define PRODUCT_STRING "VirtualBox TAP Adapter" + +#define TAP_NDIS_MAJOR_VERSION 5 +#define TAP_NDIS_MINOR_VERSION 0 + +//=========================================================== +// Driver constants +//=========================================================== + +#define ETHERNET_HEADER_SIZE (sizeof (ETH_HEADER)) +#define ETHERNET_MTU 1500 +#define ETHERNET_PACKET_SIZE (ETHERNET_MTU + ETHERNET_HEADER_SIZE) +#define DEFAULT_PACKET_LOOKAHEAD (ETHERNET_PACKET_SIZE) + +#define NIC_MAX_MCAST_LIST 32 // Max length of multicast address list + +#define MINIMUM_MTU 576 // USE TCP Minimum MTU +#define MAXIMUM_MTU 65536 // IP maximum MTU + +#define PACKET_QUEUE_SIZE 64 // tap -> userspace queue size +#define IRP_QUEUE_SIZE 16 // max number of simultaneous i/o operations from userspace + +#define TAP_LITTLE_ENDIAN // affects ntohs, htonl, etc. functions diff --git a/src/VBox/HostDrivers/VBoxTAP/dhcp.c b/src/VBox/HostDrivers/VBoxTAP/dhcp.c new file mode 100644 index 000000000..5b8eea6eb --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/dhcp.c @@ -0,0 +1,603 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//========================= +// Code to set DHCP options +//========================= + +VOID +SetDHCPOpt (DHCPMsg *m, void *data, unsigned int len) +{ + if (!m->overflow) + { + if (m->optlen + len <= DHCP_OPTIONS_BUFFER_SIZE) + { + if (len) + { + NdisMoveMemory (m->msg.options + m->optlen, data, len); + m->optlen += len; + } + } + else + { + m->overflow = TRUE; + } + } +} + +VOID +SetDHCPOpt0 (DHCPMsg *msg, int type) +{ + DHCPOPT0 opt; + opt.type = (UCHAR) type; + SetDHCPOpt (msg, &opt, sizeof (opt)); +} + +VOID +SetDHCPOpt8 (DHCPMsg *msg, int type, ULONG data) +{ + DHCPOPT8 opt; + opt.type = (UCHAR) type; + opt.len = sizeof (opt.data); + opt.data = (UCHAR) data; + SetDHCPOpt (msg, &opt, sizeof (opt)); +} + +VOID +SetDHCPOpt32 (DHCPMsg *msg, int type, ULONG data) +{ + DHCPOPT32 opt; + opt.type = (UCHAR) type; + opt.len = sizeof (opt.data); + opt.data = data; + SetDHCPOpt (msg, &opt, sizeof (opt)); +} + +//============== +// Checksum code +//============== + +USHORT +ip_checksum (const UCHAR *buf, const int len_ip_header) +{ + USHORT word16; + ULONG sum = 0; + int i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet + // and add them up + for (i = 0; i < len_ip_header - 1; i += 2) { + word16 = ((buf[i] << 8) & 0xFF00) + (buf[i+1] & 0xFF); + sum += (ULONG) word16; + } + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // one's complement the result + return ((USHORT) ~sum); +} + +USHORT +udp_checksum (const UCHAR *buf, + const int len_udp, + const UCHAR *src_addr, + const UCHAR *dest_addr) +{ + USHORT word16; + ULONG sum = 0; + int i; + + // make 16 bit words out of every two adjacent 8 bit words and + // calculate the sum of all 16 bit words + for (i = 0; i < len_udp; i += 2){ + word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0); + sum += word16; + } + + // add the UDP pseudo header which contains the IP source and destination addresses + for (i = 0; i < 4; i += 2){ + word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF); + sum += word16; + } + for (i = 0; i < 4; i += 2){ + word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF); + sum += word16; + } + + // the protocol number and the length of the UDP packet + sum += (USHORT) IPPROTO_UDP + (USHORT) len_udp; + + // keep only the last 16 bits of the 32 bit calculated sum and add the carries + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Take the one's complement of sum + return ((USHORT) ~sum); +} + +//================================ +// Set IP and UDP packet checksums +//================================ + +VOID +SetChecksumDHCPMsg (DHCPMsg *m) +{ + // Set IP checksum + m->msg.pre.ip.check = htons (ip_checksum ((UCHAR *) &m->msg.pre.ip, sizeof (IPHDR))); + + // Set UDP Checksum + m->msg.pre.udp.check = htons (udp_checksum ((UCHAR *) &m->msg.pre.udp, + sizeof (UDPHDR) + sizeof (DHCP) + m->optlen, + (UCHAR *)&m->msg.pre.ip.saddr, + (UCHAR *)&m->msg.pre.ip.daddr)); +} + +//=================== +// DHCP message tests +//=================== + +int +GetDHCPMessageType (const DHCP *dhcp, const int optlen) +{ + const UCHAR *p = (UCHAR *) (dhcp + 1); + int i; + + for (i = 0; i < optlen; ++i) + { + const UCHAR type = p[i]; + const int room = optlen - i - 1; + if (type == DHCP_END) // didn't find what we were looking for + return -1; + else if (type == DHCP_PAD) // no-operation + ; + else if (type == DHCP_MSG_TYPE) // what we are looking for + { + if (room >= 2) + { + if (p[i+1] == 1) // message length should be 1 + return p[i+2]; // return message type + } + return -1; + } + else // some other message + { + if (room >= 1) + { + const int len = p[i+1]; // get message length + i += (len + 1); // advance to next message + } + } + } + return -1; +} + +BOOLEAN +DHCPMessageOurs (const TapAdapterPointer p_Adapter, + const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp) +{ + // Must be UDPv4 protocol + if (!(eth->proto == htons (ETH_P_IP) && ip->protocol == IPPROTO_UDP)) + return FALSE; + + // Source MAC must be our adapter + if (!MAC_EQUAL (eth->src, p_Adapter->m_MAC)) + return FALSE; + + // Dest MAC must be either broadcast or our virtual DHCP server + if (!(MAC_EQUAL (eth->dest, p_Adapter->m_MAC_Broadcast) + || MAC_EQUAL (eth->dest, p_Adapter->m_dhcp_server_mac))) + return FALSE; + + // Port numbers must be correct + if (!(udp->dest == htons (BOOTPS_PORT) + && udp->source == htons (BOOTPC_PORT))) + return FALSE; + + // Hardware address must be MAC addr sized + if (!(dhcp->hlen == sizeof (MACADDR))) + return FALSE; + + // Hardware address must match our adapter + if (!MAC_EQUAL (eth->src, dhcp->chaddr)) + return FALSE; + + return TRUE; +} + + +//===================================================== +// Build all of DHCP packet except for DHCP options. +// Assume that *p has been zeroed before we are called. +//===================================================== + +VOID +BuildDHCPPre (const TapAdapterPointer a, + DHCPPre *p, + const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp, + const int optlen, + const int type) +{ + // Should we broadcast or direct to a specific MAC / IP address? + const BOOLEAN broadcast = (type == DHCPNAK + || MAC_EQUAL (eth->dest, a->m_MAC_Broadcast)); + // Build ethernet header + + COPY_MAC (p->eth.src, a->m_dhcp_server_mac); + + if (broadcast) + COPY_MAC (p->eth.dest, a->m_MAC_Broadcast); + else + COPY_MAC (p->eth.dest, eth->src); + + p->eth.proto = htons (ETH_P_IP); + + // Build IP header + + p->ip.version_len = (4 << 4) | (sizeof (IPHDR) >> 2); + p->ip.tos = 0; + p->ip.tot_len = htons (sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen); + p->ip.id = 0; + p->ip.frag_off = 0; + p->ip.ttl = 16; + p->ip.protocol = IPPROTO_UDP; + p->ip.check = 0; + p->ip.saddr = a->m_dhcp_server_ip; + + if (broadcast) + p->ip.daddr = ~0; + else + p->ip.daddr = a->m_dhcp_addr; + + // Build UDP header + + p->udp.source = htons (BOOTPS_PORT); + p->udp.dest = htons (BOOTPC_PORT); + p->udp.len = htons (sizeof (UDPHDR) + sizeof (DHCP) + optlen); + p->udp.check = 0; + + // Build DHCP response + + p->dhcp.op = BOOTREPLY; + p->dhcp.htype = 1; + p->dhcp.hlen = sizeof (MACADDR); + p->dhcp.hops = 0; + p->dhcp.xid = dhcp->xid; + p->dhcp.secs = 0; + p->dhcp.flags = 0; + p->dhcp.ciaddr = 0; + + if (type == DHCPNAK) + p->dhcp.yiaddr = 0; + else + p->dhcp.yiaddr = a->m_dhcp_addr; + + p->dhcp.siaddr = a->m_dhcp_server_ip; + p->dhcp.giaddr = 0; + COPY_MAC (p->dhcp.chaddr, eth->src); + p->dhcp.magic = htonl (0x63825363); +} +//============================= +// Build specific DHCP messages +//============================= + +VOID +SendDHCPMsg (const TapAdapterPointer a, + const int type, + const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp) +{ + DHCPMsg *pkt; + + if (!(type == DHCPOFFER || type == DHCPACK || type == DHCPNAK)) + { + DEBUGP (("[TAP] SendDHCPMsg: Bad DHCP type: %d\n", type)); + return; + } + + pkt = (DHCPMsg *) MemAlloc (sizeof (DHCPMsg), TRUE); + + if (pkt) + { + //----------------------- + // Build DHCP options + //----------------------- + + // Message Type + SetDHCPOpt8 (pkt, DHCP_MSG_TYPE, type); + + // Server ID + SetDHCPOpt32 (pkt, DHCP_SERVER_ID, a->m_dhcp_server_ip); + + if (type == DHCPOFFER || type == DHCPACK) + { + // Lease Time + SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (a->m_dhcp_lease_time)); + + // Netmask + SetDHCPOpt32 (pkt, DHCP_NETMASK, a->m_dhcp_netmask); + + // Other user-defined options + SetDHCPOpt (pkt, + a->m_dhcp_user_supplied_options_buffer, + a->m_dhcp_user_supplied_options_buffer_len); + } + + // End + SetDHCPOpt0 (pkt, DHCP_END); + + if (!DHCPMSG_OVERFLOW (pkt)) + { + // The initial part of the DHCP message (not including options) gets built here + BuildDHCPPre (a, + &pkt->msg.pre, + eth, + ip, + udp, + dhcp, + DHCPMSG_LEN_OPT (pkt), + type); + + SetChecksumDHCPMsg (pkt); + + DUMP_PACKET ("DHCPMsg", + DHCPMSG_BUF (pkt), + DHCPMSG_LEN_FULL (pkt)); + + // Return DHCP response to kernel + InjectPacket (a, + DHCPMSG_BUF (pkt), + DHCPMSG_LEN_FULL (pkt)); + } + else + { + DEBUGP (("[TAP] SendDHCPMsg: DHCP buffer overflow\n")); + } + + MemFree (pkt, sizeof (DHCPMsg)); + } +} + +//=================================================================== +// Handle a BOOTPS packet produced by the local system to +// resolve the address/netmask of this adapter. +// If we are in TAP_IOCTL_CONFIG_DHCP_MASQ mode, reply +// to the message. Return TRUE if we processed the passed +// message, so that downstream stages can ignore it. +//=================================================================== + +BOOLEAN +ProcessDHCP (TapAdapterPointer p_Adapter, + const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp, + int optlen) +{ + int msg_type; + + // Sanity check IP header + if (!(ntohs (ip->tot_len) == sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen + && (ntohs (ip->frag_off) & IP_OFFMASK) == 0)) + return TRUE; + + // Does this message belong to us? + if (!DHCPMessageOurs (p_Adapter, eth, ip, udp, dhcp)) + return FALSE; + + msg_type = GetDHCPMessageType (dhcp, optlen); + + // Drop non-BOOTREQUEST messages + if (dhcp->op != BOOTREQUEST) + return TRUE; + + // Drop any messages except DHCPDISCOVER or DHCPREQUEST + if (!(msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST)) + return TRUE; + + // Should we reply with DHCPOFFER, DHCPACK, or DHCPNAK? + if (msg_type == DHCPREQUEST + && ((dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr) + || !p_Adapter->m_dhcp_received_discover + || p_Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD)) + SendDHCPMsg (p_Adapter, + DHCPNAK, + eth, ip, udp, dhcp); + else + SendDHCPMsg (p_Adapter, + (msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK), + eth, ip, udp, dhcp); + + // Remember if we received a DHCPDISCOVER + if (msg_type == DHCPDISCOVER) + p_Adapter->m_dhcp_received_discover = TRUE; + + // Is this a bad DHCPREQUEST? + if (msg_type == DHCPREQUEST && dhcp->ciaddr != p_Adapter->m_dhcp_addr) + ++p_Adapter->m_dhcp_bad_requests; + + return TRUE; +} + +#if DBG + +const char * +message_op_text (int op) +{ + switch (op) + { + case BOOTREQUEST: + return "BOOTREQUEST"; + case BOOTREPLY: + return "BOOTREPLY"; + default: + return "???"; + } +} + +const char * +message_type_text (int type) +{ + switch (type) + { + case DHCPDISCOVER: + return "DHCPDISCOVER"; + case DHCPOFFER: + return "DHCPOFFER"; + case DHCPREQUEST: + return "DHCPREQUEST"; + case DHCPDECLINE: + return "DHCPDECLINE"; + case DHCPACK: + return "DHCPACK"; + case DHCPNAK: + return "DHCPNAK"; + case DHCPRELEASE: + return "DHCPRELEASE"; + case DHCPINFORM: + return "DHCPINFORM"; + default: + return "???"; + } +} + +const char * +port_name (int port) +{ + switch (port) + { + case BOOTPS_PORT: + return "BOOTPS"; + case BOOTPC_PORT: + return "BOOTPC"; + default: + return "unknown"; + } +} + +VOID +DumpDHCP (const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp, + const int optlen) +{ + DEBUGP ((" %s", message_op_text (dhcp->op))); + DEBUGP ((" %s ", message_type_text (GetDHCPMessageType (dhcp, optlen)))); + PrIP (ip->saddr); + DEBUGP ((":%s[", port_name (ntohs (udp->source)))); + PrMac (eth->src); + DEBUGP (("] -> ")); + PrIP (ip->daddr); + DEBUGP ((":%s[", port_name (ntohs (udp->dest)))); + PrMac (eth->dest); + DEBUGP (("]")); + if (dhcp->ciaddr) + { + DEBUGP ((" ci=")); + PrIP (dhcp->ciaddr); + } + if (dhcp->yiaddr) + { + DEBUGP ((" yi=")); + PrIP (dhcp->yiaddr); + } + if (dhcp->siaddr) + { + DEBUGP ((" si=")); + PrIP (dhcp->siaddr); + } + if (dhcp->hlen == sizeof (MACADDR)) + { + DEBUGP ((" ch=")); + PrMac (dhcp->chaddr); + } + + DEBUGP ((" xid=0x%08x", ntohl (dhcp->xid))); + + if (ntohl (dhcp->magic) != 0x63825363) + DEBUGP ((" ma=0x%08x", ntohl (dhcp->magic))); + if (dhcp->htype != 1) + DEBUGP ((" htype=%d", dhcp->htype)); + if (dhcp->hops) + DEBUGP ((" hops=%d", dhcp->hops)); + if (ntohs (dhcp->secs)) + DEBUGP ((" secs=%d", ntohs (dhcp->secs))); + if (ntohs (dhcp->flags)) + DEBUGP ((" flags=0x%04x", ntohs (dhcp->flags))); + + // extra stuff + + if (ip->version_len != 0x45) + DEBUGP ((" vl=0x%02x", ip->version_len)); + if (ntohs (ip->tot_len) != sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen) + DEBUGP ((" tl=%d", ntohs (ip->tot_len))); + if (ntohs (udp->len) != sizeof (UDPHDR) + sizeof (DHCP) + optlen) + DEBUGP ((" ul=%d", ntohs (udp->len))); + + if (ip->tos) + DEBUGP ((" tos=0x%02x", ip->tos)); + if (ntohs (ip->id)) + DEBUGP ((" id=0x%04x", ntohs (ip->id))); + if (ntohs (ip->frag_off)) + DEBUGP ((" frag_off=0x%04x", ntohs (ip->frag_off))); + + DEBUGP ((" ttl=%d", ip->ttl)); + DEBUGP ((" ic=0x%04x [0x%04x]", ntohs (ip->check), + ip_checksum ((UCHAR*)ip, sizeof (IPHDR)))); + DEBUGP ((" uc=0x%04x [0x%04x/%d]", ntohs (udp->check), + udp_checksum ((UCHAR *) udp, + sizeof (UDPHDR) + sizeof (DHCP) + optlen, + (UCHAR *) &ip->saddr, + (UCHAR *) &ip->daddr), + optlen)); + + // Options + { + const UCHAR *opt = (UCHAR *) (dhcp + 1); + int i; + + DEBUGP ((" OPT")); + for (i = 0; i < optlen; ++i) + { + const UCHAR data = opt[i]; + DEBUGP ((".%d", data)); + } + } +} + +#endif /* DEBUG */ diff --git a/src/VBox/HostDrivers/VBoxTAP/dhcp.h b/src/VBox/HostDrivers/VBoxTAP/dhcp.h new file mode 100644 index 000000000..f99973080 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/dhcp.h @@ -0,0 +1,168 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma pack(1) + +//=================================================== +// How many bad DHCPREQUESTs do we receive before we +// return a NAK? +// +// A bad DHCPREQUEST is defined to be one where the +// requestor doesn't know its IP address. +//=================================================== + +#define BAD_DHCPREQUEST_NAK_THRESHOLD 3 + +//============================================== +// Maximum number of DHCP options bytes supplied +//============================================== + +#define DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE 256 +#define DHCP_OPTIONS_BUFFER_SIZE 256 + +//=================================== +// UDP port numbers of DHCP messages. +//=================================== + +#define BOOTPS_PORT 67 +#define BOOTPC_PORT 68 + +//=========================== +// The DHCP message structure +//=========================== + +typedef struct { +# define BOOTREQUEST 1 +# define BOOTREPLY 2 + UCHAR op; /* message op */ + + UCHAR htype; /* hardware address type (e.g. '1' = 10Mb Ethernet) */ + UCHAR hlen; /* hardware address length (e.g. '6' for 10Mb Ethernet) */ + UCHAR hops; /* client sets to 0, may be used by relay agents */ + ULONG xid; /* transaction ID, chosen by client */ + USHORT secs; /* seconds since request process began, set by client */ + USHORT flags; + ULONG ciaddr; /* client IP address, client sets if known */ + ULONG yiaddr; /* 'your' IP address -- server's response to client */ + ULONG siaddr; /* server IP address */ + ULONG giaddr; /* relay agent IP address */ + UCHAR chaddr[16]; /* client hardware address */ + UCHAR sname[64]; /* optional server host name */ + UCHAR file[128]; /* boot file name */ + ULONG magic; /* must be 0x63825363 (network order) */ +} DHCP; + +typedef struct { + ETH_HEADER eth; + IPHDR ip; + UDPHDR udp; + DHCP dhcp; +} DHCPPre; + +typedef struct { + DHCPPre pre; + UCHAR options[DHCP_OPTIONS_BUFFER_SIZE]; +} DHCPFull; + +typedef struct { + unsigned int optlen; + BOOLEAN overflow; + DHCPFull msg; +} DHCPMsg; + +//=================== +// Macros for DHCPMSG +//=================== + +#define DHCPMSG_LEN_BASE(p) (sizeof (DHCPPre)) +#define DHCPMSG_LEN_OPT(p) ((p)->optlen) +#define DHCPMSG_LEN_FULL(p) (DHCPMSG_LEN_BASE(p) + DHCPMSG_LEN_OPT(p)) +#define DHCPMSG_BUF(p) ((UCHAR*) &(p)->msg) +#define DHCPMSG_OVERFLOW(p) ((p)->overflow) + +//======================================== +// structs to hold individual DHCP options +//======================================== + +typedef struct { + UCHAR type; +} DHCPOPT0; + +typedef struct { + UCHAR type; + UCHAR len; + UCHAR data; +} DHCPOPT8; + +typedef struct { + UCHAR type; + UCHAR len; + ULONG data; +} DHCPOPT32; + +#pragma pack() + +//================== +// DHCP Option types +//================== + +#define DHCP_MSG_TYPE 53 /* message type (u8) */ +#define DHCP_PARM_REQ 55 /* parameter request list: c1 (u8), ... */ +#define DHCP_CLIENT_ID 61 /* client ID: type (u8), i1 (u8), ... */ +#define DHCP_IP 50 /* requested IP addr (u32) */ +#define DHCP_NETMASK 1 /* subnet mask (u32) */ +#define DHCP_LEASE_TIME 51 /* lease time sec (u32) */ +#define DHCP_RENEW_TIME 58 /* renewal time sec (u32) */ +#define DHCP_REBIND_TIME 59 /* rebind time sec (u32) */ +#define DHCP_SERVER_ID 54 /* server ID: IP addr (u32) */ +#define DHCP_PAD 0 +#define DHCP_END 255 + +//==================== +// DHCP Messages types +//==================== + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +#if DEBUG + +VOID +DumpDHCP (const ETH_HEADER *eth, + const IPHDR *ip, + const UDPHDR *udp, + const DHCP *dhcp, + const int optlen); + +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/endian.h b/src/VBox/HostDrivers/VBoxTAP/endian.h new file mode 100644 index 000000000..3032dea53 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/endian.h @@ -0,0 +1,39 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device + * functionality on Windows. Originally derived + * from the CIPE-Win32 project by Damion K. Wilson, + * with extensive modifications by James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TAP_LITTLE_ENDIAN +#define ntohs(x) RtlUshortByteSwap(x) +#define htons(x) RtlUshortByteSwap(x) +#define ntohl(x) RtlUlongByteSwap(x) +#define htonl(x) RtlUlongByteSwap(x) +#else +#define ntohs(x) ((USHORT)(x)) +#define htons(x) ((USHORT)(x)) +#define ntohl(x) ((ULONG)(x)) +#define htonl(x) ((ULONG)(x)) +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/error.c b/src/VBox/HostDrivers/VBoxTAP/error.c new file mode 100644 index 000000000..3e52eb042 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/error.c @@ -0,0 +1,382 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//----------------- +// DEBUGGING OUTPUT +//----------------- + +const char *g_LastErrorFilename; +int g_LastErrorLineNumber; + +#if DBG + +DebugOutput g_Debug; + +BOOLEAN +NewlineExists (const char *str, int len) +{ + while (len-- > 0) + { + const char c = *str++; + if (c == '\n') + return TRUE; + else if (c == '\0') + break; + } + return FALSE; +} + +VOID +MyDebugInit (unsigned int bufsiz) +{ + NdisZeroMemory (&g_Debug, sizeof (g_Debug)); + g_Debug.text = (char *) MemAlloc (bufsiz, FALSE); + if (g_Debug.text) + g_Debug.capacity = bufsiz; +} + +VOID +MyDebugFree () +{ + if (g_Debug.text) + MemFree (g_Debug.text, g_Debug.capacity); + NdisZeroMemory (&g_Debug, sizeof (g_Debug)); +} + +VOID +MyDebugPrint (const unsigned char* format, ...) +{ + if (g_Debug.text && g_Debug.capacity > 0 && CAN_WE_PRINT) + { + BOOLEAN owned; + ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned); + if (owned) + { + const int remaining = (int)g_Debug.capacity - (int)g_Debug.out; + + if (remaining > 0) + { + va_list args; + NTSTATUS status; + char *end; + + va_start (args, format); + status = RtlStringCchVPrintfExA (g_Debug.text + g_Debug.out, + remaining, + &end, + NULL, + STRSAFE_NO_TRUNCATION | STRSAFE_IGNORE_NULLS, + format, + args); + va_end (args); + + if (status == STATUS_SUCCESS) + g_Debug.out = end - g_Debug.text; + else + g_Debug.error = TRUE; + } + else + g_Debug.error = TRUE; + + RELEASE_MUTEX (&g_Debug.lock); + } + else + g_Debug.error = TRUE; + } +} + +BOOLEAN +GetDebugLine (char *buf, const int len) +{ + static const char *truncated = "[OUTPUT TRUNCATED]\n"; + BOOLEAN ret = FALSE; + + NdisZeroMemory (buf, len); + + if (g_Debug.text && g_Debug.capacity > 0) + { + BOOLEAN owned; + ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned); + if (owned) + { + int i = 0; + + if (g_Debug.error || NewlineExists (g_Debug.text + g_Debug.in, (int)g_Debug.out - (int)g_Debug.in)) + { + while (i < (len - 1) && g_Debug.in < g_Debug.out) + { + const char c = g_Debug.text[g_Debug.in++]; + if (c == '\n') + break; + buf[i++] = c; + } + if (i < len) + buf[i] = '\0'; + } + + if (!i) + { + if (g_Debug.in == g_Debug.out) + { + g_Debug.in = g_Debug.out = 0; + if (g_Debug.error) + { + const unsigned int tlen = strlen (truncated); + if (tlen < g_Debug.capacity) + { + NdisMoveMemory (g_Debug.text, truncated, tlen+1); + g_Debug.out = tlen; + } + g_Debug.error = FALSE; + } + } + } + else + ret = TRUE; + + RELEASE_MUTEX (&g_Debug.lock); + } + } + return ret; +} + +VOID +MyAssert (const unsigned char *file, int line) +{ + DEBUGP (("MYASSERT failed %s/%d\n", file, line)); + KeBugCheckEx (0x0F00BABA, + (ULONG_PTR) line, + (ULONG_PTR) 0, + (ULONG_PTR) 0, + (ULONG_PTR) 0); +} + +VOID +PrMac (const MACADDR mac) +{ + DEBUGP (("%x:%x:%x:%x:%x:%x", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5])); +} + +VOID +PrIP (IPADDR ip_addr) +{ + const unsigned char *ip = (const unsigned char *) &ip_addr; + + DEBUGP (("%d.%d.%d.%d", + ip[0], ip[1], ip[2], ip[3])); +} + +const char * +PrIPProto (int proto) +{ + switch (proto) + { + case IPPROTO_UDP: + return "UDP"; + case IPPROTO_TCP: + return "TCP"; + case IPPROTO_ICMP: + return "ICMP"; + case IPPROTO_IGMP: + return "IGMP"; + default: + return "???"; + } +} + +VOID +DumpARP (const char *prefix, const ARP_PACKET *arp) +{ + DEBUGP (("%s ARP src=", prefix)); + PrMac (arp->m_MAC_Source); + DEBUGP ((" dest=")); + PrMac (arp->m_MAC_Destination); + DEBUGP ((" OP=0x%04x", + (int)ntohs(arp->m_ARP_Operation))); + DEBUGP ((" M=0x%04x(%d)", + (int)ntohs(arp->m_MAC_AddressType), + (int)arp->m_MAC_AddressSize)); + DEBUGP ((" P=0x%04x(%d)", + (int)ntohs(arp->m_PROTO_AddressType), + (int)arp->m_PROTO_AddressSize)); + + DEBUGP ((" MacSrc=")); + PrMac (arp->m_ARP_MAC_Source); + DEBUGP ((" MacDest=")); + PrMac (arp->m_ARP_MAC_Destination); + + DEBUGP ((" IPSrc=")); + PrIP (arp->m_ARP_IP_Source); + DEBUGP ((" IPDest=")); + PrIP (arp->m_ARP_IP_Destination); + + DEBUGP (("\n")); +} + +struct ethpayload { + ETH_HEADER eth; + UCHAR payload[DEFAULT_PACKET_LOOKAHEAD]; +}; + +VOID +DumpPacket2 (const char *prefix, + const ETH_HEADER *eth, + const unsigned char *data, + unsigned int len) +{ + struct ethpayload *ep = (struct ethpayload *) MemAlloc (sizeof (struct ethpayload), TRUE); + if (ep) + { + if (len > DEFAULT_PACKET_LOOKAHEAD) + len = DEFAULT_PACKET_LOOKAHEAD; + ep->eth = *eth; + NdisMoveMemory (ep->payload, data, len); + DumpPacket (prefix, (unsigned char *) ep, sizeof (ETH_HEADER) + len); + MemFree (ep, sizeof (struct ethpayload)); + } +} + +VOID +DumpPacket (const char *prefix, + const unsigned char *data, + unsigned int len) +{ + const ETH_HEADER *eth = (const ETH_HEADER *) data; + const IPHDR *ip = (const IPHDR *) (data + sizeof (ETH_HEADER)); + + if (len < sizeof (ETH_HEADER)) + { + DEBUGP (("%s TRUNCATED PACKET LEN=%d\n", prefix, len)); + return; + } + + // ARP Packet? + if (len >= sizeof (ARP_PACKET) && eth->proto == htons (ETH_P_ARP)) + { + DumpARP (prefix, (const ARP_PACKET *) data); + return; + } + + // IPv4 packet? + if (len >= (sizeof (IPHDR) + sizeof (ETH_HEADER)) + && eth->proto == htons (ETH_P_IP) + && IPH_GET_VER (ip->version_len) == 4) + { + const int hlen = IPH_GET_LEN (ip->version_len); + const int blen = len - sizeof (ETH_HEADER); + BOOLEAN did = FALSE; + + DEBUGP (("%s IPv4 %s[%d]", prefix, PrIPProto (ip->protocol), len)); + + if (!(ntohs (ip->tot_len) == blen && hlen <= blen)) + { + DEBUGP ((" XXX")); + return; + } + + // TCP packet? + if (ip->protocol == IPPROTO_TCP + && blen - hlen >= (sizeof (TCPHDR))) + { + const TCPHDR *tcp = (TCPHDR *) (data + sizeof (ETH_HEADER) + hlen); + DEBUGP ((" ")); + PrIP (ip->saddr); + DEBUGP ((":%d", ntohs (tcp->source))); + DEBUGP ((" -> ")); + PrIP (ip->daddr); + DEBUGP ((":%d", ntohs (tcp->dest))); + did = TRUE; + } + + // UDP packet? + else if ((ntohs (ip->frag_off) & IP_OFFMASK) == 0 + && ip->protocol == IPPROTO_UDP + && blen - hlen >= (sizeof (UDPHDR))) + { + const UDPHDR *udp = (UDPHDR *) (data + sizeof (ETH_HEADER) + hlen); + + // DHCP packet? + if ((udp->dest == htons (BOOTPC_PORT) || udp->dest == htons (BOOTPS_PORT)) + && blen - hlen >= (sizeof (UDPHDR) + sizeof (DHCP))) + { + const DHCP *dhcp = (DHCP *) (data + + hlen + + sizeof (ETH_HEADER) + + sizeof (UDPHDR)); + + int optlen = len + - sizeof (ETH_HEADER) + - hlen + - sizeof (UDPHDR) + - sizeof (DHCP); + + if (optlen < 0) + optlen = 0; + + DumpDHCP (eth, ip, udp, dhcp, optlen); + did = TRUE; + } + + if (!did) + { + DEBUGP ((" ")); + PrIP (ip->saddr); + DEBUGP ((":%d", ntohs (udp->source))); + DEBUGP ((" -> ")); + PrIP (ip->daddr); + DEBUGP ((":%d", ntohs (udp->dest))); + did = TRUE; + } + } + + if (!did) + { + DEBUGP ((" ipproto=%d ", ip->protocol)); + PrIP (ip->saddr); + DEBUGP ((" -> ")); + PrIP (ip->daddr); + } + + DEBUGP (("\n")); + return; + } + + { + DEBUGP (("%s ??? src=", prefix)); + PrMac (eth->src); + DEBUGP ((" dest=")); + PrMac (eth->dest); + DEBUGP ((" proto=0x%04x len=%d\n", + (int) ntohs(eth->proto), + len)); + } +} + +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/error.h b/src/VBox/HostDrivers/VBoxTAP/error.h new file mode 100644 index 000000000..7860bb9f7 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/error.h @@ -0,0 +1,103 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//----------------- +// DEBUGGING OUTPUT +//----------------- + +#define NOTE_ERROR() \ +{ \ + g_LastErrorFilename = __FILE__; \ + g_LastErrorLineNumber = __LINE__; \ +} + + +#ifdef DEBUG +#define DEBUGP(fmt) { DbgPrint fmt; } +#else +#define DEBUGP(fmt) +#endif + +#if DBG + +typedef struct { + unsigned int in; + unsigned int out; + unsigned int capacity; + char *text; + BOOLEAN error; + MUTEX lock; +} DebugOutput; + +VOID MyDebugPrint (const unsigned char* format, ...); + +VOID MyAssert (const unsigned char *file, int line); + +VOID DumpPacket (const char *prefix, + const unsigned char *data, + unsigned int len); + +VOID DumpPacket2 (const char *prefix, + const ETH_HEADER *eth, + const unsigned char *data, + unsigned int len); + +#define CAN_WE_PRINT (DEBUGP_AT_DISPATCH || KeGetCurrentIrql () < DISPATCH_LEVEL) + +#if ALSO_DBGPRINT +#define DEBUGP(fmt) { DbgPrint fmt; if (CAN_WE_PRINT) DbgPrint fmt; } +#else +#define DEBUGP(fmt) { DbgPrint fmt; } +#endif + +#define MYASSERT(exp) \ +{ \ + if (!(exp)) \ + { \ + MyAssert(__FILE__, __LINE__); \ + } \ +} + +#if DBG +#define DUMP_PACKET(prefix, data, len) \ + DumpPacket (prefix, data, len) + +#define DUMP_PACKET2(prefix, eth, data, len) \ + DumpPacket2 (prefix, eth, data, len) +#else +#define DUMP_PACKET(prefix, data, len) +#define DUMP_PACKET2(prefix, eth, data, len) +#endif + +#else + +#define MYASSERT(exp) +#define DUMP_PACKET(prefix, data, len) +#define DUMP_PACKET2(prefix, eth, data, len) + +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/hexdump.c b/src/VBox/HostDrivers/VBoxTAP/hexdump.c new file mode 100644 index 000000000..551377a7e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/hexdump.c @@ -0,0 +1,74 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hexdump.h" + +#ifndef NDIS_MINIPORT_DRIVER + +VOID (*DbgMessage)(char *p_Format, ...) = DisplayDebugString; + +VOID DisplayDebugString (char *p_Format, ...) + { + static char l_Buffer [4096]; + + va_list l_ArgumentList; + va_start (l_ArgumentList, p_Format); + vsprintf (l_Buffer, p_Format, l_ArgumentList); + va_end (l_ArgumentList); + + OutputDebugStringA (l_Buffer); + } + +#endif + +VOID HexDump (unsigned char *p_Buffer, unsigned long p_Size) + { + unsigned long l_Index, l_Idx; + unsigned char l_Row [17]; + + for (l_Index = l_Row [16] = 0; l_Index < p_Size || l_Index % 16; ++l_Index) + { + if (l_Index % 16 == 0) + DEBUGP (("%05x ", l_Index)); + DEBUGP (("%02x ", l_Row [l_Index % 16] = (l_Index < p_Size ? p_Buffer [l_Index] : 0))); + l_Row [l_Index % 16] = IfPrint (l_Row [l_Index % 16]); + if ((l_Index + 1) % 16 == 0) + DEBUGP ((" %s\n", l_Row)); + } + + DEBUGP (("\n")); + } + +#ifdef __cplusplus +} +#endif + diff --git a/src/VBox/HostDrivers/VBoxTAP/hexdump.h b/src/VBox/HostDrivers/VBoxTAP/hexdump.h new file mode 100644 index 000000000..4c8981213 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/hexdump.h @@ -0,0 +1,68 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef HEXDUMP_DEFINED +#define HEXDUMP_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +//===================================================================================== +// Debug Routines +//===================================================================================== + +#ifndef NDIS_MINIPORT_DRIVER +# include <stdio.h> +# include <ctype.h> +# include <windows.h> +# include <winnt.h> +# include <memory.h> + +# ifndef DEBUGP +# define DEBUGP(fmt) { DbgMessage fmt; } +# endif + + extern VOID (*DbgMessage)(char *p_Format, ...); + + VOID DisplayDebugString (char *p_Format, ...); +#endif + +//=================================================================================== +// Reporting / Debugging +//=================================================================================== +#define IfPrint(c) (c >= 32 && c < 127 ? c : '.') + +VOID HexDump (unsigned char *p_Buffer, unsigned long p_Size); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/VBox/HostDrivers/VBoxTAP/instance.c b/src/VBox/HostDrivers/VBoxTAP/instance.c new file mode 100644 index 000000000..53c800c8e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/instance.c @@ -0,0 +1,245 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device + * functionality on Windows. Originally derived + * from the CIPE-Win32 project by Damion K. Wilson, + * with extensive modifications by James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define INSTANCE_KEY(a) ((PVOID)((a)->m_Extension.m_TapDevice)) + +#define N_INSTANCE_BUCKETS 256 + +typedef struct _INSTANCE { + struct _INSTANCE *next; + TapAdapterPointer m_Adapter; +} INSTANCE; + +typedef struct { + INSTANCE *list; + MUTEX lock; +} INSTANCE_BUCKET; + +typedef struct { + INSTANCE_BUCKET buckets[N_INSTANCE_BUCKETS]; +} INSTANCE_HASH; + +INSTANCE_HASH *g_InstanceHash = NULL; + +// must return a hash >= 0 and < N_INSTANCE_BUCKETS +int +InstanceHashValue (PVOID addr) +{ + UCHAR *p = (UCHAR *) &addr; + + if (sizeof (addr) == 4) + return p[0] ^ p[1] ^ p[2] ^ p[3]; + else if (sizeof (addr) == 8) + return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4] ^ p[5] ^ p[6] ^ p[7]; + else + { + MYASSERT (0); + } +} + +BOOLEAN +InitInstanceList (VOID) +{ + MYASSERT (g_InstanceHash == NULL); + g_InstanceHash = MemAlloc (sizeof (INSTANCE_HASH), TRUE); + if (g_InstanceHash) + { + int i; + for (i = 0; i < N_INSTANCE_BUCKETS; ++i) + INIT_MUTEX (&g_InstanceHash->buckets[i].lock); + return TRUE; + } + else + return FALSE; +} + +int +NInstances (VOID) +{ + int i, n = 0; + + if (g_InstanceHash) + { + for (i = 0; i < N_INSTANCE_BUCKETS; ++i) + { + BOOLEAN got_lock; + INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i]; + ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); + + if (got_lock) + { + INSTANCE *current; + for (current = ib->list; current != NULL; current = current->next) + ++n; + RELEASE_MUTEX (&ib->lock); + } + else + return -1; + } + } + + return n; +} + +int +InstanceMaxBucketSize (VOID) +{ + int i, n = 0; + + if (g_InstanceHash) + { + for (i = 0; i < N_INSTANCE_BUCKETS; ++i) + { + BOOLEAN got_lock; + int bucket_size = 0; + INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i]; + ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); + + if (got_lock) + { + INSTANCE *current; + for (current = ib->list; current != NULL; current = current->next) + ++bucket_size; + if (bucket_size > n) + n = bucket_size; + RELEASE_MUTEX (&ib->lock); + } + else + return -1; + } + } + + return n; +} + +VOID +FreeInstanceList (VOID) +{ + if (g_InstanceHash) + { + MYASSERT (NInstances() == 0); + MemFree (g_InstanceHash, sizeof (INSTANCE_HASH)); + g_InstanceHash = NULL; + } +} + +BOOLEAN +AddAdapterToInstanceList (TapAdapterPointer p_Adapter) +{ + BOOLEAN got_lock; + BOOLEAN ret = FALSE; + const int hash = InstanceHashValue(INSTANCE_KEY(p_Adapter)); + INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[hash]; + + DEBUGP (("[TAP] AddAdapterToInstanceList hash=%d\n", hash)); + + ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); + + if (got_lock) + { + INSTANCE *i = MemAlloc (sizeof (INSTANCE), FALSE); + if (i) + { + MYASSERT (p_Adapter); + i->m_Adapter = p_Adapter; + i->next = ib->list; + ib->list = i; + ret = TRUE; + } + RELEASE_MUTEX (&ib->lock); + } + + return ret; +} + +BOOLEAN +RemoveAdapterFromInstanceList (TapAdapterPointer p_Adapter) +{ + BOOLEAN got_lock; + BOOLEAN ret = FALSE; + INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue(INSTANCE_KEY(p_Adapter))]; + + ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); + + if (got_lock) + { + INSTANCE *current, *prev=NULL; + for (current = ib->list; current != NULL; current = current->next) + { + if (current->m_Adapter == p_Adapter) // found match + { + if (prev) + prev->next = current->next; + else + ib->list = current->next; + MemFree (current->m_Adapter, sizeof (TapAdapter)); + MemFree (current, sizeof (INSTANCE)); + ret = TRUE; + break; + } + prev = current; + } + RELEASE_MUTEX (&ib->lock); + } + + return ret; +} + +TapAdapterPointer +LookupAdapterInInstanceList (PDEVICE_OBJECT p_DeviceObject) +{ + BOOLEAN got_lock; + TapAdapterPointer ret = NULL; + INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue((PVOID)p_DeviceObject)]; + + ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); + + if (got_lock) + { + INSTANCE *current, *prev=NULL; + for (current = ib->list; current != NULL; current = current->next) + { + if (p_DeviceObject == INSTANCE_KEY (current->m_Adapter)) // found match + { + // move it to head of list + if (prev) + { + prev->next = current->next; + current->next = ib->list; + ib->list = current; + } + ret = ib->list->m_Adapter; + break; + } + prev = current; + } + RELEASE_MUTEX (&ib->lock); + } + + return ret; +} diff --git a/src/VBox/HostDrivers/VBoxTAP/lock.h b/src/VBox/HostDrivers/VBoxTAP/lock.h new file mode 100644 index 000000000..8598f319d --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/lock.h @@ -0,0 +1,79 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device + * functionality on Windows. Originally derived + * from the CIPE-Win32 project by Damion K. Wilson, + * with extensive modifications by James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct +{ + volatile long count; +} MUTEX; + +#define MUTEX_SLEEP_TIME 10000 // microseconds + +#define INIT_MUTEX(m) { (m)->count = 0; } + +#define ACQUIRE_MUTEX_BLOCKING(m) \ +{ \ + while (NdisInterlockedIncrement (&((m)->count)) != 1) \ + { \ + NdisInterlockedDecrement(&((m)->count)); \ + NdisMSleep(MUTEX_SLEEP_TIME); \ + } \ +} + +#define RELEASE_MUTEX(m) \ +{ \ + NdisInterlockedDecrement(&((m)->count)); \ +} + +#define ACQUIRE_MUTEX_NONBLOCKING(m, result) \ +{ \ + if (NdisInterlockedIncrement (&((m)->count)) != 1) \ + { \ + NdisInterlockedDecrement(&((m)->count)); \ + result = FALSE; \ + } \ + else \ + { \ + result = TRUE; \ + } \ +} + +#define ACQUIRE_MUTEX_ADAPTIVE(m, result) \ +{ \ + result = TRUE; \ + while (NdisInterlockedIncrement (&((m)->count)) != 1) \ + { \ + NdisInterlockedDecrement(&((m)->count)); \ + if (KeGetCurrentIrql () < DISPATCH_LEVEL) \ + NdisMSleep(MUTEX_SLEEP_TIME); \ + else \ + { \ + result = FALSE; \ + break; \ + } \ + } \ +} diff --git a/src/VBox/HostDrivers/VBoxTAP/macinfo.c b/src/VBox/HostDrivers/VBoxTAP/macinfo.c new file mode 100644 index 000000000..131881887 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/macinfo.c @@ -0,0 +1,159 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "macinfo.h" + +int +HexStringToDecimalInt (const int p_Character) +{ + int l_Value = 0; + + if (p_Character >= 'A' && p_Character <= 'F') + l_Value = (p_Character - 'A') + 10; + else if (p_Character >= 'a' && p_Character <= 'f') + l_Value = (p_Character - 'a') + 10; + else if (p_Character >= '0' && p_Character <= '9') + l_Value = p_Character - '0'; + + return l_Value; +} + +BOOLEAN +ParseMAC (MACADDR dest, const char *src) +{ + int c; + int mac_index = 0; + BOOLEAN high_digit = FALSE; + int delim_action = 1; + + MYASSERT (src); + MYASSERT (dest); + + CLEAR_MAC (dest); + + while (c = *src++) + { + if (IsMacDelimiter (c)) + { + mac_index += delim_action; + high_digit = FALSE; + delim_action = 1; + } + else if (IsHexDigit (c)) + { + const int digit = HexStringToDecimalInt (c); + if (mac_index < sizeof (MACADDR)) + { + if (!high_digit) + { + dest[mac_index] = (char)(digit); + high_digit = TRUE; + delim_action = 1; + } + else + { + dest[mac_index] = (char)(dest[mac_index] * 16 + digit); + ++mac_index; + high_digit = FALSE; + delim_action = 0; + } + } + else + return FALSE; + } + else + return FALSE; + } + + return (mac_index + delim_action) >= sizeof (MACADDR); +} + +/* + * Generate a MAC using the GUID in the adapter name. + * + * The mac is constructed as 00:FF:xx:xx:xx:xx where + * the Xs are taken from the first 32 bits of the GUID in the + * adapter name. This is similar to the Linux 2.4 tap MAC + * generator, except linux uses 32 random bits for the Xs. + * + * In general, this solution is reasonable for most + * applications except for very large bridged TAP networks, + * where the probability of address collisions becomes more + * than infintesimal. + * + * Using the well-known "birthday paradox", on a 1000 node + * network the probability of collision would be + * 0.000116292153. On a 10,000 node network, the probability + * of collision would be 0.01157288998621678766. + */ + +VOID GenerateRandomMac (MACADDR mac, const unsigned char *adapter_name) +{ + unsigned const char *cp = adapter_name; + unsigned char c; + unsigned int i = 2; + unsigned int byte = 0; + int brace = 0; + int state = 0; + + CLEAR_MAC (mac); + + mac[0] = 0x00; + mac[1] = 0xFF; + + while (c = *cp++) + { + if (i >= sizeof (MACADDR)) + break; + if (c == '{') + brace = 1; + if (IsHexDigit (c) && brace) + { + const unsigned int digit = HexStringToDecimalInt (c); + if (state) + { + byte <<= 4; + byte |= digit; + mac[i++] = (unsigned char) byte; + state = 0; + } + else + { + byte = digit; + state = 1; + } + } + } +} + +VOID GenerateRelatedMAC (MACADDR dest, const MACADDR src, const int delta) +{ + COPY_MAC (dest, src); + dest[2] += (UCHAR) delta; +} + diff --git a/src/VBox/HostDrivers/VBoxTAP/macinfo.h b/src/VBox/HostDrivers/VBoxTAP/macinfo.h new file mode 100644 index 000000000..28c76d251 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/macinfo.h @@ -0,0 +1,43 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MacInfoDefined +#define MacInfoDefined + +//=================================================================================== +// Macros +//=================================================================================== +#define IsMacDelimiter(a) (a == ':' || a == '-' || a == '.') +#define IsHexDigit(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) + +#define COPY_MAC(dest, src) NdisMoveMemory ((dest), (src), sizeof (MACADDR)) +#define CLEAR_MAC(dest) NdisZeroMemory ((dest), sizeof (MACADDR)) +#define MAC_EQUAL(a,b) (memcmp ((a), (b), sizeof (MACADDR)) == 0) + +#endif + diff --git a/src/VBox/HostDrivers/VBoxTAP/mem.c b/src/VBox/HostDrivers/VBoxTAP/mem.c new file mode 100644 index 000000000..5681fd4f0 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/mem.c @@ -0,0 +1,190 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//------------------ +// Memory Management +//------------------ + +PVOID +MemAlloc (ULONG p_Size, BOOLEAN zero) +{ + PVOID l_Return = NULL; + + if (p_Size) + { + __try + { + if (NdisAllocateMemoryWithTag (&l_Return, p_Size, 'APAT') + == NDIS_STATUS_SUCCESS) + { + if (zero) + NdisZeroMemory (l_Return, p_Size); + } + else + l_Return = NULL; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + l_Return = NULL; + } + } + + return l_Return; +} + +VOID +MemFree (PVOID p_Addr, ULONG p_Size) +{ + if (p_Addr && p_Size) + { + __try + { +#if DEBUG + NdisZeroMemory (p_Addr, p_Size); +#endif + NdisFreeMemory (p_Addr, p_Size, 0); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + } +} + +/* + * Circular queue management routines. + */ + +#define QUEUE_BYTE_ALLOCATION(size) \ + (sizeof (Queue) + (size * sizeof (PVOID))) + +#define QUEUE_ADD_INDEX(var, inc) \ +{ \ + var += inc; \ + if (var >= q->capacity) \ + var -= q->capacity; \ + MYASSERT (var < q->capacity); \ +} + +#define QUEUE_SANITY_CHECK() \ + MYASSERT (q != NULL && q->base < q->capacity && q->size <= q->capacity) + +#define QueueCount(q) (q->size) + +#define UPDATE_MAX_SIZE() \ +{ \ + if (q->size > q->max_size) \ + q->max_size = q->size; \ +} + +Queue * +QueueInit (ULONG capacity) +{ + Queue *q; + + MYASSERT (capacity > 0); + q = (Queue *) MemAlloc (QUEUE_BYTE_ALLOCATION (capacity), TRUE); + if (!q) + return NULL; + + q->base = q->size = 0; + q->capacity = capacity; + q->max_size = 0; + return q; +} + +VOID +QueueFree (Queue *q) +{ + if (q) + { + QUEUE_SANITY_CHECK (); + MemFree (q, QUEUE_BYTE_ALLOCATION (q->capacity)); + } +} + +PVOID +QueuePush (Queue *q, PVOID item) +{ + ULONG dest; + QUEUE_SANITY_CHECK (); + if (q->size == q->capacity) + return NULL; + dest = q->base; + QUEUE_ADD_INDEX (dest, q->size); + q->data[dest] = item; + ++q->size; + UPDATE_MAX_SIZE(); + return item; +} + +PVOID +QueuePop (Queue *q) +{ + ULONG oldbase; + QUEUE_SANITY_CHECK (); + if (!q->size) + return NULL; + oldbase = q->base; + QUEUE_ADD_INDEX (q->base, 1); + --q->size; + UPDATE_MAX_SIZE(); + return q->data[oldbase]; +} + +PVOID +QueueExtract (Queue *q, PVOID item) +{ + ULONG src, dest, count, n; + QUEUE_SANITY_CHECK (); + n = 0; + src = dest = q->base; + count = q->size; + while (count--) + { + if (item == q->data[src]) + { + ++n; + --q->size; + } + else + { + q->data[dest] = q->data[src]; + QUEUE_ADD_INDEX (dest, 1); + } + QUEUE_ADD_INDEX (src, 1); + } + if (n) + return item; + else + return NULL; +} + +#undef QUEUE_BYTE_ALLOCATION +#undef QUEUE_ADD_INDEX +#undef QUEUE_SANITY_CHECK +#undef UPDATE_MAX_SIZE diff --git a/src/VBox/HostDrivers/VBoxTAP/proto.h b/src/VBox/HostDrivers/VBoxTAP/proto.h new file mode 100644 index 000000000..6a70bebd7 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/proto.h @@ -0,0 +1,168 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================ +// MAC address, Ethernet header, and ARP +//============================================================ + +#pragma pack(1) + +#define IP_HEADER_SIZE 20 + +typedef unsigned char MACADDR [6]; +typedef unsigned long IPADDR; + +//----------------- +// Ethernet address +//----------------- + +typedef struct { + MACADDR addr; +} ETH_ADDR; + +typedef struct { + ETH_ADDR list[NIC_MAX_MCAST_LIST]; +} MC_LIST; + +//---------------- +// Ethernet header +//---------------- + +typedef struct +{ + MACADDR dest; /* destination eth addr */ + MACADDR src; /* source ether addr */ + +# define ETH_P_IP 0x0800 /* IPv4 protocol */ +# define ETH_P_ARP 0x0806 /* ARP protocol */ + USHORT proto; /* packet type ID field */ +} ETH_HEADER, *PETH_HEADER; + +//---------------- +// ARP packet +//---------------- + +typedef struct + { + MACADDR m_MAC_Destination; // Reverse these two + MACADDR m_MAC_Source; // to answer ARP requests + USHORT m_Proto; // 0x0806 + +# define MAC_ADDR_TYPE 0x0001 + USHORT m_MAC_AddressType; // 0x0001 + + USHORT m_PROTO_AddressType; // 0x0800 + UCHAR m_MAC_AddressSize; // 0x06 + UCHAR m_PROTO_AddressSize; // 0x04 + +# define ARP_REQUEST 0x0001 +# define ARP_REPLY 0x0002 + USHORT m_ARP_Operation; // 0x0001 for ARP request, 0x0002 for ARP reply + + MACADDR m_ARP_MAC_Source; + IPADDR m_ARP_IP_Source; + MACADDR m_ARP_MAC_Destination; + IPADDR m_ARP_IP_Destination; + } +ARP_PACKET, *PARP_PACKET; + +//---------- +// IP Header +//---------- + +typedef struct { +# define IPH_GET_VER(v) (((v) >> 4) & 0x0F) +# define IPH_GET_LEN(v) (((v) & 0x0F) << 2) + UCHAR version_len; + + UCHAR tos; + USHORT tot_len; + USHORT id; + +# define IP_OFFMASK 0x1fff + USHORT frag_off; + + UCHAR ttl; + +# define IPPROTO_UDP 17 /* UDP protocol */ +# define IPPROTO_TCP 6 /* TCP protocol */ +# define IPPROTO_ICMP 1 /* ICMP protocol */ +# define IPPROTO_IGMP 2 /* IGMP protocol */ + UCHAR protocol; + + USHORT check; + ULONG saddr; + ULONG daddr; + /* The options start here. */ +} IPHDR; + +//----------- +// UDP header +//----------- + +typedef struct { + USHORT source; + USHORT dest; + USHORT len; + USHORT check; +} UDPHDR; + +//-------------------------- +// TCP header, per RFC 793. +//-------------------------- + +typedef struct { + USHORT source; /* source port */ + USHORT dest; /* destination port */ + ULONG seq; /* sequence number */ + ULONG ack_seq; /* acknowledgement number */ + +# define TCPH_GET_DOFF(d) (((d) & 0xF0) >> 2) + UCHAR doff_res; + +# define TCPH_FIN_MASK (1<<0) +# define TCPH_SYN_MASK (1<<1) +# define TCPH_RST_MASK (1<<2) +# define TCPH_PSH_MASK (1<<3) +# define TCPH_ACK_MASK (1<<4) +# define TCPH_URG_MASK (1<<5) +# define TCPH_ECE_MASK (1<<6) +# define TCPH_CWR_MASK (1<<7) + UCHAR flags; + + USHORT window; + USHORT check; + USHORT urg_ptr; +} TCPHDR; + +#define TCPOPT_EOL 0 +#define TCPOPT_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 + +#pragma pack() diff --git a/src/VBox/HostDrivers/VBoxTAP/prototypes.h b/src/VBox/HostDrivers/VBoxTAP/prototypes.h new file mode 100644 index 000000000..38395cd7e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/prototypes.h @@ -0,0 +1,206 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TAP_PROTOTYPES_DEFINED +#define TAP_PROTOTYPES_DEFINED + +NTSTATUS DriverEntry + ( + IN PDRIVER_OBJECT p_DriverObject, + IN PUNICODE_STRING p_RegistryPath + ); + +VOID TapDriverUnload + ( + IN PDRIVER_OBJECT p_DriverObject + ); + +NDIS_STATUS AdapterCreate + ( + OUT PNDIS_STATUS p_ErrorStatus, + OUT PUINT p_MediaIndex, + IN PNDIS_MEDIUM p_Media, + IN UINT p_MediaCount, + IN NDIS_HANDLE p_AdapterHandle, + IN NDIS_HANDLE p_ConfigurationHandle + ); + +VOID AdapterHalt + ( + IN NDIS_HANDLE p_AdapterContext + ); + +VOID AdapterFreeResources + ( + TapAdapterPointer p_Adapter + ); + +NDIS_STATUS AdapterReset + ( + OUT PBOOLEAN p_AddressingReset, + IN NDIS_HANDLE p_AdapterContext + ); + +NDIS_STATUS AdapterQuery + ( + IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesWritten, + OUT PULONG p_BytesNeeded + ); + +NDIS_STATUS AdapterModify + ( + IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesRead, + OUT PULONG p_BytesNeeded + ); + +NDIS_STATUS AdapterTransmit + ( + IN NDIS_HANDLE p_AdapterContext, + IN PNDIS_PACKET p_Packet, + IN UINT p_Flags + ); + +NDIS_STATUS AdapterReceive + ( + OUT PNDIS_PACKET p_Packet, + OUT PUINT p_Transferred, + IN NDIS_HANDLE p_AdapterContext, + IN NDIS_HANDLE p_ReceiveContext, + IN UINT p_Offset, + IN UINT p_ToTransfer + ); + +NTSTATUS TapDeviceHook + ( + IN PDEVICE_OBJECT p_DeviceObject, + IN PIRP p_IRP + ); + +NDIS_STATUS CreateTapDevice + ( + TapExtensionPointer p_Extension, + const char *p_Name + ); + +VOID DestroyTapDevice + ( + TapExtensionPointer p_Extension + ); + +VOID TapDeviceFreeResources + ( + TapExtensionPointer p_Extension + ); + +NTSTATUS CompleteIRP + ( + IN PIRP p_IRP, + IN TapPacketPointer p_PacketBuffer, + IN CCHAR PriorityBoost + ); + +VOID CancelIRPCallback + ( + IN PDEVICE_OBJECT p_DeviceObject, + IN PIRP p_IRP + ); + +VOID CancelIRP + ( + TapExtensionPointer p_Extension, + IN PIRP p_IRP, + BOOLEAN callback + ); + +VOID FlushQueues + ( + TapExtensionPointer p_Extension + ); + +VOID ResetTapAdapterState + ( + TapAdapterPointer p_Adapter + ); + +BOOLEAN ProcessARP + ( + TapAdapterPointer p_Adapter, + const PARP_PACKET src, + const IPADDR adapter_ip, + const IPADDR ip, + const MACADDR mac + ); + +VOID SetMediaStatus + ( + TapAdapterPointer p_Adapter, + BOOLEAN state + ); + +VOID InjectPacket + ( + TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len + ); + +VOID CheckIfDhcpAndPointToPointMode + ( + TapAdapterPointer p_Adapter + ); + +VOID HookDispatchFunctions(); + +#if ENABLE_NONADMIN + +typedef struct _SECURITY_DESCRIPTOR { + unsigned char opaque[20]; +} SECURITY_DESCRIPTOR; + +NTSYSAPI +NTSTATUS +NTAPI +ZwSetSecurityObject ( + IN HANDLE Handle, + IN SECURITY_INFORMATION SecurityInformation, + IN PSECURITY_DESCRIPTOR SecurityDescriptor); + +VOID AllowNonAdmin (TapExtensionPointer p_Extension); + +#endif + + +#endif diff --git a/src/VBox/HostDrivers/VBoxTAP/tapdrvr.c b/src/VBox/HostDrivers/VBoxTAP/tapdrvr.c new file mode 100644 index 000000000..ce8815aa6 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/tapdrvr.c @@ -0,0 +1,2793 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device + * functionality on Windows. Originally derived + * from the CIPE-Win32 project by Damion K. Wilson, + * with extensive modifications by James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//====================================================== +// This driver is designed to work on Win 2000 or higher +// versions of Windows. +// +// It is SMP-safe and handles NDIS 5 power management. +// +// By default we operate as a "tap" virtual ethernet +// 802.3 interface, but we can emulate a "tun" +// interface (point-to-point IPv4) through the +// TAP_IOCTL_CONFIG_POINT_TO_POINT ioctl. +//====================================================== + +#define NDIS_MINIPORT_DRIVER +#define BINARY_COMPATIBLE 0 +#define NDIS50_MINIPORT 1 +#define NDIS_WDM 0 +#define NDIS50 1 +#define NTSTRSAFE_LIB + +// Debug info output +#define ALSO_DBGPRINT 1 +#define DEBUGP_AT_DISPATCH 0 + +//======================================================== +// EXPERIMENTAL -- Configure TAP device object to be +// accessible from non-administrative accounts, based +// on an advanced properties setting. +// +// Duplicates the functionality of OpenVPN's +// --allow-nonadmin directive. +//======================================================== +#define ENABLE_NONADMIN 1 // JYFIXME + + +#include <ndis.h> +#include <ntstrsafe.h> + +#include "lock.h" +#include "constants.h" +#include "common.h" +#include "proto.h" +#include "error.h" +#include "endian.h" +#include "dhcp.h" +#include "types.h" +#include "prototypes.h" + +#include "mem.c" +#include "macinfo.c" +#include "error.c" +#include "dhcp.c" +#include "instance.c" + +#ifdef VBOX +#include <VBox/tapwin32.h> +#endif + +#define IS_UP(ta) \ + ((ta)->m_InterfaceIsRunning && (ta)->m_Extension.m_TapIsRunning) + +#define INCREMENT_STAT(s) ++(s) + +#define NAME_BUFFER_SIZE 80 + +//======================================================== +// Globals +//======================================================== + +NDIS_HANDLE g_NdisWrapperHandle; + +const UINT g_SupportedOIDList[] = { + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAC_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_GEN_RCV_NO_BUFFER, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_SUPPORTED_LIST +}; + +//============================================================ +// Driver Entry +//============================================================ +#pragma NDIS_INIT_FUNCTION (DriverEntry) + +NTSTATUS +DriverEntry (IN PDRIVER_OBJECT p_DriverObject, + IN PUNICODE_STRING p_RegistryPath) +{ + NDIS_STATUS l_Status = NDIS_STATUS_FAILURE; + NDIS_MINIPORT_CHARACTERISTICS *l_Properties = NULL; + + //======================================================== + // Notify NDIS that a new miniport driver is initializing. + //======================================================== + + NdisMInitializeWrapper (&g_NdisWrapperHandle, + p_DriverObject, + p_RegistryPath, NULL); + + //====================== + // Global initialization + //====================== + +#if DBG + MyDebugInit (10000); // Allocate debugging text space +#endif + + if (!InitInstanceList ()) + { + DEBUGP (("[TAP] Allocation failed for adapter instance list\n")); + goto cleanup; + } + + //======================================= + // Set and register miniport entry points + //======================================= + + l_Properties = MemAlloc (sizeof (NDIS_MINIPORT_CHARACTERISTICS), TRUE); + + if (l_Properties == NULL) + { + DEBUGP (("[TAP] Allocation failed for miniport entry points\n")); + goto cleanup; + } + + l_Properties->MajorNdisVersion = TAP_NDIS_MAJOR_VERSION; + l_Properties->MinorNdisVersion = TAP_NDIS_MINOR_VERSION; + l_Properties->InitializeHandler = AdapterCreate; + l_Properties->HaltHandler = AdapterHalt; + l_Properties->ResetHandler = AdapterReset; /* DISPATCH_LEVEL */ + l_Properties->TransferDataHandler = AdapterReceive; /* DISPATCH_LEVEL */ + l_Properties->SendHandler = AdapterTransmit; /* DISPATCH_LEVEL */ + l_Properties->QueryInformationHandler = AdapterQuery; /* DISPATCH_LEVEL */ + l_Properties->SetInformationHandler = AdapterModify; /* DISPATCH_LEVEL */ + + switch (l_Status = + NdisMRegisterMiniport (g_NdisWrapperHandle, l_Properties, + sizeof (NDIS_MINIPORT_CHARACTERISTICS))) + { + case NDIS_STATUS_SUCCESS: + { + DEBUGP (("[TAP] version [%d.%d] %s %s registered miniport successfully\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__)); + DEBUGP (("Registry Path: '%S'\n", p_RegistryPath->Buffer)); + break; + } + + case NDIS_STATUS_BAD_CHARACTERISTICS: + { + DEBUGP (("[TAP] Miniport characteristics were badly defined\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + case NDIS_STATUS_BAD_VERSION: + { + DEBUGP + (("[TAP] NDIS Version is wrong for the given characteristics\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + case NDIS_STATUS_RESOURCES: + { + DEBUGP (("[TAP] Insufficient resources\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + default: + case NDIS_STATUS_FAILURE: + { + DEBUGP (("[TAP] Unknown fatal registration error\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + } + + cleanup: + if (l_Properties) + MemFree (l_Properties, sizeof (NDIS_MINIPORT_CHARACTERISTICS)); + + if (l_Status == NDIS_STATUS_SUCCESS) + NdisMRegisterUnloadHandler (g_NdisWrapperHandle, TapDriverUnload); + else + TapDriverUnload (p_DriverObject); + + return l_Status; +} + +//============================================================ +// Driver Unload +//============================================================ +VOID +TapDriverUnload (IN PDRIVER_OBJECT p_DriverObject) +{ + DEBUGP (("[TAP] version [%d.%d] %s %s unloaded, instances=%d, imbs=%d\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__, + NInstances(), + InstanceMaxBucketSize())); + + FreeInstanceList (); + + //============================== + // Free debugging text space + //============================== +#if DBG + MyDebugFree (); +#endif +} + +//========================================================== +// Adapter Initialization +//========================================================== +NDIS_STATUS AdapterCreate + (OUT PNDIS_STATUS p_ErrorStatus, + OUT PUINT p_MediaIndex, + IN PNDIS_MEDIUM p_Media, + IN UINT p_MediaCount, + IN NDIS_HANDLE p_AdapterHandle, + IN NDIS_HANDLE p_ConfigurationHandle) +{ + TapAdapterPointer l_Adapter = NULL; + + NDIS_MEDIUM l_PreferredMedium = NdisMedium802_3; // Ethernet + BOOLEAN l_MacFromRegistry = FALSE; + UINT l_Index; +#ifndef VBOX + NDIS_STATUS status; +#endif + +#if ENABLE_NONADMIN + BOOLEAN enable_non_admin = FALSE; +#endif + + //==================================== + // Make sure adapter type is supported + //==================================== + + for (l_Index = 0; + l_Index < p_MediaCount && p_Media[l_Index] != l_PreferredMedium; + ++l_Index); + + if (l_Index == p_MediaCount) + { + DEBUGP (("[TAP] Unsupported adapter type [wanted: %d]\n", + l_PreferredMedium)); + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + *p_MediaIndex = l_Index; + + //========================================= + // Allocate memory for TapAdapter structure + //========================================= + + l_Adapter = MemAlloc (sizeof (TapAdapter), TRUE); + + if (l_Adapter == NULL) + { + DEBUGP (("[TAP] Couldn't allocate adapter memory\n")); + return NDIS_STATUS_RESOURCES; + } + + //========================================== + // Inform the NDIS library about significant + // features of our virtual NIC. + //========================================== + + NdisMSetAttributesEx + (p_AdapterHandle, + (NDIS_HANDLE) l_Adapter, + 16, + NDIS_ATTRIBUTE_DESERIALIZE + | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT + | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT + | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, + NdisInterfaceInternal); + + //===================================== + // Initialize simple Adapter parameters + //===================================== + + l_Adapter->m_Lookahead = DEFAULT_PACKET_LOOKAHEAD; + l_Adapter->m_Medium = l_PreferredMedium; + l_Adapter->m_DeviceState = '?'; + l_Adapter->m_MiniportAdapterHandle = p_AdapterHandle; + + //================================== + // Allocate spinlock for controlling + // access to multicast address list. + //================================== + NdisAllocateSpinLock (&l_Adapter->m_MCLock); + l_Adapter->m_MCLockAllocated = TRUE; + + //==================================================== + // Register a shutdown handler which will be called + // on system restart/shutdown to halt our virtual NIC. + //==================================================== + + NdisMRegisterAdapterShutdownHandler (p_AdapterHandle, l_Adapter, + AdapterHalt); + l_Adapter->m_RegisteredAdapterShutdownHandler = TRUE; + + //============================================ + // Get parameters from registry which were set + // in the adapter advanced properties dialog. + //============================================ + { + NDIS_STATUS status; + NDIS_HANDLE configHandle; + NDIS_CONFIGURATION_PARAMETER *parm; + + // set defaults in case our registry query fails + l_Adapter->m_MTU = ETHERNET_MTU; + l_Adapter->m_MediaStateAlwaysConnected = FALSE; + l_Adapter->m_MediaState = FALSE; + + NdisOpenConfiguration (&status, &configHandle, p_ConfigurationHandle); + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP (("[TAP] Couldn't open adapter registry\n")); + AdapterFreeResources (l_Adapter); + return status; + } + + //==================================== + // Allocate and construct adapter name + //==================================== + { + NDIS_STRING key = NDIS_STRING_CONST("MiniportName"); + NdisReadConfiguration (&status, &parm, configHandle, &key, NdisParameterString); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterString) + { + DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%s)\n", parm->ParameterData.StringData.Buffer)); + + if (RtlUnicodeStringToAnsiString ( + &l_Adapter->m_NameAnsi, + &parm->ParameterData.StringData, + TRUE) != STATUS_SUCCESS) + { + DEBUGP (("[TAP] RtlUnicodeStringToAnsiString MiniportName failed\n")); + status = NDIS_STATUS_RESOURCES; + } + } + } else { + /* "MiniportName" is available only XP and above. Not on Windows 2000. */ + NDIS_STRING key = NDIS_STRING_CONST("NdisVersion"); + NdisReadConfiguration (&status, &parm, configHandle, &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterData.IntegerData == 0x50000) + { + /* Fallback for Windows 2000 with NDIS version 5.00.00 + Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */ + DEBUGP (("[TAP] NdisReadConfiguration NdisVersion (Int=%X)\n", parm->ParameterData.IntegerData)); + if (RtlUnicodeStringToAnsiString ( + &l_Adapter->m_NameAnsi, + &((PNDIS_MINIPORT_BLOCK) p_AdapterHandle)->MiniportName, + TRUE) != STATUS_SUCCESS) + { + DEBUGP (("[TAP] RtlUnicodeStringToAnsiString MiniportName (W2K) failed\n")); + status = NDIS_STATUS_RESOURCES; + } + } + } + } + } + + /* Can't continue without name (see macro 'NAME') */ + if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer) + { + NdisCloseConfiguration (configHandle); + AdapterFreeResources (l_Adapter); + return NDIS_STATUS_RESOURCES; + } + + /* Read MTU setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MTU"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + int mtu = parm->ParameterData.IntegerData; + if (mtu < MINIMUM_MTU) + mtu = MINIMUM_MTU; + if (mtu > MAXIMUM_MTU) + mtu = MAXIMUM_MTU; + l_Adapter->m_MTU = mtu; + } + } + } + + /* Read Media Status setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MediaStatus"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + if (parm->ParameterData.IntegerData) + { + l_Adapter->m_MediaStateAlwaysConnected = TRUE; + l_Adapter->m_MediaState = TRUE; + } + } + } + } + +#if ENABLE_NONADMIN + /* Read AllowNonAdmin setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + if (parm->ParameterData.IntegerData) + { + enable_non_admin = TRUE; + } + } + } + } +#endif + + /* Read optional MAC setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MAC"); + ANSI_STRING mac_string; + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterString); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterString) + { + if (RtlUnicodeStringToAnsiString (&mac_string, &parm->ParameterData.StringData, TRUE) == STATUS_SUCCESS) + { + l_MacFromRegistry = ParseMAC (l_Adapter->m_MAC, mac_string.Buffer); + RtlFreeAnsiString (&mac_string); + } + } + } + } + + NdisCloseConfiguration (configHandle); + + DEBUGP (("[%s] MTU=%d\n", NAME (l_Adapter), l_Adapter->m_MTU)); + } + + //================================== + // Store and update MAC address info + //================================== + + if (!l_MacFromRegistry) + GenerateRandomMac (l_Adapter->m_MAC, NAME (l_Adapter)); + + DEBUGP (("[%s] Using MAC %x:%x:%x:%x:%x:%x\n", + NAME (l_Adapter), + l_Adapter->m_MAC[0], l_Adapter->m_MAC[1], l_Adapter->m_MAC[2], + l_Adapter->m_MAC[3], l_Adapter->m_MAC[4], l_Adapter->m_MAC[5])); + + //================== + // Set broadcast MAC + //================== + { + int i; + for (i = 0; i < sizeof (MACADDR); ++i) + l_Adapter->m_MAC_Broadcast[i] = 0xFF; + } + + //==================================== + // Initialize TAP device + //==================================== + { + NDIS_STATUS tap_status; + tap_status = CreateTapDevice (&l_Adapter->m_Extension, NAME (l_Adapter)); + if (tap_status != NDIS_STATUS_SUCCESS) + { + AdapterFreeResources (l_Adapter); + return tap_status; + } + } + + if (!AddAdapterToInstanceList (l_Adapter)) + { + NOTE_ERROR (); + TapDeviceFreeResources (&l_Adapter->m_Extension); + AdapterFreeResources (l_Adapter); + return NDIS_STATUS_RESOURCES; + } + + l_Adapter->m_InterfaceIsRunning = TRUE; + +#if ENABLE_NONADMIN + if (enable_non_admin) + AllowNonAdmin (&l_Adapter->m_Extension); +#endif + + return NDIS_STATUS_SUCCESS; +} + +VOID +AdapterHalt (IN NDIS_HANDLE p_AdapterContext) +{ + BOOLEAN status; + + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + + NOTE_ERROR (); + + l_Adapter->m_InterfaceIsRunning = FALSE; + + DEBUGP (("[%s] is being halted\n", NAME (l_Adapter))); + + DestroyTapDevice (&l_Adapter->m_Extension); + + // Free resources + DEBUGP (("[%s] Freeing Resources\n", NAME (l_Adapter))); + AdapterFreeResources (l_Adapter); + + status = RemoveAdapterFromInstanceList (l_Adapter); + DEBUGP (("[TAP] RemoveAdapterFromInstanceList returned %d\n", (int) status)); + + DEBUGP (("[TAP] version [%d.%d] %s %s AdapterHalt returning\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__)); +} + +VOID +AdapterFreeResources (TapAdapterPointer p_Adapter) +{ + MYASSERT (!p_Adapter->m_CalledAdapterFreeResources); + p_Adapter->m_CalledAdapterFreeResources = TRUE; + + if (p_Adapter->m_NameAnsi.Buffer) + RtlFreeAnsiString (&p_Adapter->m_NameAnsi); + + if (p_Adapter->m_RegisteredAdapterShutdownHandler) + NdisMDeregisterAdapterShutdownHandler (p_Adapter->m_MiniportAdapterHandle); + + if (p_Adapter->m_MCLockAllocated) + NdisFreeSpinLock (&l_Adapter->m_MCLock); +} + +VOID +DestroyTapDevice (TapExtensionPointer p_Extension) +{ + DEBUGP (("[%s] Destroying tap device\n", p_Extension->m_TapName)); + + //====================================== + // Let clients know we are shutting down + //====================================== + p_Extension->m_TapIsRunning = FALSE; + p_Extension->m_TapOpens = 0; + p_Extension->m_Halt = TRUE; + + //===================================== + // If we are concurrently executing in + // TapDeviceHook or AdapterTransmit, + // give those calls time to finish. + // Note that we must be running at IRQL + // < DISPATCH_LEVEL in order to call + // NdisMSleep. + //===================================== + NdisMSleep (500000); + + //=========================================================== + // Exhaust IRP and packet queues. Any pending IRPs will + // be cancelled, causing user-space to get this error + // on overlapped reads: + // The I/O operation has been aborted because of either a + // thread exit or an application request. (code=995) + // It's important that user-space close the device handle + // when this code is returned, so that when we finally + // do a NdisMDeregisterDevice, the device reference count + // is 0. Otherwise the driver will not unload even if the + // the last adapter has been halted. + //=========================================================== + FlushQueues (p_Extension); + NdisMSleep (500000); // give user space time to respond to IRP cancel + + TapDeviceFreeResources (p_Extension); +} + +VOID +TapDeviceFreeResources (TapExtensionPointer p_Extension) +{ + MYASSERT (p_Extension); + MYASSERT (!p_Extension->m_CalledTapDeviceFreeResources); + p_Extension->m_CalledTapDeviceFreeResources = TRUE; + + if (p_Extension->m_PacketQueue) + QueueFree (p_Extension->m_PacketQueue); + if (p_Extension->m_IrpQueue) + QueueFree (p_Extension->m_IrpQueue); + + if (p_Extension->m_CreatedUnicodeLinkName) + RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName); + + //========================================================== + // According to DDK docs, the device is not actually deleted + // until its reference count falls to zero. That means we + // still need to gracefully fail TapDeviceHook requests + // after this point, otherwise ugly things would happen if + // the device was disabled (e.g. in the network connections + // control panel) while a userspace app still held an open + // file handle to it. + //========================================================== + + if (p_Extension->m_TapDevice) + { + BOOLEAN status; + status = (NdisMDeregisterDevice (p_Extension->m_TapDeviceHandle) + == NDIS_STATUS_SUCCESS); + DEBUGP (("[TAP] Deregistering TAP device, status=%d\n", (int)status)); + } + + if (p_Extension->m_TapName) + MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE); + + if (p_Extension->m_AllocatedSpinlocks) + NdisFreeSpinLock (&p_Extension->m_QueueLock); +} + +//======================================================================== +// Tap Device Initialization +//======================================================================== + +NDIS_STATUS +CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name) +{ +# define SIZEOF_DISPATCH (sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1)) + PDRIVER_DISPATCH *l_Dispatch = NULL; + ANSI_STRING l_TapString, l_LinkString; + UNICODE_STRING l_TapUnicode; + BOOLEAN l_FreeTapUnicode = FALSE; + NTSTATUS l_Status, l_Return = NDIS_STATUS_SUCCESS; + const char *l_UsableName; + + DEBUGP (("[TAP] version [%d.%d] creating tap device: %s\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + p_Name)); + + NdisZeroMemory (p_Extension, sizeof (TapExtension)); + + INIT_MUTEX (&p_Extension->m_OpenCloseMutex); + + l_LinkString.Buffer = NULL; + l_TapString.Buffer = NULL; + + l_TapString.MaximumLength = l_LinkString.MaximumLength = NAME_BUFFER_SIZE; + + //======================================= + // Set TAP device entry points + //======================================= + + if ((l_Dispatch = MemAlloc (SIZEOF_DISPATCH, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP dispatch table\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + l_Dispatch[IRP_MJ_DEVICE_CONTROL] = TapDeviceHook; + l_Dispatch[IRP_MJ_READ] = TapDeviceHook; + l_Dispatch[IRP_MJ_WRITE] = TapDeviceHook; + l_Dispatch[IRP_MJ_CREATE] = TapDeviceHook; + l_Dispatch[IRP_MJ_CLOSE] = TapDeviceHook; + + //================================== + // Find the beginning of the GUID + //================================== + l_UsableName = p_Name; + while (*l_UsableName != '{') + { + if (*l_UsableName == '\0') + { + DEBUGP (("[%s] couldn't find leading '{' in name\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + ++l_UsableName; + } + + //================================== + // Allocate pool for TAP device name + //================================== + + if ((p_Extension->m_TapName = l_TapString.Buffer = + MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP name buffer\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //================================================ + // Allocate pool for TAP symbolic link name buffer + //================================================ + + if ((l_LinkString.Buffer = + MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP symbolic link name buffer\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //======================================================= + // Set TAP device name + //======================================================= + + l_Status = RtlStringCchPrintfExA + (l_TapString.Buffer, + l_TapString.MaximumLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, + "%s%s%s", + SYSDEVICEDIR, + l_UsableName, + TAPSUFFIX); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't format TAP device name\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_TapString.Length = (USHORT) strlen (l_TapString.Buffer); + + DEBUGP (("TAP DEV NAME: '%s'\n", l_TapString.Buffer)); + + //======================================================= + // Set TAP link name + //======================================================= + + l_Status = RtlStringCchPrintfExA + (l_LinkString.Buffer, + l_LinkString.MaximumLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, + "%s%s%s", + USERDEVICEDIR, + l_UsableName, + TAPSUFFIX); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't format TAP device symbolic link\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_LinkString.Length = (USHORT) strlen (l_LinkString.Buffer); + + DEBUGP (("TAP LINK NAME: '%s'\n", l_LinkString.Buffer)); + + //================================================== + // Convert strings to unicode + //================================================== + if (RtlAnsiStringToUnicodeString (&l_TapUnicode, &l_TapString, TRUE) != + STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't alloc TAP unicode name buffer\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_FreeTapUnicode = TRUE; + + if (RtlAnsiStringToUnicodeString + (&p_Extension->m_UnicodeLinkName, &l_LinkString, TRUE) + != STATUS_SUCCESS) + { + DEBUGP + (("[%s] Couldn't allocate unicode string for symbolic link name\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + p_Extension->m_CreatedUnicodeLinkName = TRUE; + + //================================================== + // Create new TAP device with symbolic + // link and associate with adapter. + //================================================== + + l_Status = NdisMRegisterDevice + (g_NdisWrapperHandle, + &l_TapUnicode, + &p_Extension->m_UnicodeLinkName, + l_Dispatch, + &p_Extension->m_TapDevice, + &p_Extension->m_TapDeviceHandle + ); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't be created\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + /* Set TAP device flags */ + p_Extension->m_TapDevice->Flags |= DO_DIRECT_IO; + + //======================================================== + // Initialize Packet and IRP queues. + // + // The packet queue is used to buffer data which has been + // "transmitted" by the virtual NIC, before user space + // has had a chance to read it. + // + // The IRP queue is used to buffer pending I/O requests + // from userspace, i.e. read requests on the TAP device + // waiting for the system to "transmit" something through + // the virtual NIC. + // + // Basically, packets in the packet queue are used + // to satisfy IRP requests in the IRP queue. + // + // QueueLock is used to lock the packet queue used + // for the TAP-Win32 NIC -> User Space packet flow direction. + // + // All accesses to packet or IRP queues should be + // bracketed by the QueueLock spinlock, + // in order to be SMP-safe. + //======================================================== + + NdisAllocateSpinLock (&p_Extension->m_QueueLock); + p_Extension->m_AllocatedSpinlocks = TRUE; + + p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE); + p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE); + + if (!p_Extension->m_PacketQueue + || !p_Extension->m_IrpQueue) + { + DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //======================== + // Finalize initialization + //======================== + + p_Extension->m_TapIsRunning = TRUE; + + DEBUGP (("[%s] successfully created TAP device [%s]\n", p_Name, + p_Extension->m_TapName)); + + cleanup: + if (l_FreeTapUnicode) + RtlFreeUnicodeString (&l_TapUnicode); + if (l_LinkString.Buffer) + MemFree (l_LinkString.Buffer, NAME_BUFFER_SIZE); + if (l_Dispatch) + MemFree (l_Dispatch, SIZEOF_DISPATCH); + + if (l_Return != NDIS_STATUS_SUCCESS) + TapDeviceFreeResources (p_Extension); + + return l_Return; +} +#undef SIZEOF_DISPATCH + +//======================================================== +// Adapter Control +//======================================================== +NDIS_STATUS +AdapterReset (OUT PBOOLEAN p_AddressingReset, IN NDIS_HANDLE p_AdapterContext) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + DEBUGP (("[%s] is resetting\n", NAME (l_Adapter))); + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS AdapterReceive + (OUT PNDIS_PACKET p_Packet, + OUT PUINT p_Transferred, + IN NDIS_HANDLE p_AdapterContext, + IN NDIS_HANDLE p_ReceiveContext, + IN UINT p_Offset, + IN UINT p_ToTransfer) +{ + DEBUGP(("AdapterReceive %08x size=%x\n", p_Packet, p_ToTransfer)); + return NDIS_STATUS_SUCCESS; +} + +//============================================================== +// Adapter Option Query/Modification +//============================================================== +NDIS_STATUS AdapterQuery +(IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesWritten, OUT PULONG p_BytesNeeded) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + TapAdapterQuery l_Query, *l_QueryPtr = &l_Query; + NDIS_STATUS l_Status = NDIS_STATUS_SUCCESS; + UINT l_QueryLength = 4; +#ifndef VBOX + BOOLEAN lock_succeeded; +#endif + + NdisZeroMemory (&l_Query, sizeof (l_Query)); + + switch (p_OID) + { + //=================================================================== + // Vendor & Driver version Info + //=================================================================== + case OID_GEN_VENDOR_DESCRIPTION: + l_QueryPtr = (TapAdapterQueryPointer) PRODUCT_STRING; + l_QueryLength = strlen (PRODUCT_STRING) + 1; + break; + + case OID_GEN_VENDOR_ID: + l_Query.m_Long = 0xffffff; + break; + + case OID_GEN_DRIVER_VERSION: + l_Query.m_Short = + (((USHORT) TAP_NDIS_MAJOR_VERSION) << 8 | (USHORT) + TAP_NDIS_MINOR_VERSION); + l_QueryLength = sizeof (unsigned short); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + l_Query.m_Long = + (((USHORT) TAP_DRIVER_MAJOR_VERSION) << 8 | (USHORT) + TAP_DRIVER_MINOR_VERSION); + break; + + //================================================================= + // Statistics + //================================================================= + case OID_GEN_RCV_NO_BUFFER: + l_Query.m_Long = 0; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + l_Query.m_Long = 0; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + l_Query.m_Long = 0; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + l_Query.m_Long = 0; + break; + + case OID_GEN_XMIT_OK: + l_Query.m_Long = l_Adapter->m_Tx; + break; + + case OID_GEN_RCV_OK: + l_Query.m_Long = l_Adapter->m_Rx; + break; + + case OID_GEN_XMIT_ERROR: + l_Query.m_Long = l_Adapter->m_TxErr; + break; + + case OID_GEN_RCV_ERROR: + l_Query.m_Long = l_Adapter->m_RxErr; + break; + + //=================================================================== + // Device & Protocol Options + //=================================================================== + case OID_GEN_SUPPORTED_LIST: + l_QueryPtr = (TapAdapterQueryPointer) g_SupportedOIDList; + l_QueryLength = sizeof (g_SupportedOIDList); + break; + + case OID_GEN_MAC_OPTIONS: + // This MUST be here !!! + l_Query.m_Long = (NDIS_MAC_OPTION_RECEIVE_SERIALIZED + | NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA + | NDIS_MAC_OPTION_NO_LOOPBACK + | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND); + + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + l_Query.m_Long = + (NDIS_PACKET_TYPE_ALL_LOCAL | + NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL); + + break; + + case OID_GEN_PROTOCOL_OPTIONS: + l_Query.m_Long = 0; + break; + + //================================================================== + // Device Info + //================================================================== + case OID_GEN_MEDIA_CONNECT_STATUS: + l_Query.m_Long = l_Adapter->m_MediaState + ? NdisMediaStateConnected : NdisMediaStateDisconnected; + break; + + case OID_GEN_HARDWARE_STATUS: + l_Query.m_HardwareStatus = NdisHardwareStatusReady; + l_QueryLength = sizeof (NDIS_HARDWARE_STATUS); + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + l_Query.m_Medium = l_Adapter->m_Medium; + l_QueryLength = sizeof (NDIS_MEDIUM); + break; + + case OID_GEN_PHYSICAL_MEDIUM: + l_Query.m_PhysicalMedium = NdisPhysicalMediumUnspecified; + l_QueryLength = sizeof (NDIS_PHYSICAL_MEDIUM); + break; + + case OID_GEN_LINK_SPEED: + l_Query.m_Long = 100000; + break; + + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + COPY_MAC (l_Query.m_MacAddress, l_Adapter->m_MAC); + l_QueryLength = sizeof (MACADDR); + break; + + //================================================================== + // Limits + //================================================================== + + case OID_GEN_MAXIMUM_SEND_PACKETS: + l_Query.m_Long = 1; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + l_Query.m_Long = NIC_MAX_MCAST_LIST; + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + l_Query.m_Long = l_Adapter->m_Lookahead; + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + l_Query.m_Long = DEFAULT_PACKET_LOOKAHEAD; + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_TRANSMIT_BUFFER_SPACE: + l_Query.m_Long = l_Adapter->m_MTU; + break; + + case OID_PNP_CAPABILITIES: + do + { + PNDIS_PNP_CAPABILITIES pPNPCapabilities; + PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct; + + if (p_BufferLength >= sizeof (NDIS_PNP_CAPABILITIES)) + { + pPNPCapabilities = (PNDIS_PNP_CAPABILITIES) (p_Buffer); + + // + // Setting up the buffer to be returned + // to the Protocol above the Passthru miniport + // + pPMstruct = &pPNPCapabilities->WakeUpCapabilities; + pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified; + } + l_QueryLength = sizeof (NDIS_PNP_CAPABILITIES); + } + while (FALSE); + break; + case OID_PNP_QUERY_POWER: + break; + + // Required OIDs that we don't support + + case OID_GEN_SUPPORTED_GUIDS: + case OID_GEN_MEDIA_CAPABILITIES: + case OID_TCP_TASK_OFFLOAD: + case OID_FFP_SUPPORT: + l_Status = NDIS_STATUS_INVALID_OID; + break; + + // Optional stats OIDs + + case OID_GEN_DIRECTED_BYTES_XMIT: + case OID_GEN_DIRECTED_FRAMES_XMIT: + case OID_GEN_MULTICAST_BYTES_XMIT: + case OID_GEN_MULTICAST_FRAMES_XMIT: + case OID_GEN_BROADCAST_BYTES_XMIT: + case OID_GEN_BROADCAST_FRAMES_XMIT: + case OID_GEN_DIRECTED_BYTES_RCV: + case OID_GEN_DIRECTED_FRAMES_RCV: + case OID_GEN_MULTICAST_BYTES_RCV: + case OID_GEN_MULTICAST_FRAMES_RCV: + case OID_GEN_BROADCAST_BYTES_RCV: + case OID_GEN_BROADCAST_FRAMES_RCV: + l_Status = NDIS_STATUS_INVALID_OID; + break; + + //=================================================================== + // Not Handled + //=================================================================== + default: + DEBUGP (("[%s] Unhandled OID %lx\n", NAME (l_Adapter), p_OID)); + l_Status = NDIS_STATUS_INVALID_OID; + break; + } + + if (l_Status != NDIS_STATUS_SUCCESS) + ; + else if (l_QueryLength > p_BufferLength) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = l_QueryLength; + } + else + NdisMoveMemory (p_Buffer, (PVOID) l_QueryPtr, + (*p_BytesWritten = l_QueryLength)); + + return l_Status; +} + +NDIS_STATUS AdapterModify +(IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesRead, + OUT PULONG p_BytesNeeded) +{ + TapAdapterQueryPointer l_Query = (TapAdapterQueryPointer) p_Buffer; + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + NDIS_STATUS l_Status = NDIS_STATUS_INVALID_OID; +#ifndef VBOX + ULONG l_Long; +#endif + + switch (p_OID) + { + //================================================================== + // Device Info + //================================================================== + case OID_802_3_MULTICAST_LIST: + DEBUGP (("[%s] Setting [OID_802_3_MULTICAST_LIST]\n", + NAME (l_Adapter))); + + *p_BytesNeeded = sizeof (ETH_ADDR); + *p_BytesRead = p_BufferLength; + + if (p_BufferLength % sizeof (ETH_ADDR)) + l_Status = NDIS_STATUS_INVALID_LENGTH; + else if (p_BufferLength > sizeof (MC_LIST)) + { + l_Status = NDIS_STATUS_MULTICAST_FULL; + *p_BytesNeeded = sizeof (MC_LIST); + } + else + { + NdisAcquireSpinLock (&l_Adapter->m_MCLock); + + NdisZeroMemory(&l_Adapter->m_MCList, sizeof (MC_LIST)); + + NdisMoveMemory(&l_Adapter->m_MCList, + p_Buffer, + p_BufferLength); + + l_Adapter->m_MCListSize = p_BufferLength / sizeof (ETH_ADDR); + + NdisReleaseSpinLock (&l_Adapter->m_MCLock); + + l_Status = NDIS_STATUS_SUCCESS; + } + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = 4; + + if (p_BufferLength >= sizeof (ULONG)) + { + DEBUGP + (("[%s] Setting [OID_GEN_CURRENT_PACKET_FILTER] to [0x%02lx]\n", + NAME (l_Adapter), l_Query->m_Long)); + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = sizeof (ULONG); + } + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + if (p_BufferLength < sizeof (ULONG)) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = 4; + } + else if (l_Query->m_Long > DEFAULT_PACKET_LOOKAHEAD + || l_Query->m_Long <= 0) + { + l_Status = NDIS_STATUS_INVALID_DATA; + } + else + { + DEBUGP (("[%s] Setting [OID_GEN_CURRENT_LOOKAHEAD] to [%d]\n", + NAME (l_Adapter), l_Query->m_Long)); + l_Adapter->m_Lookahead = l_Query->m_Long; + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = sizeof (ULONG); + } + break; + + case OID_GEN_NETWORK_LAYER_ADDRESSES: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + case OID_GEN_TRANSPORT_HEADER_OFFSET: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + case OID_PNP_SET_POWER: + do + { + NDIS_DEVICE_POWER_STATE NewDeviceState; + + NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE) p_Buffer); + + switch (NewDeviceState) + { + case NdisDeviceStateD0: + l_Adapter->m_DeviceState = '0'; + break; + case NdisDeviceStateD1: + l_Adapter->m_DeviceState = '1'; + break; + case NdisDeviceStateD2: + l_Adapter->m_DeviceState = '2'; + break; + case NdisDeviceStateD3: + l_Adapter->m_DeviceState = '3'; + break; + default: + l_Adapter->m_DeviceState = '?'; + break; + } + + l_Status = NDIS_STATUS_FAILURE; + + // + // Check for invalid length + // + if (p_BufferLength < sizeof (NDIS_DEVICE_POWER_STATE)) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (NewDeviceState > NdisDeviceStateD0) + { + l_Adapter->m_InterfaceIsRunning = FALSE; + DEBUGP (("[%s] Power management device state OFF\n", + NAME (l_Adapter))); + } + else + { + l_Adapter->m_InterfaceIsRunning = TRUE; + DEBUGP (("[%s] Power management device state ON\n", + NAME (l_Adapter))); + } + + l_Status = NDIS_STATUS_SUCCESS; + } + while (FALSE); + + if (l_Status == NDIS_STATUS_SUCCESS) + { + *p_BytesRead = sizeof (NDIS_DEVICE_POWER_STATE); + *p_BytesNeeded = 0; + } + else + { + *p_BytesRead = 0; + *p_BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE); + } + break; + + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ADD_WAKE_UP_PATTERN: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + default: + DEBUGP (("[%s] Can't set value for OID %lx\n", NAME (l_Adapter), + p_OID)); + l_Status = NDIS_STATUS_INVALID_OID; + *p_BytesRead = *p_BytesNeeded = 0; + break; + } + + return l_Status; +} + +//==================================================================== +// Adapter Transmission +//==================================================================== +NDIS_STATUS +AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, + IN PNDIS_PACKET p_Packet, + IN UINT p_Flags) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + ULONG l_Index = 0, l_BufferLength = 0, l_PacketLength = 0; + PIRP l_IRP; + TapPacketPointer l_PacketBuffer; + PNDIS_BUFFER l_NDIS_Buffer; + PUCHAR l_Buffer; + PVOID result; + + NdisQueryPacket (p_Packet, NULL, NULL, &l_NDIS_Buffer, &l_PacketLength); + +//// DEBUGP(("AdapterTransmit %08x size=%x\n", l_NDIS_Buffer, l_PacketLength)); + + //==================================================== + // Here we abandon the transmission attempt if any of + // the parameters is wrong or memory allocation fails + // but we do not indicate failure. The packet is + // silently dropped. + //==================================================== + + if (l_PacketLength < ETHERNET_HEADER_SIZE || l_PacketLength > 65535) + goto exit_fail; + else if (!l_Adapter->m_Extension.m_TapOpens || !l_Adapter->m_MediaState) + goto exit_success; // Nothing is bound to the TAP device + + if (NdisAllocateMemoryWithTag (&l_PacketBuffer, + TAP_PACKET_SIZE (l_PacketLength), + '5PAT') != NDIS_STATUS_SUCCESS) + goto exit_no_resources; + + if (l_PacketBuffer == NULL) + goto exit_no_resources; + + l_PacketBuffer->m_SizeFlags = (l_PacketLength & TP_SIZE_MASK); + + //=========================== + // Reassemble packet contents + //=========================== + + __try + { + for (l_Index = 0; l_NDIS_Buffer && l_Index < l_PacketLength; + l_Index += l_BufferLength) + { + NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer, + &l_BufferLength); + NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer, + l_BufferLength); + NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer); + } + + DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength); + + //===================================================== + // Are we running in DHCP server masquerade mode? + // + // If so, catch both DHCP requests and ARP queries + // to resolve the address of our virtual DHCP server. + //===================================================== + if (l_Adapter->m_dhcp_enabled) + { + const ETH_HEADER *eth = (ETH_HEADER *) l_PacketBuffer->m_Data; + const IPHDR *ip = (IPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER)); + const UDPHDR *udp = (UDPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR)); + + // ARP packet? + if (l_PacketLength == sizeof (ARP_PACKET) + && eth->proto == htons (ETH_P_ARP) + && l_Adapter->m_dhcp_server_arp) + { + if (ProcessARP (l_Adapter, + (PARP_PACKET) l_PacketBuffer->m_Data, + l_Adapter->m_dhcp_addr, + l_Adapter->m_dhcp_server_ip, + l_Adapter->m_dhcp_server_mac)) + goto no_queue; + } + + // DHCP packet? + else if (l_PacketLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + && eth->proto == htons (ETH_P_IP) + && ip->version_len == 0x45 // IPv4, 20 byte header + && ip->protocol == IPPROTO_UDP + && udp->dest == htons (BOOTPS_PORT)) + { + const DHCP *dhcp = (DHCP *) (l_PacketBuffer->m_Data + + sizeof (ETH_HEADER) + + sizeof (IPHDR) + + sizeof (UDPHDR)); + + const int optlen = l_PacketLength + - sizeof (ETH_HEADER) + - sizeof (IPHDR) + - sizeof (UDPHDR) + - sizeof (DHCP); + + if (optlen > 0) // we must have at least one DHCP option + { + if (ProcessDHCP (l_Adapter, eth, ip, udp, dhcp, optlen)) + goto no_queue; + } + else + goto no_queue; + } + } + + //=============================================== + // In Point-To-Point mode, check to see whether + // packet is ARP or IPv4 (if neither, then drop). + //=============================================== + if (l_Adapter->m_PointToPoint) + { + ETH_HEADER *e; + + if (l_PacketLength < ETHERNET_HEADER_SIZE) + goto no_queue; + + e = (ETH_HEADER *) l_PacketBuffer->m_Data; + + switch (ntohs (e->proto)) + { + case ETH_P_ARP: + + // Make sure that packet is the + // right size for ARP. + if (l_PacketLength != sizeof (ARP_PACKET)) + goto no_queue; + + ProcessARP (l_Adapter, + (PARP_PACKET) l_PacketBuffer->m_Data, + l_Adapter->m_localIP, + l_Adapter->m_remoteIP, + l_Adapter->m_TapToUser.dest); + + default: + goto no_queue; + + case ETH_P_IP: + + // Make sure that packet is large + // enough to be IPv4. + if (l_PacketLength + < ETHERNET_HEADER_SIZE + IP_HEADER_SIZE) + goto no_queue; + + // Only accept directed packets, + // not broadcasts. + if (memcmp (e, &l_Adapter->m_TapToUser, ETHERNET_HEADER_SIZE)) + goto no_queue; + + // Packet looks like IPv4, queue it. + l_PacketBuffer->m_SizeFlags |= TP_POINT_TO_POINT; + } + } + + //=============================================== + // Push packet onto queue to wait for read from + // userspace. + //=============================================== + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + result = NULL; + if (IS_UP (l_Adapter)) + result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer); + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if ((TapPacketPointer) result != l_PacketBuffer) + { + // adapter receive overrun + INCREMENT_STAT (l_Adapter->m_TxErr); + goto no_queue; + } + else + { + INCREMENT_STAT (l_Adapter->m_Tx); + } + + //============================================================ + // Cycle through IRPs and packets, try to satisfy each pending + // IRP with a queued packet. + //============================================================ + while (TRUE) + { + l_IRP = NULL; + l_PacketBuffer = NULL; + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueueCount (l_Adapter->m_Extension.m_PacketQueue) + && QueueCount (l_Adapter->m_Extension.m_IrpQueue)) + { + l_IRP = (PIRP) QueuePop (l_Adapter->m_Extension.m_IrpQueue); + l_PacketBuffer = (TapPacketPointer) + QueuePop (l_Adapter->m_Extension.m_PacketQueue); + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + MYASSERT ((l_IRP != NULL) + (l_PacketBuffer != NULL) != 1); + + if (l_IRP && l_PacketBuffer) + { + CompleteIRP (l_IRP, + l_PacketBuffer, + IO_NETWORK_INCREMENT); + } + else + break; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + + return NDIS_STATUS_SUCCESS; + + no_queue: + NdisFreeMemory (l_PacketBuffer, + TAP_PACKET_SIZE (l_PacketLength), + 0); + + exit_success: + return NDIS_STATUS_SUCCESS; + + exit_fail: + return NDIS_STATUS_FAILURE; + + exit_no_resources: + return NDIS_STATUS_RESOURCES; +} + +//====================================================================== +// Hooks for catching TAP device IRP's. +//====================================================================== + +NTSTATUS +TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) +{ + TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject); + PIO_STACK_LOCATION l_IrpSp; + NTSTATUS l_Status = STATUS_SUCCESS; +#ifndef VBOX + BOOLEAN accessible; +#endif + + l_IrpSp = IoGetCurrentIrpStackLocation (p_IRP); + + p_IRP->IoStatus.Status = STATUS_SUCCESS; + p_IRP->IoStatus.Information = 0; + + if (!l_Adapter || l_Adapter->m_Extension.m_Halt) + { + DEBUGP (("TapDeviceHook called when TAP device is halted, MajorFunction=%d\n", + (int)l_IrpSp->MajorFunction)); + + if (l_IrpSp->MajorFunction == IRP_MJ_CLOSE) + { + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else + { + p_IRP->IoStatus.Status = STATUS_NO_SUCH_DEVICE; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + return STATUS_NO_SUCH_DEVICE; + } + } + + switch (l_IrpSp->MajorFunction) + { + //=========================================================== + // Ioctl call handlers + //=========================================================== + case IRP_MJ_DEVICE_CONTROL: + { + switch (l_IrpSp->Parameters.DeviceIoControl.IoControlCode) + { + case TAP_IOCTL_GET_MAC: + { + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= sizeof (MACADDR)) + { + COPY_MAC (p_IRP->AssociatedIrp.SystemBuffer, + l_Adapter->m_MAC); + p_IRP->IoStatus.Information = sizeof (MACADDR); + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + case TAP_IOCTL_GET_VERSION: + { + const ULONG size = sizeof (ULONG) * 3; + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= size) + { + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0] + = TAP_DRIVER_MAJOR_VERSION; + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[1] + = TAP_DRIVER_MINOR_VERSION; + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[2] +#if DBG + = 1; +#else + = 0; +#endif + p_IRP->IoStatus.Information = size; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + case TAP_IOCTL_GET_MTU: + { + const ULONG size = sizeof (ULONG) * 1; + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= size) + { + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0] + = l_Adapter->m_MTU; + p_IRP->IoStatus.Information = size; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + case TAP_IOCTL_GET_INFO: + { + char state[16]; + if (l_Adapter->m_InterfaceIsRunning) + state[0] = 'A'; + else + state[0] = 'a'; + if (l_Adapter->m_Extension.m_TapIsRunning) + state[1] = 'T'; + else + state[1] = 't'; + state[2] = l_Adapter->m_DeviceState; + if (l_Adapter->m_MediaStateAlwaysConnected) + state[3] = 'C'; + else + state[3] = 'c'; + state[4] = '\0'; + + p_IRP->IoStatus.Status = l_Status = RtlStringCchPrintfExA ( + ((LPTSTR) (p_IRP->AssociatedIrp.SystemBuffer)), + l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]", + state, + g_LastErrorFilename, + g_LastErrorLineNumber, + (int)l_Adapter->m_Extension.m_NumTapOpens, + (int)l_Adapter->m_Tx, + (int)l_Adapter->m_TxErr, + (int)l_Adapter->m_Rx, + (int)l_Adapter->m_RxErr, + (int)l_Adapter->m_Extension.m_IrpQueue->size, + (int)l_Adapter->m_Extension.m_IrpQueue->max_size, + (int)IRP_QUEUE_SIZE, + (int)l_Adapter->m_Extension.m_PacketQueue->size, + (int)l_Adapter->m_Extension.m_PacketQueue->max_size, + (int)PACKET_QUEUE_SIZE + ); + + p_IRP->IoStatus.Information + = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + break; + } + +#if DBG + case TAP_IOCTL_GET_LOG_LINE: + { + if (GetDebugLine ((LPTSTR)p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength)) + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + else + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + + p_IRP->IoStatus.Information + = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + break; + } +#endif + + case TAP_IOCTL_CONFIG_POINT_TO_POINT: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 2)) + { + MACADDR dest; + + l_Adapter->m_PointToPoint = FALSE; + + GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1); + + l_Adapter->m_localIP = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_remoteIP = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + + COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC); + COPY_MAC (l_Adapter->m_TapToUser.dest, dest); + COPY_MAC (l_Adapter->m_UserToTap.src, dest); + COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); + + l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + + l_Adapter->m_PointToPoint = TRUE; + + CheckIfDhcpAndPointToPointMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_SET_MEDIA_STATUS: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (ULONG) * 1)) + { + ULONG parm = ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + SetMediaStatus (l_Adapter, (BOOLEAN) parm); + p_IRP->IoStatus.Information = 1; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + break; + } + + case TAP_IOCTL_CONFIG_DHCP_MASQ: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 4)) + { + l_Adapter->m_dhcp_enabled = FALSE; + l_Adapter->m_dhcp_server_arp = FALSE; + l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + // Adapter IP addr / netmask + l_Adapter->m_dhcp_addr = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_dhcp_netmask = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + + // IP addr of DHCP masq server + l_Adapter->m_dhcp_server_ip = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2]; + + // Lease time in seconds + l_Adapter->m_dhcp_lease_time = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[3]; + + GenerateRelatedMAC (l_Adapter->m_dhcp_server_mac, l_Adapter->m_MAC, 2); + + l_Adapter->m_dhcp_enabled = TRUE; + l_Adapter->m_dhcp_server_arp = TRUE; + + CheckIfDhcpAndPointToPointMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_CONFIG_DHCP_SET_OPT: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength <= + DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE + && l_Adapter->m_dhcp_enabled) + { + l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + NdisMoveMemory (l_Adapter->m_dhcp_user_supplied_options_buffer, + p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.DeviceIoControl.InputBufferLength); + + l_Adapter->m_dhcp_user_supplied_options_buffer_len = + l_IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + +#ifdef VBOX + case TAP_IOCTL_TRANSFER_ETHPACKETS: + { + PTAP_SCATTER_GATHER_LIST pList; + ULONG i; + PMDL pMdlBuf = NULL; + + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(TAP_SCATTER_GATHER_LIST)) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + pList = (PTAP_SCATTER_GATHER_LIST)p_IRP->AssociatedIrp.SystemBuffer; + + /* Sanity checks */ + if ( pList->cPackets > TAP_SCATTER_GATHER_MAX_PACKETS + || l_IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(TAP_SCATTER_GATHER_LIST) + (pList->cPackets-1)*sizeof(TAP_SCATTER_GATHER_ITEM) + ) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + + __try + { + /* Send all packets. */ + for (i=0;i<pList->cPackets;i++) + { + char *pBuffer; + + DUMP_PACKET ("IRP_MJ_WRITE ETH", (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length); + + pMdlBuf = IoAllocateMdl(pList->aPacket[i].pPacket, pList->aPacket[i].cb, FALSE, FALSE, NULL); + if (!pMdlBuf) + { + p_IRP->IoStatus.Status = l_Status = STATUS_NO_MEMORY; + break; + } + + /* Exceptions caught by top _try _except block */ + /** @todo really necessary to lock the pages?? */ + MmProbeAndLockPages(pMdlBuf, KernelMode, IoModifyAccess); + + pBuffer = MmGetSystemAddressForMdlSafe(pMdlBuf, NormalPagePriority); + if (!pBuffer) + { + MmUnlockPages(pMdlBuf); + IoFreeMdl(pMdlBuf); + p_IRP->IoStatus.Status = l_Status = STATUS_NO_MEMORY; + break; + } + + NdisMEthIndicateReceive(l_Adapter->m_MiniportAdapterHandle, (NDIS_HANDLE) l_Adapter, + pBuffer, + ETHERNET_HEADER_SIZE, + pBuffer + ETHERNET_HEADER_SIZE, + pList->aPacket[i].cb - ETHERNET_HEADER_SIZE, + pList->aPacket[i].cb - ETHERNET_HEADER_SIZE); + + NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle); + + MmUnlockPages(pMdlBuf); + IoFreeMdl(pMdlBuf); + pMdlBuf = NULL; + + } + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + if (pMdlBuf) + IoFreeMdl(pMdlBuf); + + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + break; + } + break; + } +#endif + + default: + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //=========================================================== + // User mode thread issued a read request on the tap device + // If there are packets waiting to be read, then the request + // will be satisfied here. If not, then the request will be + // queued and satisfied by any packet that is not used to + // satisfy requests ahead of it. + //=========================================================== + case IRP_MJ_READ: + { + TapPacketPointer l_PacketBuffer; + BOOLEAN pending = FALSE; + + // Save IRP-accessible copy of buffer length + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Read.Length; + + if (p_IRP->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + else if ((p_IRP->AssociatedIrp.SystemBuffer = + MmGetSystemAddressForMdlSafe + (p_IRP->MdlAddress, NormalPagePriority)) == NULL) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + else if (!l_Adapter->m_InterfaceIsRunning) + { + DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //================================== + // Can we provide immediate service? + //================================== + + l_PacketBuffer = NULL; + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueueCount (l_Adapter->m_Extension.m_PacketQueue) + && QueueCount (l_Adapter->m_Extension.m_IrpQueue) == 0) + { + l_PacketBuffer = (TapPacketPointer) + QueuePop (l_Adapter->m_Extension.m_PacketQueue); + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (l_PacketBuffer) + { + l_Status = CompleteIRP (p_IRP, + l_PacketBuffer, + IO_NO_INCREMENT); + break; + } + + //============================= + // Attempt to pend read request + //============================= + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueuePush (l_Adapter->m_Extension.m_IrpQueue, p_IRP) == (PIRP) p_IRP) + { + IoSetCancelRoutine (p_IRP, CancelIRPCallback); + l_Status = STATUS_PENDING; + IoMarkIrpPending (p_IRP); + pending = TRUE; + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (pending) + break; + + // Can't queue anymore IRP's + DEBUGP (("[%s] TAP [%s] read IRP overrun\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapName)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //============================================================== + // User mode issued a WriteFile request on the TAP file handle. + // The request will always get satisfied here. The call may + // fail if there are too many pending packets (queue full). + //============================================================== + case IRP_MJ_WRITE: + { + if (p_IRP->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + p_IRP->IoStatus.Information = 0; + } + else if ((p_IRP->AssociatedIrp.SystemBuffer = + MmGetSystemAddressForMdlSafe + (p_IRP->MdlAddress, NormalPagePriority)) == NULL) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES; + p_IRP->IoStatus.Information = 0; + } + else if (!l_Adapter->m_InterfaceIsRunning) + { + DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + else if (!l_Adapter->m_PointToPoint && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) + { + __try + { + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; + + ////DEBUGP(("IRP_MJ_WRITE %08x %x\n", p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length)); + + DUMP_PACKET ("IRP_MJ_WRITE ETH", + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length); + + NdisMEthIndicateReceive + (l_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) l_Adapter, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + ETHERNET_HEADER_SIZE, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer + ETHERNET_HEADER_SIZE, + l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE, + l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE); + + NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle); + + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + else if (l_Adapter->m_PointToPoint && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) + { + __try + { + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; + + DUMP_PACKET2 ("IRP_MJ_WRITE P2P", + &l_Adapter->m_UserToTap, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length); + + NdisMEthIndicateReceive + (l_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) l_Adapter, + (unsigned char *) &l_Adapter->m_UserToTap, + sizeof (l_Adapter->m_UserToTap), + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + l_IrpSp->Parameters.Write.Length); + + NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle); + + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE (P2P)\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + else + { + DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n", + NAME (l_Adapter), + l_IrpSp->Parameters.Write.Length)); + NOTE_ERROR (); + p_IRP->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE; + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + if (l_Status == STATUS_SUCCESS) + INCREMENT_STAT (l_Adapter->m_Rx); + else + INCREMENT_STAT (l_Adapter->m_RxErr); + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //-------------------------------------------------------------- + // User mode thread has called CreateFile() on the tap device + //-------------------------------------------------------------- + case IRP_MJ_CREATE: + { + BOOLEAN succeeded = FALSE; + BOOLEAN mutex_succeeded; + + DEBUGP + (("[%s] [TAP] release [%d.%d] open request (m_TapOpens=%d)\n", + NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, l_Adapter->m_Extension.m_TapOpens)); + + ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded); + if (mutex_succeeded) + { + if (l_Adapter->m_Extension.m_TapIsRunning && !l_Adapter->m_Extension.m_TapOpens) + { + ResetTapAdapterState (l_Adapter); + l_Adapter->m_Extension.m_TapOpens = 1; + succeeded = TRUE; + } + + if (succeeded) + { + INCREMENT_STAT (l_Adapter->m_Extension.m_NumTapOpens); + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + p_IRP->IoStatus.Information = 0; + } + else + { + DEBUGP (("[%s] TAP is presently unavailable (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex); + } + else + { + DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //----------------------------------------------------------- + // User mode thread called CloseHandle() on the tap device + //----------------------------------------------------------- + case IRP_MJ_CLOSE: + { + BOOLEAN mutex_succeeded; + + DEBUGP (("[%s] [TAP] release [%d.%d] close/cleanup request\n", + NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION)); + + ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded); + if (mutex_succeeded) + { + l_Adapter->m_Extension.m_TapOpens = 0; + ResetTapAdapterState (l_Adapter); + FlushQueues (&l_Adapter->m_Extension); + SetMediaStatus (l_Adapter, FALSE); + RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex); + } + else + { + DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //------------------ + // Strange Request + //------------------ + default: + { + //NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + } + + return l_Status; +} + +//============================================================= +// CompleteIRP is normally called with an adapter -> userspace +// network packet and an IRP (Pending I/O request) from userspace. +// +// The IRP will normally represent a queued overlapped read +// operation from userspace that is in a wait state. +// +// Use the ethernet packet to satisfy the IRP. +//============================================================= + +NTSTATUS +CompleteIRP (IN PIRP p_IRP, + IN TapPacketPointer p_PacketBuffer, + IN CCHAR PriorityBoost) +{ + NTSTATUS l_Status = STATUS_UNSUCCESSFUL; + + int offset; + int len; + + MYASSERT (p_IRP); + MYASSERT (p_PacketBuffer); + + IoSetCancelRoutine (p_IRP, NULL); // Disable cancel routine + + //------------------------------------------- + // While p_PacketBuffer always contains a + // full ethernet packet, including the + // ethernet header, in point-to-point mode, + // we only want to return the IPv4 + // component. + //------------------------------------------- + + if (p_PacketBuffer->m_SizeFlags & TP_POINT_TO_POINT) + { + offset = ETHERNET_HEADER_SIZE; + len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; + } + else + { + offset = 0; + len = (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK); + } + + if (len < 0 || (int) p_IRP->IoStatus.Information < len) + { + p_IRP->IoStatus.Information = 0; + p_IRP->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + NOTE_ERROR (); + } + else + { + p_IRP->IoStatus.Information = len; + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + + __try + { + NdisMoveMemory (p_IRP->AssociatedIrp.SystemBuffer, + p_PacketBuffer->m_Data + offset, + len); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + + __try + { + NdisFreeMemory (p_PacketBuffer, + TAP_PACKET_SIZE (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK), + 0); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + + if (l_Status == STATUS_SUCCESS) + { + IoCompleteRequest (p_IRP, PriorityBoost); + } + else + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + + return l_Status; +} + +//============================================== +// IRPs get cancelled for a number of reasons. +// +// The TAP device could be closed by userspace +// when there are still pending read operations. +// +// The user could disable the TAP adapter in the +// network connections control panel, while the +// device is still open by a process. +//============================================== +VOID +CancelIRPCallback (IN PDEVICE_OBJECT p_DeviceObject, + IN PIRP p_IRP) +{ + TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject); + CancelIRP (l_Adapter ? &l_Adapter->m_Extension : NULL, p_IRP, TRUE); +} + +VOID +CancelIRP (TapExtensionPointer p_Extension, + IN PIRP p_IRP, + BOOLEAN callback) +{ + BOOLEAN exists = FALSE; + + MYASSERT (p_IRP); + + if (p_Extension) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + exists = (QueueExtract (p_Extension->m_IrpQueue, p_IRP) == p_IRP); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + } + else + exists = TRUE; + + if (exists) + { + IoSetCancelRoutine (p_IRP, NULL); + p_IRP->IoStatus.Status = STATUS_CANCELLED; + p_IRP->IoStatus.Information = 0; + } + + if (callback) + IoReleaseCancelSpinLock (p_IRP->CancelIrql); + + if (exists) + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); +} + +//==================================== +// Exhaust packet and IRP queues. +//==================================== +VOID +FlushQueues (TapExtensionPointer p_Extension) +{ + PIRP l_IRP; + TapPacketPointer l_PacketBuffer; + int n_IRP=0, n_Packet=0; + + MYASSERT (p_Extension); + MYASSERT (p_Extension->m_TapDevice); + + while (TRUE) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + l_IRP = QueuePop (p_Extension->m_IrpQueue); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + if (l_IRP) + { + ++n_IRP; + CancelIRP (NULL, l_IRP, FALSE); + } + else + break; + } + + while (TRUE) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + l_PacketBuffer = QueuePop (p_Extension->m_PacketQueue); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + if (l_PacketBuffer) + { + ++n_Packet; + MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK)); + } + else + break; + } + + DEBUGP (( + "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d]\n", + p_Extension->m_TapName, + n_IRP, + p_Extension->m_IrpQueue->max_size, + IRP_QUEUE_SIZE, + n_Packet, + p_Extension->m_PacketQueue->max_size, + PACKET_QUEUE_SIZE + )); +} + +//=================================================== +// Tell Windows whether the TAP device should be +// considered "connected" or "disconnected". +//=================================================== +VOID +SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state) +{ + if (p_Adapter->m_MediaState != state && !p_Adapter->m_MediaStateAlwaysConnected) + { + if (state) + NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle, + NDIS_STATUS_MEDIA_CONNECT, NULL, 0); + else + NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle, + NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); + + NdisMIndicateStatusComplete (p_Adapter->m_MiniportAdapterHandle); + p_Adapter->m_MediaState = state; + } +} + + +//====================================================== +// If DHCP mode is used together with Point-to-point +// mode, consider the fact that the P2P remote endpoint +// might be equal to the DHCP masq server address. +//====================================================== +VOID +CheckIfDhcpAndPointToPointMode (TapAdapterPointer p_Adapter) +{ + if (p_Adapter->m_PointToPoint && p_Adapter->m_dhcp_enabled) + { + if (p_Adapter->m_dhcp_server_ip == p_Adapter->m_remoteIP) + { + COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest); + p_Adapter->m_dhcp_server_arp = FALSE; + } + } +} + +//=================================================== +// Generate an ARP reply message for specific kinds +// ARP queries. +//=================================================== +BOOLEAN +ProcessARP (TapAdapterPointer p_Adapter, + const PARP_PACKET src, + const IPADDR adapter_ip, + const IPADDR ip, + const MACADDR mac) +{ + //----------------------------------------------- + // Is this the kind of packet we are looking for? + //----------------------------------------------- + if (src->m_Proto == htons (ETH_P_ARP) + && MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC) + && MAC_EQUAL (src->m_ARP_MAC_Source, p_Adapter->m_MAC) + && MAC_EQUAL (src->m_MAC_Destination, p_Adapter->m_MAC_Broadcast) + && src->m_ARP_Operation == htons (ARP_REQUEST) + && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE) + && src->m_MAC_AddressSize == sizeof (MACADDR) + && src->m_PROTO_AddressType == htons (ETH_P_IP) + && src->m_PROTO_AddressSize == sizeof (IPADDR) + && src->m_ARP_IP_Source == adapter_ip + && src->m_ARP_IP_Destination == ip) + { + ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE); + if (arp) + { + //---------------------------------------------- + // Initialize ARP reply fields + //---------------------------------------------- + arp->m_Proto = htons (ETH_P_ARP); + arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE); + arp->m_PROTO_AddressType = htons (ETH_P_IP); + arp->m_MAC_AddressSize = sizeof (MACADDR); + arp->m_PROTO_AddressSize = sizeof (IPADDR); + arp->m_ARP_Operation = htons (ARP_REPLY); + + //---------------------------------------------- + // ARP addresses + //---------------------------------------------- + COPY_MAC (arp->m_MAC_Source, mac); + COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC); + COPY_MAC (arp->m_ARP_MAC_Source, mac); + COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC); + arp->m_ARP_IP_Source = ip; + arp->m_ARP_IP_Destination = adapter_ip; + + DUMP_PACKET ("ProcessARP", + (unsigned char *) arp, + sizeof (ARP_PACKET)); + + InjectPacket (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); + + MemFree (arp, sizeof (ARP_PACKET)); + } + + return TRUE; + } + else + return FALSE; +} + +//=============================================================== +// Used in cases where internally generated packets such as +// ARP or DHCP replies must be returned to the kernel, to be +// seen as an incoming packet "arriving" on the interface. +//=============================================================== + +VOID +InjectPacket (TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len) +{ + MYASSERT (len >= ETHERNET_HEADER_SIZE); + + __try + { + //------------------------------------------------------------ + // NdisMEthIndicateReceive and NdisMEthIndicateReceiveComplete + // could potentially be called reentrantly both here and in + // TapDeviceHook/IRP_MJ_WRITE. + // + // The DDK docs imply that this is okay. + //------------------------------------------------------------ + NdisMEthIndicateReceive + (p_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) p_Adapter, + packet, + ETHERNET_HEADER_SIZE, + packet + ETHERNET_HEADER_SIZE, + len - ETHERNET_HEADER_SIZE, + len - ETHERNET_HEADER_SIZE); + + NdisMEthIndicateReceiveComplete (p_Adapter->m_MiniportAdapterHandle); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacket\n", + NAME (p_Adapter))); + NOTE_ERROR (); + } +} + +//=================================================================== +// Go back to default TAP mode from Point-To-Point mode. +// Also reset (i.e. disable) DHCP Masq mode. +//=================================================================== +VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) +{ + // Point-To-Point + p_Adapter->m_PointToPoint = FALSE; + p_Adapter->m_localIP = 0; + p_Adapter->m_remoteIP = 0; + NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser)); + NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap)); + + // DHCP Masq + p_Adapter->m_dhcp_enabled = FALSE; + p_Adapter->m_dhcp_server_arp = FALSE; + p_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + p_Adapter->m_dhcp_addr = 0; + p_Adapter->m_dhcp_netmask = 0; + p_Adapter->m_dhcp_server_ip = 0; + p_Adapter->m_dhcp_lease_time = 0; + p_Adapter->m_dhcp_received_discover = FALSE; + p_Adapter->m_dhcp_bad_requests = 0; + NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR)); +} + +#if ENABLE_NONADMIN + +//=================================================================== +// Set TAP device handle to be accessible without admin privileges. +//=================================================================== +VOID AllowNonAdmin (TapExtensionPointer p_Extension) +{ + NTSTATUS stat; + SECURITY_DESCRIPTOR sd; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + HANDLE hand = NULL; + + NdisZeroMemory (&sd, sizeof (sd)); + NdisZeroMemory (&oa, sizeof (oa)); + NdisZeroMemory (&isb, sizeof (isb)); + + if (!p_Extension->m_CreatedUnicodeLinkName) + { + DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n")); + NOTE_ERROR (); + return; + } + + stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n")); + NOTE_ERROR (); + return; + } + + InitializeObjectAttributes ( + &oa, + &p_Extension->m_UnicodeLinkName, + OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + stat = ZwOpenFile ( + &hand, + WRITE_DAC, + &oa, + &isb, + 0, + 0 + ); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat)); + NOTE_ERROR (); + return; + } + + stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n")); + NOTE_ERROR (); + return; + } + + stat = ZwClose (hand); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n")); + NOTE_ERROR (); + return; + } + + DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n")); +} + +#endif + +//====================================================================== +// End of Source +//====================================================================== diff --git a/src/VBox/HostDrivers/VBoxTAP/types.h b/src/VBox/HostDrivers/VBoxTAP/types.h new file mode 100644 index 000000000..a82ad2b4f --- /dev/null +++ b/src/VBox/HostDrivers/VBoxTAP/types.h @@ -0,0 +1,163 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TAP_TYPES_DEFINED +#define TAP_TYPES_DEFINED + +typedef struct _Queue +{ + ULONG base; + ULONG size; + ULONG capacity; + ULONG max_size; + PVOID data[]; +} Queue; + +typedef struct _TapAdapter; +typedef struct _TapPacket; + +typedef union _TapAdapterQuery +{ + NDIS_HARDWARE_STATUS m_HardwareStatus; + NDIS_MEDIUM m_Medium; + NDIS_PHYSICAL_MEDIUM m_PhysicalMedium; + UCHAR m_MacAddress [6]; + UCHAR m_Buffer [256]; + ULONG m_Long; + USHORT m_Short; + UCHAR m_Byte; +} +TapAdapterQuery, *TapAdapterQueryPointer; + +typedef struct _TapExtension +{ + // TAP device object and packet queues + Queue *m_PacketQueue, *m_IrpQueue; + PDEVICE_OBJECT m_TapDevice; + NDIS_HANDLE m_TapDeviceHandle; + ULONG m_TapOpens; + + // Used to lock packet queues + NDIS_SPIN_LOCK m_QueueLock; + BOOLEAN m_AllocatedSpinlocks; + + // Used to bracket open/close + // state changes. + MUTEX m_OpenCloseMutex; + + // True if device has been permanently halted + BOOLEAN m_Halt; + + // TAP device name + unsigned char *m_TapName; + UNICODE_STRING m_UnicodeLinkName; + BOOLEAN m_CreatedUnicodeLinkName; + + // Used for device status ioctl only + const char *m_LastErrorFilename; + int m_LastErrorLineNumber; + LONG m_NumTapOpens; + + // Flags + BOOLEAN m_TapIsRunning; + BOOLEAN m_CalledTapDeviceFreeResources; +} +TapExtension, *TapExtensionPointer; + +typedef struct _TapPacket + { +# define TAP_PACKET_SIZE(data_size) (sizeof (TapPacket) + (data_size)) +# define TP_POINT_TO_POINT 0x80000000 +# define TP_SIZE_MASK (~TP_POINT_TO_POINT) + ULONG m_SizeFlags; + UCHAR m_Data []; // m_Data must be the last struct member + } +TapPacket, *TapPacketPointer; + +typedef struct _TapAdapter +{ +# define NAME(a) ((a)->m_NameAnsi.Buffer) + ANSI_STRING m_NameAnsi; + MACADDR m_MAC; + BOOLEAN m_InterfaceIsRunning; + NDIS_HANDLE m_MiniportAdapterHandle; + LONG m_Rx, m_Tx, m_RxErr, m_TxErr; + NDIS_MEDIUM m_Medium; + ULONG m_Lookahead; + ULONG m_MTU; + + // TRUE if adapter should always be + // "connected" even when device node + // is not open by a userspace process. + BOOLEAN m_MediaStateAlwaysConnected; + + // TRUE if device is "connected" + BOOLEAN m_MediaState; + + // Adapter power state + char m_DeviceState; + + // Info for point-to-point mode + BOOLEAN m_PointToPoint; + IPADDR m_localIP; + IPADDR m_remoteIP; + ETH_HEADER m_TapToUser; + ETH_HEADER m_UserToTap; + MACADDR m_MAC_Broadcast; + + // Used for DHCP server masquerade + BOOLEAN m_dhcp_enabled; + IPADDR m_dhcp_addr; + ULONG m_dhcp_netmask; + IPADDR m_dhcp_server_ip; + BOOLEAN m_dhcp_server_arp; + MACADDR m_dhcp_server_mac; + ULONG m_dhcp_lease_time; + UCHAR m_dhcp_user_supplied_options_buffer[DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE]; + ULONG m_dhcp_user_supplied_options_buffer_len; + BOOLEAN m_dhcp_received_discover; + ULONG m_dhcp_bad_requests; + + // Help to tear down the adapter by keeping + // some state information on allocated + // resources. + BOOLEAN m_CalledAdapterFreeResources; + BOOLEAN m_RegisteredAdapterShutdownHandler; + + // Multicast list info + NDIS_SPIN_LOCK m_MCLock; + BOOLEAN m_MCLockAllocated; + ULONG m_MCListSize; + MC_LIST m_MCList; + + // Information on the TAP device + TapExtension m_Extension; +} TapAdapter, *TapAdapterPointer; + +#endif + diff --git a/src/VBox/HostDrivers/linux/Makefile b/src/VBox/HostDrivers/linux/Makefile new file mode 100644 index 000000000..ea79be321 --- /dev/null +++ b/src/VBox/HostDrivers/linux/Makefile @@ -0,0 +1,64 @@ +# +# Makefile for the VirtualBox Linux Host Drivers. +# + +# +# +# Copyright (C) 2008-2009 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +KBUILD_VERBOSE = + +all: + @echo "*** Building 'vboxdrv' module ***" + @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv + @cp vboxdrv/vboxdrv.ko . + @echo + @if [ -d vboxnetflt ]; then \ + if [ -f vboxdrv/Module.symvers ]; then \ + cp vboxdrv/Module.symvers vboxnetflt; \ + fi; \ + echo "*** Building 'vboxnetflt' module ***"; \ + $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt; \ + cp vboxnetflt/vboxnetflt.ko .; \ + fi + + +install: + @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv install + @if [ -d vboxnetflt ]; then \ + $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt install; \ + fi + +clean: + @$(MAKE) -C vboxdrv clean + @if [ -d vboxnetflt ]; then \ + $(MAKE) -C vboxnetflt clean; \ + fi + rm -f vboxdrv.ko vboxnetflt.ko + +load: + @for module in vboxnetflt vboxdrv; do \ + if grep "^$$module " /proc/modules >/dev/null; then \ + echo "Removing previously installed $$module module"; \ + /sbin/rmmod $$module; \ + fi; \ + done + @for module in vboxdrv vboxnetflt; do \ + if test -f $$module.ko; then \ + echo "Installing $$module module"; \ + /sbin/insmod $$module.ko; \ + fi; \ + done diff --git a/src/VBox/HostDrivers/linux/Makefile.kup b/src/VBox/HostDrivers/linux/Makefile.kup new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/VBox/HostDrivers/linux/Makefile.kup diff --git a/src/VBox/HostDrivers/linux/build_in_tmp b/src/VBox/HostDrivers/linux/build_in_tmp new file mode 100755 index 000000000..0b1d17a5a --- /dev/null +++ b/src/VBox/HostDrivers/linux/build_in_tmp @@ -0,0 +1,112 @@ +#!/bin/sh + +# +# Script to build a kernel module in /tmp. Useful if the module sources +# are installed in read-only directory. +# +# Copyright (C) 2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +# Attempt to build using DKMS first +DKMS=`which dkms 2>/dev/null` +if [ -n "$DKMS" ] +then + echo "Attempting to install using DKMS" + $DKMS status -m _MODULE_ | while read line + # first, remove _any_ old module + do + if echo "$line" | grep -q added > /dev/null || + echo "$line" | grep -q built > /dev/null || + echo "$line" | grep -q installed > /dev/null; then + version=`echo "$line" | sed "s/_MODULE_,\([^,]*\),.*/\1/;t;d"` + echo " removing old DKMS module _MODULE_ version $version" + $DKMS remove -m _MODULE_ -v $version --all + fi + done + # there should not be any more matches + status=`$DKMS status -m _MODULE_ -v _VERSION_` + if echo $status | grep added > /dev/null || + echo $status | grep built > /dev/null || + echo $status | grep installed > /dev/null + then + $DKMS remove -m _MODULE_ -v _VERSION_ --all + fi + # finally install the module + if $DKMS add -m _MODULE_ -v _VERSION_ && + $DKMS build -m _MODULE_ -v _VERSION_ && + $DKMS install -m _MODULE_ -v _VERSION_ --force + then + exit 0 + fi + echo "Failed to install using DKMS, attempting to install without" +fi + +# find a unique temp directory +num=0 +while true; do + tmpdir="/tmp/vbox.$num" + if mkdir -m 0755 "$tmpdir" 2> /dev/null; then + break + fi + num=`expr $num + 1` + if [ $num -gt 200 ]; then + echo "Could not find a valid tmp directory" + exit 1 + fi +done + +if [ "$1" = "--save-module-symvers" ]; then + shift + SAVE_MOD_SYMVERS="$1" + shift +fi + +if [ "$1" = "--use-module-symvers" ]; then + shift + USE_MOD_SYMVERS="$1" + shift +fi + +# copy +cp -a ${0%/*}/* $tmpdir/ +if [ -n "$USE_MOD_SYMVERS" ]; then + cp $USE_MOD_SYMVERS $tmpdir/Module.symvers +fi + +# make, cleanup if success +cd "$tmpdir" +if make "$@"; then + if [ -n "$SAVE_MOD_SYMVERS" ]; then + if [ -f Module.symvers ]; then + cp -f Module.symvers $SAVE_MOD_SYMVERS + else + cat /dev/null > $SAVE_MOD_SYMVERS + fi + fi + rm -rf $tmpdir + exit 0 +fi + +# failure +exit 1 diff --git a/src/VBox/HostDrivers/linux/export_modules b/src/VBox/HostDrivers/linux/export_modules new file mode 100755 index 000000000..a0dca9902 --- /dev/null +++ b/src/VBox/HostDrivers/linux/export_modules @@ -0,0 +1,103 @@ +#!/bin/sh + +# +# Create a tar archive containing the sources of the vboxdrv kernel module +# +# Copyright (C) 2007 Sun Microsystems, Inc. +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa +# Clara, CA 95054 USA or visit http://www.sun.com if you need +# additional information or have any questions. +# + +if [ -z "$1" ]; then + echo "Usage: $0 <filename.tar.gz> [--without-hardening]" + echo " Export VirtualBox kernel modules to <filename.tar.gz>" + exit 1 +fi + +VBOX_WITH_HARDENING=1 +if [ "$2" = "--without-hardening" ]; then + VBOX_WITH_HARDENING= +fi + +PATH_TMP="`cd \`dirname $1\`; pwd`/.vbox_modules" +PATH_OUT=$PATH_TMP +FILE_OUT="`cd \`dirname $1\`; pwd`/`basename $1`" +PATH_ROOT="`cd \`dirname $0\`/../../../..; pwd`" +PATH_LINUX="$PATH_ROOT/src/VBox/HostDrivers/linux" +PATH_VBOXDRV="$PATH_ROOT/src/VBox/HostDrivers/Support" +PATH_VBOXNET="$PATH_ROOT/src/VBox/HostDrivers/VBoxNetFlt" + +VBOX_VERSION_MAJOR=`sed -e "s/^ *VBOX_VERSION_MAJOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Config.kmk` +VBOX_VERSION_MINOR=`sed -e "s/^ *VBOX_VERSION_MINOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Config.kmk` +VBOX_VERSION_BUILD=`sed -e "s/^ *VBOX_VERSION_BUILD *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Config.kmk` +VBOX_VERSION_STRING=$VBOX_VERSION_MAJOR.$VBOX_VERSION_MINOR.$VBOX_VERSION_BUILD + +. $PATH_VBOXDRV/linux/files_vboxdrv +. $PATH_VBOXNET/linux/files_vboxnetflt + +# Temporary path for creating the modules, will be removed later +mkdir $PATH_TMP || exit 1 + +# Create auto-generated version file, needed by all modules +echo "#ifndef __version_generated_h__" > $PATH_TMP/version-generated.h +echo "#define __version_generated_h__" >> $PATH_TMP/version-generated.h +echo "" >> $PATH_TMP/version-generated.h +echo "#define VBOX_VERSION_MAJOR $VBOX_VERSION_MAJOR" >> $PATH_TMP/version-generated.h +echo "#define VBOX_VERSION_MINOR $VBOX_VERSION_MINOR" >> $PATH_TMP/version-generated.h +echo "#define VBOX_VERSION_BUILD $VBOX_VERSION_BUILD" >> $PATH_TMP/version-generated.h +echo "#define VBOX_VERSION_STRING \"$VBOX_VERSION_STRING\"" >> $PATH_TMP/version-generated.h +echo "" >> $PATH_TMP/version-generated.h +echo "#endif" >> $PATH_TMP/version-generated.h + +# vboxdrv (VirtualBox host kernel module) +mkdir $PATH_TMP/vboxdrv || exit 1 +for f in $FILES_VBOXDRV_NOBIN; do + install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxdrv/`echo $f|cut -d'>' -f2`" +done +for f in $FILES_VBOXDRV_BIN; do + install -D -m 0755 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxdrv/`echo $f|cut -d'>' -f2`" +done +sed -e "s;_VERSION_;$VBOX_VERSION_STRING;g" < $PATH_LINUX/build_in_tmp > $PATH_TMP/vboxdrv/build_in_tmp +chmod 0755 $PATH_TMP/vboxdrv/build_in_tmp +sed -e "s;_VERSION_;$VBOX_VERSION_STRING;g" < $PATH_VBOXDRV/linux/dkms.conf > $PATH_TMP/vboxdrv/dkms.conf +if [ -n "$VBOX_WITH_HARDENING" ]; then + cat $PATH_VBOXDRV/linux/Makefile > $PATH_TMP/vboxdrv/Makefile +else + sed -e "s;-DVBOX_WITH_HARDENING;;g" < $PATH_VBOXDRV/linux/Makefile > $PATH_TMP/vboxdrv/Makefile +fi + +# vboxnetflt (VirtualBox netfilter kernel module) +mkdir $PATH_TMP/vboxnetflt || exit 1 +for f in $VBOX_VBOXNETFLT_SOURCES; do + install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_TMP/vboxnetflt/`echo $f|cut -d'>' -f2`" +done +sed -e "s;_VERSION_;$VBOX_VERSION_STRING;g" < $PATH_LINUX/build_in_tmp > $PATH_TMP/vboxnetflt/build_in_tmp +chmod 0755 $PATH_TMP/vboxnetflt/build_in_tmp +sed -e "s;_VERSION_;$VBOX_VERSION_STRING;g" < $PATH_VBOXNET/linux/dkms.conf > $PATH_TMP/vboxnetflt/dkms.conf +if [ -n "$VBOX_WITH_HARDENING" ]; then + cat $PATH_VBOXNET/linux/Makefile > $PATH_TMP/vboxnetflt/Makefile +else + sed -e "s;-DVBOX_WITH_HARDENING;;g" < $PATH_VBOXNET/linux/Makefile > $PATH_TMP/vboxnetflt/Makefile +fi + +install -D -m 0644 $PATH_LINUX/Makefile $PATH_TMP/Makefile + +# Only temporary, omit from archive +rm $PATH_TMP/version-generated.h + +# Create the archive +tar -czf $FILE_OUT -C $PATH_TMP . || exit 1 + +# Remove the temporary directory +rm -r $PATH_TMP + |
