diff options
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest')
86 files changed, 39447 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/.scm-settings b/src/VBox/Additions/common/VBoxGuest/.scm-settings new file mode 100644 index 00000000..171ab7e3 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/.scm-settings @@ -0,0 +1,65 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for VBoxGuest +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# Like the host drivers, this is dual licensed. +--license-ose-dual + +/VBoxGuest-solaris.conf: --treat-as .sh + +/netbsd/vboxguest.ioconf: --treat-as .sh --no-convert-tabs --no-convert-eol + +# a stub for a file that is normally autogenerated +/netbsd/locators.h: --external-copyright --no-fix-header-guards + +# And the R0 library is MIT to make two-way code exchange with the Linux kernel +# easier. +/lib/VBoxGuestR0LibCrOgl.cpp: --license-mit +/lib/VBoxGuestR0LibGenericRequest.cpp: --license-mit +/lib/VBoxGuestR0LibHGCM.cpp: --license-mit +/lib/VBoxGuestR0LibHGCMInternal.cpp: --license-mit +/lib/VBoxGuestR0LibIdc-os2.cpp: --license-mit +/lib/VBoxGuestR0LibIdc-solaris.cpp: --license-mit +/lib/VBoxGuestR0LibIdc-unix.cpp: --license-mit +/lib/VBoxGuestR0LibIdc-win.cpp: --license-mit +/lib/VBoxGuestR0LibIdc.cpp: --license-mit +/lib/VBoxGuestR0LibInit.cpp: --license-mit +/lib/VBoxGuestR0LibInternal.h: --license-mit +/lib/VBoxGuestR0LibMouse.cpp: --license-mit +/lib/VBoxGuestR0LibPhysHeap.cpp: --license-mit +/lib/VBoxGuestR0LibSharedFolders.c: --license-mit +/lib/VBoxGuestR0LibVMMDev.cpp: --license-mit +/lib/VbglR0CanUsePhysPageList.cpp: --license-mit + diff --git a/src/VBox/Additions/common/VBoxGuest/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/Makefile.kmk new file mode 100644 index 00000000..a51f05b5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/Makefile.kmk @@ -0,0 +1,287 @@ +# $Id: Makefile.kmk $ +## @file +# Makefile for the Cross Platform Guest Additions Driver. +# + +# +# Copyright (C) 2007-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile. +include $(PATH_SUB_CURRENT)/lib/Makefile.kmk + + +if defined(VBOX_WITH_ADDITION_DRIVERS) && "$(intersects $(KBUILD_TARGET), darwin freebsd haiku netbsd os2 solaris win)" != "" + # + # VBoxGuest - The Guest Additions Driver. + # + SYSMODS += VBoxGuest + VBoxGuest_TEMPLATE = VBOXGUESTR0 + VBoxGuest_NAME.freebsd = vboxguest + VBoxGuest_NAME.haiku = vboxguest + VBoxGuest_NAME.netbsd = vboxguest + VBoxGuest_NAME.solaris = vboxguest + VBoxGuest_INST.darwin = $(INST_ADDITIONS)VBoxGuest.kext/Contents/MacOS/ + if defined(VBOX_SIGNING_MODE) && defined(VBOX_SIGN_ADDITIONS) # See Additions/WINNT/Makefile.kmk? + VBoxGuest_INSTTYPE.win = none + VBoxGuest_DEBUG_INSTTYPE.win = both + endif + VBoxGuest_DEFS.haiku = VBOX_SVN_REV=$(VBOX_SVN_REV) _KERNEL_MODE=1 + VBoxGuest_DEFS.solaris = VBOX_SVN_REV=$(VBOX_SVN_REV) + VBoxGuest_DEFS.win = VBOX_GUESTDRV_WITH_RELEASE_LOGGER + VBoxGuest_DEFS.win.x86 = TARGET_NT4 TARGET_NT3 RT_WITHOUT_NOCRT_WRAPPERS + VBoxGuest_DEFS.darwin = VBOX_GUESTDRV_WITH_RELEASE_LOGGER + ifeq ($(KBUILD_TYPE),release) + # Allow stopping/removing the driver without a reboot + # in debug mode; this is very useful for testing the shutdown stuff! + VBoxGuest_DEFS.win += VBOX_REBOOT_ON_UNINSTALL + endif + ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION + VBoxGuest_DEFS.win += VBOX_WITH_GUEST_BUGCHECK_DETECTION + endif + #VBoxGuest_DEFS.win += LOG_ENABLED LOG_TO_BACKDOOR + VBoxGuest_DEFS.win += \ + $(if $(VBOX_WITH_DPC_LATENCY_CHECKER),VBOX_WITH_DPC_LATENCY_CHECKER,) + VBoxGuest_DEPS.solaris += $(VBOX_SVN_REV_KMK) + VBoxGuest_DEPS.haiku += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEPS.freebsd += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEPS.netbsd += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEPS.darwin += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEFS = VBGL_VBOXGUEST VBOX_WITH_HGCM + VBoxGuest_INCS = . + VBoxGuest_INCS.freebsd = $(VBoxGuest_0_OUTDIR) $(PATH_STAGE)/gen-sys-hdrs + VBoxGuest_INCS.netbsd = $(VBoxGuest_0_OUTDIR) netbsd + ifeq ($(KBUILD_HOST),solaris) + VBoxGuest_LDFLAGS.solaris += -N misc/ctf + else + VBoxGuest_SOURCES.solaris = solaris/deps.asm + VBoxGuest_solaris/deps.asm_ASFLAGS = -f bin -g null + endif + ifneq ($(KBUILD_TARGET),os2) + ifeq ($(KBUILD_TARGET),win) + VBoxGuest_LDFLAGS.x86 = -Entry:DriverEntry@8 + VBoxGuest_LDFLAGS.amd64 = -Entry:DriverEntry + ifeq ($(KBUILD_TARGET_ARCH),x86) + VBoxGuest_SDKS = ReorderCompilerIncs $(VBOX_WINDDK_GST_NT4) + VBoxGuest_LIBS = \ + $(VBOX_LIB_VBGL_R0BASE) \ + $(VBOX_LIB_IPRT_GUEST_R0) \ + $(PATH_SDK_$(VBOX_WINDDK_GST_NT4)_LIB)/exsup.lib \ + $(PATH_SDK_$(VBOX_WINDDK_GST_NT4)_LIB)/int64.lib \ + $(PATH_SDK_$(VBOX_WINDDK_GST)_LIB)/ntoskrnl.lib \ + $(PATH_SDK_$(VBOX_WINDDK_GST)_LIB)/hal.lib + ifneq ($(VBOX_VCC_CC_GUARD_CF),) + VBoxGuest_LIBS += \ + $(PATH_SDK_$(VBOX_WINDDK_GST)_LIB)/BufferOverflowK.lib + endif + else + VBoxGuest_LIBS = \ + $(PATH_SDK_$(VBOX_WINDDK_GST)_LIB)/ntoskrnl.lib \ + $(PATH_SDK_$(VBOX_WINDDK_GST)_LIB)/hal.lib + endif + VBoxGuest_USES.win += vboximportchecker + VBoxGuest_VBOX_IMPORT_CHECKER.win.x86 = nt31/r0 + VBoxGuest_VBOX_IMPORT_CHECKER.win.amd64 = xp64/r0 + endif # win + ifn1of ($(KBUILD_TARGET), linux freebsd netbsd solaris haiku) + VBoxGuest_SOURCES = VBoxGuest-$(KBUILD_TARGET).cpp + else + VBoxGuest_SOURCES = VBoxGuest-$(KBUILD_TARGET).c + VBoxGuest_$(KBUILD_TARGET).c_DEPS = $(VBOX_SVN_REV_HEADER) + ifeq ($(KBUILD_TARGET),freebsd) + VBoxGuest-$(KBUILD_TARGET).c_CFLAGS = -Wno-sign-compare # /usr/src/sys/sys/vmmeter.h: In function 'vm_paging_needed' + endif + endif + VBoxGuest_SOURCES += \ + VBoxGuest.cpp + VBoxGuest_SOURCES.win += \ + win/VBoxGuest.rc + VBoxGuest_SOURCES.win.x86 += \ + ../../../Runtime/common/string/strcmp.asm \ + ../../../Runtime/common/string/strchr.asm \ + ../../../Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp \ + ../../../Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm + VBoxGuest_LIBS += \ + $(VBOX_LIB_VBGL_R0BASE) \ + $(VBOX_LIB_IPRT_GUEST_R0) + VBoxGuest_ORDERDEPS.freebsd = \ + $(PATH_STAGE)/gen-sys-hdrs/pci_if.h \ + $(PATH_STAGE)/gen-sys-hdrs/bus_if.h \ + $(PATH_STAGE)/gen-sys-hdrs/device_if.h + ifeq ($(KBUILD_TARGET),haiku) + # Haiku drivers cannot export symbols for other drivers, but modules can. + # Therefore vboxguest is a module containing the ring-0 guest lib, and vboxdev/vboxsf + # use this module to access the guest lib + SYSMODS += VBoxDev + VBoxDev_TEMPLATE = VBOXGUESTR0 + VBoxDev_NAME = vboxdev + VBoxDev_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV) _KERNEL_MODE=1 VBGL_VBOXGUEST VBOX_WITH_HGCM IN_RING0 + VBoxDev_SOURCES = VBoxDev-haiku.c VBoxGuest-haiku-stubs.c + endif + else # OS/2: + # The library order is crucial, so a bit of trickery is necessary. + # A library is used to make sure that VBoxGuestA-os2.asm is first in the link. (temporary hack?) +VBoxGuest_SOURCES = \ + VBoxGuestA-os2.asm + ifdef VBOX_USE_WATCOM_FOR_OS2 +VBoxGuest_LIBS = \ + $(VBoxGuestLibOs2Hack_1_TARGET) \ + $(VBOX_LIB_VBGL_R0BASE) \ + $(VBOX_LIB_IPRT_GUEST_R0) \ + $(PATH_IGCC)/lib/libend.lib + else +VBoxGuest_SOURCES += \ + VBoxGuest-os2.def +#VBoxGuest_LDFLAGS = -s -t -v +VBoxGuest_LIBS = \ + $(VBoxGuestLibOs2Hack_1_TARGET) \ + $(VBOX_LIB_VBGL_R0BASE) \ + $(VBOX_LIB_IPRT_GUEST_R0) \ + $(VBOX_GCC_LIBGCC) \ + end + endif +## When debugging init with kDrvTest: +#VBoxGuest_NAME = VBoxGst + +# See above. +LIBRARIES += VBoxGuestLibOs2Hack +VBoxGuestLibOs2Hack_TEMPLATE = VBOXGUESTR0LIB +VBoxGuestLibOs2Hack_INSTTYPE = none +VBoxGuestLibOs2Hack_DEFS = $(VBoxGuest_DEFS) +VBoxGuestLibOs2Hack_INCS = \ + . \ + $(PATH_ROOT)/src/VBox/Runtime/include # for the os2ddk +VBoxGuestLibOs2Hack_SOURCES = \ + VBoxGuest-os2.cpp \ + VBoxGuest.cpp + endif # OS/2 + + VBoxGuest.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV) +endif # enabled + + +if defined(VBOX_WITH_ADDITION_DRIVERS) && "$(KBUILD_TARGET)" == "darwin" + # Files necessary to make a darwin kernel extension bundle. + INSTALLS += VBoxGuest.kext + VBoxGuest.kext_INST = $(INST_ADDITIONS)/VBoxGuest.kext/Contents/ + VBoxGuest.kext_SOURCES = $(VBoxGuest.kext_0_OUTDIR)/Contents/Info.plist + VBoxGuest.kext_CLEAN = $(VBoxGuest.kext_0_OUTDIR)/Contents/Info.plist + VBoxGuest.kext_BLDDIRS = $(VBoxGuest.kext_0_OUTDIR)/Contents/ + +$$(VBoxGuest.kext_0_OUTDIR)/Contents/Info.plist: \ + $(PATH_SUB_CURRENT)/darwin/Info.plist \ + $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxGuest,$@,$<) + $(QUIET)$(RM) -f $@ + $(QUIET)$(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' \ + -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \ + -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \ + -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \ + --output $@ \ + $< + +$(evalcall2 VBOX_TEST_SIGN_KEXT,VBoxGuest) +endif # darwin + + +ifeq ($(KBUILD_TARGET),linux) + # + # Install the source files and script(s). + # + include $(PATH_SUB_CURRENT)/linux/files_vboxguest + # sources and stuff. + INSTALLS += vboxguest-src + vboxguest-src_INST = $(INST_ADDITIONS)src/vboxguest/ + vboxguest-src_MODE = a+r,u+w + vboxguest-src_SOURCES = $(subst ",,$(FILES_VBOXGUEST_NOBIN)) + + INSTALLS += vboxguest-scripts + vboxguest-scripts_INST = $(INST_ADDITIONS)src/ + vboxguest-scripts_MODE = a+rx,u+w + vboxguest-scripts_SOURCES = ../../../HostDrivers/linux/build_in_tmp + + # scripts. + INSTALLS += vboxguest-sh + vboxguest-sh_INST = $(INST_ADDITIONS)src/vboxguest/ + vboxguest-sh_MODE = a+rx,u+w + vboxguest-sh_SOURCES = $(subst ",,$(FILES_VBOXGUEST_BIN)) + + # + # Build test for the Guest Additions kernel module (kmk check). + # +$(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,vboxguest-src,,save_symvers) +endif # Linux + +ifeq ($(KBUILD_TARGET),freebsd) + # + # Install the source files and script(s). + # + include $(PATH_SUB_CURRENT)/freebsd/files_vboxguest + # sources and stuff. + INSTALLS += vboxguest-src + vboxguest-src_INST = $(INST_ADDITIONS)src/vboxguest/ + vboxguest-src_MODE = a+r,u+w + vboxguest-src_SOURCES = $(subst ",,$(FILES_VBOXGUEST_NOBIN)) + +endif # FreeBSD + +ifeq ($(KBUILD_TARGET),win) + # + # VBoxGuestInst - The installer. + # + PROGRAMS.win.x86 += VBoxGuestInstNT + VBoxGuestInstNT_TEMPLATE = VBoxGuestR3Exe + ifndef VBOX_WITH_NOCRT_STATIC + VBoxGuestInstNT_LDFLAGS := -Include:_vcc100_kernel32_fakes_asm # Temporary kludge to deal with some link order issue. + endif + VBoxGuestInstNT_INCS = ../../WINNT/include + VBoxGuestInstNT_SOURCES = win/VBoxGuestInst.cpp + VBoxGuestInstNT_USES = vboximportchecker + VBoxGuestInstNT_VBOX_IMPORT_CHECKER.win.x86 = nt31 +endif + + +# +# Helper script. +# +INSTALLS.solaris += VBoxGuestLoad +VBoxGuestLoad_TEMPLATE = VBOXGUESTR0 +VBoxGuestLoad_EXEC_SOURCES = solaris/load.sh + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c b/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c new file mode 100644 index 00000000..160bf8d4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c @@ -0,0 +1,446 @@ +/* $Id: VBoxDev-haiku.c $ */ +/** @file + * VBoxGuest kernel driver, Haiku Guest Additions, implementation. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * 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 * +*********************************************************************************************************************************/ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <OS.h> +#include <Drivers.h> +#include <KernelExport.h> +#include <PCI.h> + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> + +#define DRIVER_NAME "vboxdev" +#define DEVICE_NAME "misc/vboxguest" +#define MODULE_NAME "generic/vboxguest" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +int32 api_version = B_CUR_DRIVER_API_VERSION; + + +/** + * Driver open hook. + * + * @param name The name of the device as returned by publish_devices. + * @param flags Open flags. + * @param cookie Where to store the session pointer. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuOpen(const char *name, uint32 flags, void **cookie) +{ + int rc; + PVBOXGUESTSESSION pSession; + + LogFlow((DRIVER_NAME ":vgdrvHaikuOpen\n")); + + /* + * Create a new session. + */ + rc = VGDrvCommonCreateUserSession(&g_DevExt, VMMDEV_REQUESTOR_USERMODE, &pSession); + if (RT_SUCCESS(rc)) + { + Log((DRIVER_NAME ":vgdrvHaikuOpen success: g_DevExt=%p pSession=%p rc=%d pid=%d\n",&g_DevExt, pSession, rc,(int)RTProcSelf())); + ASMAtomicIncU32(&cUsers); + *cookie = pSession; + return B_OK; + } + + LogRel((DRIVER_NAME ":vgdrvHaikuOpen: failed. rc=%d\n", rc)); + return RTErrConvertToErrno(rc); +} + + +/** + * Driver close hook. + * @param cookie The session. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuClose(void *cookie) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + Log(("vgdrvHaikuClose: pSession=%p\n", pSession)); + + /** @todo r=ramshankar: should we really be using the session spinlock here? */ + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + /** @todo we don't know if it belongs to this session!! */ + if (sState.selectSync) + { + //dprintf(DRIVER_NAME "close: unblocking select %p %x\n", sState.selectSync, sState.selectEvent); + notify_select_event(sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = (void *)NULL; + } + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + return B_OK; +} + + +/** + * Driver free hook. + * @param cookie The session. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuFree(void *cookie) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + Log(("vgdrvHaikuFree: pSession=%p\n", pSession)); + + /* + * Close the session if it's still hanging on to the device... + */ + if (RT_VALID_PTR(pSession)) + { + VGDrvCommonCloseSession(&g_DevExt, pSession); + ASMAtomicDecU32(&cUsers); + } + else + Log(("vgdrvHaikuFree: si_drv1=%p!\n", pSession)); + return B_OK; +} + + +/** + * Driver IOCtl entry. + * @param cookie The session. + * @param op The operation to perform. + * @param data The data associated with the operation. + * @param len Size of the data in bytes. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + int rc; + Log(("vgdrvHaikuIOCtl: cookie=%p op=0x%08x data=%p len=%lu)\n", cookie, op, data, len)); + + /* + * Validate the input. + */ + if (RT_UNLIKELY(!RT_VALID_PTR(pSession))) + return EINVAL; + + /* + * Validate the request wrapper. + */ +#if 0 + if (IOCPARM_LEN(ulCmd) != sizeof(VBGLBIGREQ)) + { + Log((DRIVER_NAME ": vgdrvHaikuIOCtl: bad request %lu size=%lu expected=%d\n", ulCmd, IOCPARM_LEN(ulCmd), + sizeof(VBGLBIGREQ))); + return ENOTTY; + } +#endif + + if (RT_UNLIKELY(len > _1M * 16)) + { + dprintf(DRIVER_NAME ": vgdrvHaikuIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", (unsigned)len, data, op); + return EINVAL; + } + + /* + * Read the request. + */ + void *pvBuf = NULL; + if (RT_LIKELY(len > 0)) + { + pvBuf = RTMemTmpAlloc(len); + if (RT_UNLIKELY(!pvBuf)) + { + LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", len)); + return ENOMEM; + } + + /** @todo r=ramshankar: replace with RTR0MemUserCopyFrom() */ + rc = user_memcpy(pvBuf, data, len); + if (RT_UNLIKELY(rc < 0)) + { + RTMemTmpFree(pvBuf); + LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p data=%p op=%d. rc=%d\n", pvBuf, data, op, rc)); + return EFAULT; + } + if (RT_UNLIKELY(!RT_VALID_PTR(pvBuf))) + { + RTMemTmpFree(pvBuf); + LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: pvBuf invalid pointer %p\n", pvBuf)); + return EINVAL; + } + } + Log(("vgdrvHaikuIOCtl: pSession=%p pid=%d.\n", pSession,(int)RTProcSelf())); + + /* + * Process the IOCtl. + */ + size_t cbDataReturned; + rc = VGDrvCommonIoCtl(op, &g_DevExt, pSession, pvBuf, len, &cbDataReturned); + if (RT_SUCCESS(rc)) + { + rc = 0; + if (RT_UNLIKELY(cbDataReturned > len)) + { + Log(("vgdrvHaikuIOCtl: too much output data %d expected %d\n", cbDataReturned, len)); + cbDataReturned = len; + } + if (cbDataReturned > 0) + { + rc = user_memcpy(data, pvBuf, cbDataReturned); + if (RT_UNLIKELY(rc < 0)) + { + Log(("vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, op, rc)); + rc = EFAULT; + } + } + } + else + { + Log(("vgdrvHaikuIOCtl: VGDrvCommonIoCtl failed. rc=%d\n", rc)); + rc = EFAULT; + } + RTMemTmpFree(pvBuf); + return rc; +} + + +/** + * Driver select hook. + * + * @param cookie The session. + * @param event The event. + * @param ref ??? + * @param sync ??? + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuSelect(void *cookie, uint8 event, uint32 ref, selectsync *sync) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + status_t err = B_OK; + + switch (event) + { + case B_SELECT_READ: + break; + default: + return EINVAL; + } + + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + pSession->u32MousePosChangedSeq = u32CurSeq; + notify_select_event(sync, event); + } + else if (sState.selectSync == NULL) + { + sState.selectEvent = (uint8_t)event; + sState.selectRef = (uint32_t)ref; + sState.selectSync = (void *)sync; + } + else + err = B_WOULD_BLOCK; + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + + return err; +} + + +/** + * Driver deselect hook. + * @param cookie The session. + * @param event The event. + * @param sync ??? + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuDeselect(void *cookie, uint8 event, selectsync *sync) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + status_t err = B_OK; + //dprintf(DRIVER_NAME "deselect(,%d,%p)\n", event, sync); + + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + if (sState.selectSync == sync) + { + //dprintf(DRIVER_NAME "deselect: dropping: %p %x\n", sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = NULL; + } + else + err = B_OK; + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + return err; +} + + +/** + * Driver write hook. + * @param cookie The session. + * @param position The offset. + * @param data Pointer to the data. + * @param numBytes Where to store the number of bytes written. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuWrite(void *cookie, off_t position, const void *data, size_t *numBytes) +{ + *numBytes = 0; + return B_OK; +} + + +/** + * Driver read hook. + * @param cookie The session. + * @param position The offset. + * @param data Pointer to the data. + * @param numBytes Where to store the number of bytes read. + * + * @return Haiku status code. + */ +static status_t vgdrvHaikuRead(void *cookie, off_t position, void *data, size_t *numBytes) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + + if (*numBytes == 0) + return B_OK; + + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + pSession->u32MousePosChangedSeq = u32CurSeq; + *numBytes = 1; + return B_OK; + } + + *numBytes = 0; + return B_OK; +} + + + +status_t init_hardware() +{ + return get_module(MODULE_NAME, (module_info **)&g_VBoxGuest); +} + +status_t init_driver() +{ + return B_OK; +} + +device_hooks *find_device(const char *name) +{ + static device_hooks s_vgdrvHaikuDeviceHooks = + { + vgdrvHaikuOpen, + vgdrvHaikuClose, + vgdrvHaikuFree, + vgdrvHaikuIOCtl, + vgdrvHaikuRead, + vgdrvHaikuWrite, + vgdrvHaikuSelect, + vgdrvHaikuDeselect, + }; + return &s_vgdrvHaikuDeviceHooks; +} + +const char **publish_devices() +{ + static const char *s_papszDevices[] = { DEVICE_NAME, NULL }; + return s_papszDevices; +} + +void uninit_driver() +{ + put_module(MODULE_NAME); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp new file mode 100644 index 00000000..cdabc4ac --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp @@ -0,0 +1,1393 @@ +/* $Id: VBoxGuest-darwin.cpp $ */ +/** @file + * VBoxGuest - Darwin Specifics. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_VGDRV +/* + * 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 <VBox/version.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/power.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <iprt/spinlock.h> +#include <iprt/string.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 <sys/kauth.h> +#define _OS_OSUNSERIALIZE_H /* HACK ALERT! Block importing OSUnserialized.h as it causes compilation trouble with + newer clang versions and the 10.15 SDK, and we really don't need it. Sample error: + libkern/c++/OSUnserialize.h:72:2: error: use of OSPtr outside of a return type [-Werror,-Wossharedptr-misuse] */ +#include <IOKit/IOService.h> +#include <IOKit/IOUserClient.h> +#include <IOKit/pwr_mgt/RootDomain.h> +#include <IOKit/pci/IOPCIDevice.h> +#include <IOKit/IOBufferMemoryDescriptor.h> +#include <IOKit/IOFilterInterruptEventSource.h> +#include "VBoxGuestInternal.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The system device node name. */ +#define DEVICE_NAME_SYS "vboxguest" +/** The user device node name. */ +#define DEVICE_NAME_USR "vboxguestu" + + +/** @name For debugging/whatever, now permanent. + * @{ */ +#define VBOX_PROC_SELFNAME_LEN 31 +#define VBOX_RETRIEVE_CUR_PROC_NAME(a_Name) char a_Name[VBOX_PROC_SELFNAME_LEN + 1]; \ + proc_selfname(a_Name, VBOX_PROC_SELFNAME_LEN) +/** @} */ + +#ifndef minor +/* The inlined C++ function version minor() takes the wrong parameter + type, uint32_t instead of dev_t. This kludge works around that. */ +# define minor(x) minor((uint32_t)(x)) +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +RT_C_DECLS_BEGIN +static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData); +static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData); +static int vgdrvDarwinCharDevRemove(void); + +static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess); +static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess); + +static int vgdrvDarwinErr2DarwinErr(int rc); + +static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize); +RT_C_DECLS_END + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The service class for handling the VMMDev PCI device. + * + * Instantiated when the module is loaded (and on PCI hotplugging?). + */ +class org_virtualbox_VBoxGuest : public IOService +{ + OSDeclareDefaultStructors(org_virtualbox_VBoxGuest); + +private: + IOPCIDevice *m_pIOPCIDevice; + IOMemoryMap *m_pMap; + IOFilterInterruptEventSource *m_pInterruptSrc; + + bool setupVmmDevInterrupts(IOService *pProvider); + bool disableVmmDevInterrupts(void); + bool isVmmDev(IOPCIDevice *pIOPCIDevice); + +protected: + /** Non-NULL if interrupts are registered. Probably same as getProvider(). */ + IOService *m_pInterruptProvider; + +public: + virtual bool init(OSDictionary *pDictionary = 0); + virtual void free(void); + virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score); + virtual bool start(IOService *pProvider); + virtual void stop(IOService *pProvider); + virtual bool terminate(IOOptionBits fOptions); + static void vgdrvDarwinIrqHandler(OSObject *pTarget, void *pvRefCon, IOService *pNub, int iSrc); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuest, 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. + * + * Instantiated when userland does IOServiceOpen(). + */ +class org_virtualbox_VBoxGuestClient : public IOUserClient +{ + OSDeclareDefaultStructors(org_virtualbox_VBoxGuestClient); + +private: + /** Guard against the parent class growing and us using outdated headers. */ + uint8_t m_abSafetyPadding[256]; + + PVBOXGUESTSESSION m_pSession; /**< The session. */ + task_t m_Task; /**< The client task. */ + org_virtualbox_VBoxGuest *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); + + RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT(); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuestClient, IOUserClient); + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Declare the module stuff. + */ +RT_C_DECLS_BEGIN +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(VBoxGuest, VBOX_VERSION_STRING, _start, _stop) +DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = vgdrvDarwinStart; +DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = vgdrvDarwinStop; +DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__; +RT_C_DECLS_END + + +/** + * Device extention & session data association structure. + */ +static VBOXGUESTDEVEXT g_DevExt; + +/** + * The character device switch table for the driver. + */ +static struct cdevsw g_DevCW = +{ + /*.d_open = */ vgdrvDarwinOpen, + /*.d_close = */ vgdrvDarwinClose, + /*.d_read = */ eno_rdwrt, + /*.d_write = */ eno_rdwrt, + /*.d_ioctl = */ vgdrvDarwinIOCtl, + /*.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 = */ (void *)(uintptr_t)&enodev, //eno_getc, + /*.d_putc = */ (void *)(uintptr_t)&enodev, //eno_putc, + /*.d_type = */ 0 +}; + +/** Major device number. */ +static int g_iMajorDeviceNo = -1; +/** Registered devfs device handle. */ +static void *g_hDevFsDeviceSys = NULL; +/** Registered devfs device handle for the user device. */ +static void *g_hDevFsDeviceUsr = NULL; /**< @todo 4 later */ + +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PVBOXGUESTSESSION 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; +/** Makes sure there is only one org_virtualbox_VBoxGuest instance. */ +static bool volatile g_fInstantiated = 0; +/** The notifier handle for the sleep callback handler. */ +static IONotifier *g_pSleepNotifier = NULL; + + +/** + * Start the kernel module. + */ +static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData) +{ + RT_NOREF(pKModInfo, pvData); +#ifdef DEBUG + printf("vgdrvDarwinStart\n"); +#endif +#if 0 + gIOKitDebug |= 0x001 //kIOLogAttach + | 0x002 //kIOLogProbe + | 0x004 //kIOLogStart + | 0x008 //kIOLogRegister + | 0x010 //kIOLogMatch + | 0x020 //kIOLogConfig + ; +#endif + + /* + * Initialize IPRT. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + Log(("VBoxGuest: driver loaded\n")); + return KMOD_RETURN_SUCCESS; + } + + RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed with rc=%Rrc\n", rc); + printf("VBoxGuest: RTR0Init failed with rc=%d\n", rc); + return KMOD_RETURN_FAILURE; +} + + +/** + * Stop the kernel module. + */ +static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData) +{ + RT_NOREF(pKModInfo, pvData); + + /** @todo we need to check for VBoxSF clients? */ + + RTLogBackdoorPrintf("VBoxGuest: calling RTR0TermForced ...\n"); + RTR0TermForced(); + + RTLogBackdoorPrintf("VBoxGuest: vgdrvDarwinStop returns.\n"); + printf("VBoxGuest: driver unloaded\n"); + return KMOD_RETURN_SUCCESS; +} + + +/** + * Register VBoxGuest char device + */ +static int vgdrvDarwinCharDevInit(void) +{ + int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestDarwin"); + if (RT_SUCCESS(rc)) + { + /* + * Registering ourselves as a character device. + */ + g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW); + if (g_iMajorDeviceNo >= 0) + { + /** @todo limit /dev/vboxguest access. */ + g_hDevFsDeviceSys = devfs_make_node(makedev((uint32_t)g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS); + if (g_hDevFsDeviceSys != NULL) + { + /* + * And a all-user device. + */ + g_hDevFsDeviceUsr = devfs_make_node(makedev((uint32_t)g_iMajorDeviceNo, 1), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_USR); + if (g_hDevFsDeviceUsr != NULL) + { + /* + * Register a sleep/wakeup notification callback. + */ + g_pSleepNotifier = registerPrioritySleepWakeInterest(&vgdrvDarwinSleepHandler, &g_DevExt, NULL); + if (g_pSleepNotifier != NULL) + return KMOD_RETURN_SUCCESS; + } + } + } + vgdrvDarwinCharDevRemove(); + } + return KMOD_RETURN_FAILURE; +} + + +/** + * Unregister VBoxGuest char devices and associated session spinlock. + */ +static int vgdrvDarwinCharDevRemove(void) +{ + if (g_pSleepNotifier) + { + g_pSleepNotifier->remove(); + g_pSleepNotifier = NULL; + } + + if (g_hDevFsDeviceSys) + { + devfs_remove(g_hDevFsDeviceSys); + g_hDevFsDeviceSys = NULL; + } + + if (g_hDevFsDeviceUsr) + { + devfs_remove(g_hDevFsDeviceUsr); + g_hDevFsDeviceUsr = NULL; + } + + if (g_iMajorDeviceNo != -1) + { + int rc2 = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + Assert(rc2 == g_iMajorDeviceNo); NOREF(rc2); + g_iMajorDeviceNo = -1; + } + + if (g_Spinlock != NIL_RTSPINLOCK) + { + int rc2 = RTSpinlockDestroy(g_Spinlock); AssertRC(rc2); + g_Spinlock = NIL_RTSPINLOCK; + } + + return KMOD_RETURN_SUCCESS; +} + + +/** + * Device open. Called on open /dev/vboxguest and (later) /dev/vboxguestu. + * + * @param Dev The device number. + * @param fFlags ???. + * @param fDevType ???. + * @param pProcess The process issuing this request. + */ +static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + RT_NOREF(fFlags, fDevType); + + /* + * Only two minor devices numbers are allowed. + */ + if (minor(Dev) != 0 && minor(Dev) != 1) + return EACCES; + + /* + * The process issuing the request must be the current process. + */ + RTPROCESS Process = RTProcSelf(); + if ((int)Process != proc_pid(pProcess)) + return EIO; + + /* + * Find the session created by org_virtualbox_VBoxGuestClient, 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. + */ + const bool fUnrestricted = minor(Dev) == 0; + int rc = VINF_SUCCESS; + PVBOXGUESTSESSION pSession = NULL; + kauth_cred_t pCred = kauth_cred_proc_ref(pProcess); + if (pCred) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + RTUID Uid = kauth_cred_getruid(pCred); + RTGID Gid = kauth_cred_getrgid(pCred); +#else + RTUID Uid = pCred->cr_ruid; + RTGID Gid = pCred->cr_rgid; +#endif + unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + + pSession = g_apSessionHashTab[iHash]; + while (pSession && pSession->Process != Process) + pSession = pSession->pNextHash; + if (pSession) + { + if (!pSession->fOpened) + { + pSession->fOpened = true; + pSession->fUserSession = !fUnrestricted; + pSession->fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + if (Uid == 0) + pSession->fRequestor |= VMMDEV_REQUESTOR_USR_ROOT; + else + pSession->fRequestor |= VMMDEV_REQUESTOR_USR_USER; + if (Gid == 0) + pSession->fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL; + if (!fUnrestricted) + pSession->fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE; + pSession->fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW; /** @todo see if we can figure out console relationship of pProc. */ + } + else + rc = VERR_ALREADY_LOADED; + } + else + rc = VERR_GENERAL_FAILURE; + + RTSpinlockRelease(g_Spinlock); +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + kauth_cred_unref(&pCred); +#else /* 10.4 */ + /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions + of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */ + kauth_cred_rele(pCred); +#endif /* 10.4 */ + } + else + rc = VERR_INVALID_PARAMETER; + + Log(("vgdrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess))); + return vgdrvDarwinErr2DarwinErr(rc); +} + + +/** + * Close device. + */ +static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + RT_NOREF(Dev, fFlags, fDevType, pProcess); + Log(("vgdrvDarwinClose: pid=%d\n", (int)RTProcSelf())); + Assert(proc_pid(pProcess) == (int)RTProcSelf()); + + /* + * Hand the session closing to org_virtualbox_VBoxGuestClient. + */ + org_virtualbox_VBoxGuestClient::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 request data. + * @param fFlags Flag saying we're a character device (like we didn't know already). + * @param pProcess The process issuing this request. + */ +static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) +{ + RT_NOREF(Dev, fFlags); + const bool fUnrestricted = minor(Dev) == 0; + const RTPROCESS Process = (RTPROCESS)proc_pid(pProcess); + const unsigned iHash = SESSION_HASH(Process); + PVBOXGUESTSESSION pSession; + + /* + * Find the session. + */ + RTSpinlockAcquire(g_Spinlock); + pSession = g_apSessionHashTab[iHash]; + while (pSession && (pSession->Process != Process || pSession->fUserSession == fUnrestricted || !pSession->fOpened)) + pSession = pSession->pNextHash; + + //if (RT_LIKELY(pSession)) + // supdrvSessionRetain(pSession); + + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", + (int)Process, iCmd)); + return EINVAL; + } + + /* + * Deal with the high-speed IOCtl. + */ + int rc; + if (VBGL_IOCTL_IS_FAST(iCmd)) + rc = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession); + else + rc = vgdrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); + + //supdrvSessionRelease(pSession); + return rc; +} + + +/** + * 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 request data. + * @param pProcess The calling process. + */ +static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) +{ + RT_NOREF(pProcess); + LogFlow(("vgdrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); + + + /* + * Buffered or unbuffered? + */ + PVBGLREQHDR pHdr; + user_addr_t pUser = 0; + void *pvPageBuf = NULL; + uint32_t cbReq = IOCPARM_LEN(iCmd); + if ((IOC_DIRMASK & iCmd) == IOC_INOUT) + { + pHdr = (PVBGLREQHDR)pData; + if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) + { + LogRel(("vgdrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd)); + return EINVAL; + } + if (RT_UNLIKELY(pHdr->uVersion != VBGLREQHDR_VERSION)) + { + LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", pHdr->uVersion, iCmd)); + return EINVAL; + } + if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq + || pHdr->cbIn < sizeof(*pHdr) + || (pHdr->cbOut < sizeof(*pHdr) && pHdr->cbOut != 0))) + { + LogRel(("vgdrvDarwinIOCtlSlow: 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. + */ + VBGLREQHDR Hdr; + pUser = (user_addr_t)*(void **)pData; + int rc = copyin(pUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + LogRel(("vgdrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); + return rc; + } + if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION)) + { + LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", Hdr.uVersion, iCmd)); + return EINVAL; + } + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || (Hdr.cbOut < sizeof(Hdr) && Hdr.cbOut != 0) + || cbReq > _1M*16)) + { + LogRel(("vgdrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd)); + return EINVAL; + } + + /* + * Allocate buffer and copy in the data. + */ + pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq); + if (!pHdr) + pvPageBuf = pHdr = (PVBGLREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); + if (RT_UNLIKELY(!pHdr)) + { + LogRel(("vgdrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); + return ENOMEM; + } + rc = copyin(pUser, pHdr, Hdr.cbIn); + if (RT_UNLIKELY(rc)) + { + LogRel(("vgdrvDarwinIOCtlSlow: 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; + } + if (Hdr.cbIn < cbReq) + RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn); + } + else + { + Log(("vgdrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + int rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbReq); + if (RT_LIKELY(!rc)) + { + /* + * If not buffered, copy back the buffer before returning. + */ + if (pUser) + { + uint32_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + LogRel(("vgdrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd)); + cbOut = cbReq; + } + rc = copyout(pHdr, pUser, cbOut); + if (RT_UNLIKELY(rc)) + LogRel(("vgdrvDarwinIOCtlSlow: 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(("vgdrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); + rc = EINVAL; + } + + Log2(("vgdrvDarwinIOCtlSlow: returns %d\n", rc)); + return rc; +} + + +/** + * @note This code is duplicated on other platforms with variations, so please + * keep them all up to date when making changes! + */ +int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + /* + * Simple request validation (common code does the rest). + */ + int rc; + if ( RT_VALID_PTR(pReqHdr) + && cbReq >= sizeof(*pReqHdr)) + { + /* + * All requests except the connect one requires a valid session. + */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; + if (pSession) + { + if ( RT_VALID_PTR(pSession) + && pSession->pDevExt == &g_DevExt) + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + else + rc = VERR_INVALID_HANDLE; + } + else if (uReq == VBGL_IOCTL_IDC_CONNECT) + { + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + if (RT_FAILURE(rc)) + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + } + else + rc = VERR_INVALID_HANDLE; + } + else + rc = VERR_INVALID_POINTER; + return rc; +} + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + NOREF(pDevExt); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +/** + * Callback for blah blah blah. + * + * @todo move to IPRT. + */ +static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, + IOService *pProvider, void *pvMsgArg, vm_size_t cbMsgArg) +{ + RT_NOREF(pvTarget, pProvider, pvMsgArg, cbMsgArg); + LogFlow(("VBoxGuest: Got sleep/wake notice. Message type was %x\n", uMessageType)); + + if (uMessageType == kIOMessageSystemWillSleep) + RTPowerSignalEvent(RTPOWEREVENT_SUSPEND); + else if (uMessageType == kIOMessageSystemHasPoweredOn) + RTPowerSignalEvent(RTPOWEREVENT_RESUME); + + acknowledgeSleepWakeNotification(pvRefCon); + + return 0; +} + + +/** + * Converts an IPRT error code to a darwin error code. + * + * @returns corresponding darwin error code. + * @param rc IPRT status code. + */ +static int vgdrvDarwinErr2DarwinErr(int rc) +{ + switch (rc) + { + case VINF_SUCCESS: return 0; + case VERR_GENERAL_FAILURE: return EACCES; + case VERR_INVALID_PARAMETER: return EINVAL; + case VERR_INVALID_MAGIC: return EILSEQ; + case VERR_INVALID_HANDLE: return ENXIO; + case VERR_INVALID_POINTER: return EFAULT; + case VERR_LOCK_FAILED: return ENOLCK; + case VERR_ALREADY_LOADED: return EEXIST; + case VERR_PERMISSION_DENIED: return EPERM; + case VERR_VERSION_MISMATCH: return ENOSYS; + } + + return EPERM; +} + + +/* + * + * org_virtualbox_VBoxGuest + * + * - IOService diff resync - + * - IOService diff resync - + * - IOService diff resync - + * + */ + + +/** + * Initialize the object. + */ +bool org_virtualbox_VBoxGuest::init(OSDictionary *pDictionary) +{ + LogFlow(("IOService::init([%p], %p)\n", this, pDictionary)); + if (IOService::init(pDictionary)) + { + /* init members. */ + return true; + } + return false; +} + + +/** + * Free the object. + */ +void org_virtualbox_VBoxGuest::free(void) +{ + RTLogBackdoorPrintf("IOService::free([%p])\n", this); /* might go sideways if we use LogFlow() here. weird. */ + 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_VBoxGuest::probe(IOService *pProvider, SInt32 *pi32Score) +{ + LogFlow(("IOService::probe([%p])\n", this)); + IOService *pRet = IOService::probe(pProvider, pi32Score); + LogFlow(("IOService::probe([%p]) returns %p *pi32Score=%d\n", this, pRet, pi32Score ? *pi32Score : -1)); + return pRet; +} + + +/** + * Start this service. + */ +bool org_virtualbox_VBoxGuest::start(IOService *pProvider) +{ + LogFlow(("IOService::start([%p])\n", this)); + + /* + * Low level initialization / device initialization should be performed only once. + */ + if (ASMAtomicCmpXchgBool(&g_fInstantiated, true, false)) + { + /* + * Make sure it's a PCI device. + */ + m_pIOPCIDevice = OSDynamicCast(IOPCIDevice, pProvider); + if (m_pIOPCIDevice) + { + /* + * Call parent. + */ + if (IOService::start(pProvider)) + { + /* + * Is it the VMM device? + */ + if (isVmmDev(m_pIOPCIDevice)) + { + /* + * Enable I/O port and memory regions on the device. + */ + m_pIOPCIDevice->setMemoryEnable(true); + m_pIOPCIDevice->setIOEnable(true); + + /* + * Region #0: I/O ports. Mandatory. + */ + IOMemoryDescriptor *pMem = m_pIOPCIDevice->getDeviceMemoryWithIndex(0); + if (pMem) + { + IOPhysicalAddress IOPortBasePhys = pMem->getPhysicalAddress(); + if ((IOPortBasePhys >> 16) == 0) + { + RTIOPORT IOPortBase = (RTIOPORT)IOPortBasePhys; + void *pvMMIOBase = NULL; + uint32_t cbMMIO = 0; + + /* + * Region #1: Shared Memory. Technically optional. + */ + m_pMap = m_pIOPCIDevice->mapDeviceMemoryWithIndex(1); + if (m_pMap) + { + pvMMIOBase = (void *)m_pMap->getVirtualAddress(); + cbMMIO = (uint32_t)m_pMap->getLength(); + } + + /* + * Initialize the device extension. + */ + int rc = VGDrvCommonInitDevExt(&g_DevExt, IOPortBase, pvMMIOBase, cbMMIO, + ARCH_BITS == 64 ? VBOXOSTYPE_MacOS_x64 : VBOXOSTYPE_MacOS, 0); + if (RT_SUCCESS(rc)) + { + /* + * Register the device nodes and enable interrupts. + */ + rc = vgdrvDarwinCharDevInit(); + if (rc == KMOD_RETURN_SUCCESS) + { + if (setupVmmDevInterrupts(pProvider)) + { + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + /* + * Just register the service and we're done! + */ + registerService(); + + LogRel(("VBoxGuest: IOService started\n")); + return true; + } + + LogRel(("VBoxGuest: Failed to set up interrupts\n")); + vgdrvDarwinCharDevRemove(); + } + else + LogRel(("VBoxGuest: Failed to initialize character devices (rc=%#x).\n", rc)); + + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + LogRel(("VBoxGuest: Failed to initialize common code (rc=%Rrc).\n", rc)); + + if (m_pMap) + { + m_pMap->release(); + m_pMap = NULL; + } + } + else + LogRel(("VBoxGuest: Bad I/O port address: %#RX64\n", (uint64_t)IOPortBasePhys)); + } + else + LogRel(("VBoxGuest: The device missing is the I/O port range (#0).\n")); + } + else + LogRel(("VBoxGuest: Not the VMMDev (%#x:%#x).\n", + m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID), m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID))); + + IOService::stop(pProvider); + } + } + else + LogRel(("VBoxGuest: Provider is not an instance of IOPCIDevice.\n")); + + ASMAtomicXchgBool(&g_fInstantiated, false); + } + return false; +} + + +/** + * Stop this service. + */ +void org_virtualbox_VBoxGuest::stop(IOService *pProvider) +{ +#ifdef LOG_ENABLED + RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::stop([%p], %p)\n", this, pProvider); /* Being cautious here, no Log(). */ +#endif + AssertReturnVoid(ASMAtomicReadBool(&g_fInstantiated)); + + /* Low level termination should be performed only once */ + if (!disableVmmDevInterrupts()) + printf("VBoxGuest: unable to unregister interrupt handler\n"); + + vgdrvDarwinCharDevRemove(); + VGDrvCommonDeleteDevExt(&g_DevExt); + + if (m_pMap) + { + m_pMap->release(); + m_pMap = NULL; + } + + IOService::stop(pProvider); + + ASMAtomicWriteBool(&g_fInstantiated, false); + + printf("VBoxGuest: IOService stopped\n"); + RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::stop: returning\n"); /* Being cautious here, no Log(). */ +} + + +/** + * Termination request. + * + * @return true if we're ok with shutting down now, false if we're not. + * @param fOptions Flags. + */ +bool org_virtualbox_VBoxGuest::terminate(IOOptionBits fOptions) +{ +#ifdef LOG_ENABLED + RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n", + KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions); /* Being cautious here, no Log(). */ +#endif + + bool fRc; + if ( KMOD_INFO_NAME.reference_count != 0 + || ASMAtomicUoReadS32(&g_cSessions)) + fRc = false; + else + fRc = IOService::terminate(fOptions); + +#ifdef LOG_ENABLED + RTLogBackdoorPrintf("org_virtualbox_SupDrv::terminate: returns %d\n", fRc); /* Being cautious here, no Log(). */ +#endif + return fRc; +} + + +/** + * Implementes a IOInterruptHandler, called by provider when an interrupt occurs. + */ +/*static*/ void org_virtualbox_VBoxGuest::vgdrvDarwinIrqHandler(OSObject *pTarget, void *pvRefCon, IOService *pNub, int iSrc) +{ +#ifdef LOG_ENABLED + RTLogBackdoorPrintf("vgdrvDarwinIrqHandler: %p %p %p %d\n", pTarget, pvRefCon, pNub, iSrc); +#endif + RT_NOREF(pTarget, pvRefCon, pNub, iSrc); + + VGDrvCommonISR(&g_DevExt); + /* There is in fact no way of indicating that this is our interrupt, other + than making the device lower it. So, the return code is ignored. */ +} + + +/** + * Sets up and enables interrupts on the device. + * + * Interrupts are handled directly, no messing around with workloops. The + * rational here is is that the main job of our interrupt handler is waking up + * other threads currently sitting in HGCM calls, i.e. little more effort than + * waking up the workloop thread. + * + * @returns success indicator. Failures are fully logged. + */ +bool org_virtualbox_VBoxGuest::setupVmmDevInterrupts(IOService *pProvider) +{ + AssertReturn(pProvider, false); + + if (m_pInterruptProvider != pProvider) + { + pProvider->retain(); + if (m_pInterruptProvider) + m_pInterruptProvider->release(); + m_pInterruptProvider = pProvider; + } + + IOReturn rc = pProvider->registerInterrupt(0 /*intIndex*/, this, vgdrvDarwinIrqHandler, this); + if (rc == kIOReturnSuccess) + { + rc = pProvider->enableInterrupt(0 /*intIndex*/); + if (rc == kIOReturnSuccess) + return true; + + LogRel(("VBoxGuest: Failed to enable interrupt: %#x\n", rc)); + m_pInterruptProvider->unregisterInterrupt(0 /*intIndex*/); + } + else + LogRel(("VBoxGuest: Failed to register interrupt: %#x\n", rc)); + return false; +} + + +/** + * Counterpart to setupVmmDevInterrupts(). + */ +bool org_virtualbox_VBoxGuest::disableVmmDevInterrupts(void) +{ + if (m_pInterruptProvider) + { + IOReturn rc = m_pInterruptProvider->disableInterrupt(0 /*intIndex*/); + AssertMsg(rc == kIOReturnSuccess, ("%#x\n", rc)); + rc = m_pInterruptProvider->unregisterInterrupt(0 /*intIndex*/); + AssertMsg(rc == kIOReturnSuccess, ("%#x\n", rc)); + RT_NOREF_PV(rc); + + m_pInterruptProvider->release(); + m_pInterruptProvider = NULL; + } + + return true; +} + + +/** + * Checks if it's the VMM device. + * + * @returns true if it is, false if it isn't. + * @param pIOPCIDevice The PCI device we think might be the VMM device. + */ +bool org_virtualbox_VBoxGuest::isVmmDev(IOPCIDevice *pIOPCIDevice) +{ + if (pIOPCIDevice) + { + uint16_t idVendor = m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID); + if (idVendor == VMMDEV_VENDORID) + { + uint16_t idDevice = m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID); + if (idDevice == VMMDEV_DEVICEID) + return true; + } + } + return false; +} + + + +/* + * + * org_virtualbox_VBoxGuestClient + * + */ + + +/** + * Initializer called when the client opens the service. + */ +bool org_virtualbox_VBoxGuestClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::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 (u32Type != VBOXGUEST_DARWIN_IOSERVICE_COOKIE) + { + VBOX_RETRIEVE_CUR_PROC_NAME(szProcName); + LogRelMax(10, ("org_virtualbox_VBoxGuestClient::initWithTask: Bad cookie %#x (%s)\n", u32Type, szProcName)); + return false; + } + + if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type)) + { + /* + * In theory we have to call task_reference() to make sure that the task is + * valid during the lifetime of this object. The pointer is only used to check + * for the context this object is called in though and never dereferenced + * or passed to anything which might, so we just skip this step. + */ + m_Task = OwningTask; + m_pSession = NULL; + m_pProvider = NULL; + return true; + } + return false; +} + + +/** + * Start the client service. + */ +bool org_virtualbox_VBoxGuestClient::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::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_VBoxGuest, pProvider); + if (m_pProvider) + { + Assert(!m_pSession); + + /* + * Create a new session. + * Note! We complete the requestor stuff in the open method. + */ + int rc = VGDrvCommonCreateUserSession(&g_DevExt, VMMDEV_REQUESTOR_USERMODE, &m_pSession); + if (RT_SUCCESS(rc)) + { + m_pSession->fOpened = false; + /* The Uid, Gid and fUnrestricted fields are set on open. */ + + /* + * Insert it into the hash table, checking that there isn't + * already one for this process first. (One session per proc!) + */ + unsigned iHash = SESSION_HASH(m_pSession->Process); + RTSpinlockAcquire(g_Spinlock); + + PVBOXGUESTSESSION pCur = g_apSessionHashTab[iHash]; + while (pCur && pCur->Process != m_pSession->Process) + pCur = pCur->pNextHash; + if (!pCur) + { + m_pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = m_pSession; + m_pSession->pvVBoxGuestClient = this; + ASMAtomicIncS32(&g_cSessions); + rc = VINF_SUCCESS; + } + else + rc = VERR_ALREADY_LOADED; + + RTSpinlockRelease(g_Spinlock); + if (RT_SUCCESS(rc)) + { + Log(("org_virtualbox_VBoxGuestClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf())); + return true; + } + + LogFlow(("org_virtualbox_VBoxGuestClient::start: already got a session for this process (%p)\n", pCur)); + VGDrvCommonCloseSession(&g_DevExt, m_pSession); //supdrvSessionRelease(m_pSession); + } + + m_pSession = NULL; + LogFlow(("org_virtualbox_VBoxGuestClient::start: rc=%Rrc from supdrvCreateSession\n", rc)); + } + else + LogFlow(("org_virtualbox_VBoxGuestClient::start: %p isn't org_virtualbox_VBoxGuest\n", pProvider)); + } + return false; +} + + +/** + * Common worker for clientClose and VBoxDrvDarwinClose. + */ +/* static */ void org_virtualbox_VBoxGuestClient::sessionClose(RTPROCESS Process) +{ + /* + * Find the session and remove it from the hash table. + * + * Note! Only one session per process. (Both start() and + * vgdrvDarwinOpen makes sure this is so.) + */ + const unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + PVBOXGUESTSESSION pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if (pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + } + else + { + PVBOXGUESTSESSION 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; + } + } + } + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("VBoxGuestClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process)); + return; + } + + /* + * Remove it from the client object. + */ + org_virtualbox_VBoxGuestClient *pThis = (org_virtualbox_VBoxGuestClient *)pSession->pvVBoxGuestClient; + pSession->pvVBoxGuestClient = NULL; + if (pThis) + { + Assert(pThis->m_pSession == pSession); + pThis->m_pSession = NULL; + } + + /* + * Close the session. + */ + VGDrvCommonCloseSession(&g_DevExt, pSession); // supdrvSessionRelease(m_pSession); +} + + +/** + * Client exits normally. + */ +IOReturn org_virtualbox_VBoxGuestClient::clientClose(void) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::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_VBoxGuestClient::clientDied(void) +{ + LogFlow(("IOService::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_VBoxGuestClient::terminate(IOOptionBits fOptions) +{ + LogFlow(("IOService::terminate([%p], %#x)\n", this, fOptions)); + return IOUserClient::terminate(fOptions); +} + + +/** + * The final stage of the client service destruction. (logging) + */ +bool org_virtualbox_VBoxGuestClient::finalize(IOOptionBits fOptions) +{ + LogFlow(("IOService::finalize([%p], %#x)\n", this, fOptions)); + return IOUserClient::finalize(fOptions); +} + + +/** + * Stop the client service. (logging) + */ +void org_virtualbox_VBoxGuestClient::stop(IOService *pProvider) +{ + LogFlow(("IOService::stop([%p])\n", this)); + IOUserClient::stop(pProvider); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c new file mode 100644 index 00000000..73d8f68a --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c @@ -0,0 +1,799 @@ +/* $Id: VBoxGuest-freebsd.c $ */ +/** @file + * VirtualBox Guest Additions Driver for FreeBSD. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/** @todo r=bird: This must merge with SUPDrv-freebsd.c before long. The two + * source files should only differ on prefixes and the extra bits wrt to the + * pci device. I.e. it should be diffable so that fixes to one can easily be + * applied to the other. */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#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 <sys/bus.h> +#include <sys/poll.h> +#include <sys/selinfo.h> +#include <sys/queue.h> +#include <sys/lock.h> +#include <sys/lockmgr.h> +#include <sys/malloc.h> +#include <sys/file.h> +#include <sys/rman.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include "VBoxGuestInternal.h" +#include <VBox/version.h> +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/asm.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The module name. */ +#define DEVICE_NAME "vboxguest" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +struct VBoxGuestDeviceState +{ + /** Resource ID of the I/O port */ + int iIOPortResId; + /** Pointer to the I/O port resource. */ + struct resource *pIOPortRes; + /** Start address of the IO Port. */ + uint16_t uIOPortBase; + /** Resource ID of the MMIO area */ + int iVMMDevMemResId; + /** Pointer to the MMIO resource. */ + struct resource *pVMMDevMemRes; + /** Handle of the MMIO resource. */ + bus_space_handle_t VMMDevMemHandle; + /** Size of the memory area. */ + bus_size_t VMMDevMemSize; + /** Mapping of the register space */ + void *pMMIOBase; + /** IRQ number */ + int iIrqResId; + /** IRQ resource handle. */ + struct resource *pIrqRes; + /** Pointer to the IRQ handler. */ + void *pfnIrqHandler; + /** VMMDev version */ + uint32_t u32Version; +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +/* + * Character device file handlers. + */ +static d_fdopen_t vgdrvFreeBSDOpen; +static d_close_t vgdrvFreeBSDClose; +static d_ioctl_t vgdrvFreeBSDIOCtl; +static int vgdrvFreeBSDIOCtlSlow(PVBOXGUESTSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd); +static d_write_t vgdrvFreeBSDWrite; +static d_read_t vgdrvFreeBSDRead; +static d_poll_t vgdrvFreeBSDPoll; + +/* + * IRQ related functions. + */ +static void vgdrvFreeBSDRemoveIRQ(device_t pDevice, void *pvState); +static int vgdrvFreeBSDAddIRQ(device_t pDevice, void *pvState); +static int vgdrvFreeBSDISR(void *pvState); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static MALLOC_DEFINE(M_VBOXGUEST, "vboxguest", "VirtualBox Guest Device Driver"); + +#ifndef D_NEEDMINOR +# define D_NEEDMINOR 0 +#endif + +/* + * The /dev/vboxguest character device entry points. + */ +static struct cdevsw g_vgdrvFreeBSDChrDevSW = +{ + .d_version = D_VERSION, + .d_flags = D_TRACKCLOSE | D_NEEDMINOR, + .d_fdopen = vgdrvFreeBSDOpen, + .d_close = vgdrvFreeBSDClose, + .d_ioctl = vgdrvFreeBSDIOCtl, + .d_read = vgdrvFreeBSDRead, + .d_write = vgdrvFreeBSDWrite, + .d_poll = vgdrvFreeBSDPoll, + .d_name = "vboxguest" +}; + +/** Device extention & session data association structure. */ +static VBOXGUESTDEVEXT g_DevExt; + +/** List of cloned device. Managed by the kernel. */ +static struct clonedevs *g_pvgdrvFreeBSDClones; +/** The dev_clone event handler tag. */ +static eventhandler_tag g_vgdrvFreeBSDEHTag; +/** Reference counter */ +static volatile uint32_t cUsers; +/** selinfo structure used for polling. */ +static struct selinfo g_SelInfo; + +/** + * DEVFS event handler. + */ +static void vgdrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev) +{ + int iUnit; + int rc; + + Log(("vgdrvFreeBSDClone: pszName=%s ppDev=%p\n", pszName, ppDev)); + + /* + * One device node per user, si_drv1 points to the session. + * /dev/vboxguest<N> where N = {0...255}. + */ + if (!ppDev) + return; + if (strcmp(pszName, "vboxguest") == 0) + iUnit = -1; + else if (dev_stdclone(pszName, NULL, "vboxguest", &iUnit) != 1) + return; + if (iUnit >= 256) + { + Log(("vgdrvFreeBSDClone: iUnit=%d >= 256 - rejected\n", iUnit)); + return; + } + + Log(("vgdrvFreeBSDClone: pszName=%s iUnit=%d\n", pszName, iUnit)); + + rc = clone_create(&g_pvgdrvFreeBSDClones, &g_vgdrvFreeBSDChrDevSW, &iUnit, ppDev, 0); + Log(("vgdrvFreeBSDClone: clone_create -> %d; iUnit=%d\n", rc, iUnit)); + if (rc) + { + *ppDev = make_dev(&g_vgdrvFreeBSDChrDevSW, + iUnit, + UID_ROOT, + GID_WHEEL, + 0664, + "vboxguest%d", iUnit); + if (*ppDev) + { + dev_ref(*ppDev); + (*ppDev)->si_flags |= SI_CHEAPCLONE; + Log(("vgdrvFreeBSDClone: 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 + Log(("vgdrvFreeBSDClone: make_dev iUnit=%d failed\n", iUnit)); + } + else + Log(("vgdrvFreeBSDClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); +} + +/** + * File open handler + * + */ +#if __FreeBSD_version >= 700000 +static int vgdrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, struct file *pFd) +#else +static int vgdrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd) +#endif +{ + int rc; + PVBOXGUESTSESSION pSession; + uint32_t fRequestor; + struct ucred *pCred = curthread->td_ucred; + if (!pCred) + pCred = curproc->p_ucred; + + LogFlow(("vgdrvFreeBSDOpen:\n")); + + /* + * Try grab it (we don't grab the giant, remember). + */ + if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, (void *)0x42, NULL)) + return EBUSY; + + /* + * Create a new session. + */ + fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + if (pCred && pCred->cr_uid == 0) + fRequestor |= VMMDEV_REQUESTOR_USR_ROOT; + else + fRequestor |= VMMDEV_REQUESTOR_USR_USER; + if (pCred && groupmember(0, pCred)) + fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL; + fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; /** @todo implement /dev/vboxuser + if (!fUnrestricted) + fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE; */ + fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW; /** @todo see if we can figure out console relationship of pProc. */ + rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession); + if (RT_SUCCESS(rc)) + { + if (ASMAtomicCmpXchgPtr(&pDev->si_drv1, pSession, (void *)0x42)) + { + Log(("vgdrvFreeBSDOpen: success - g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf())); + ASMAtomicIncU32(&cUsers); + return 0; + } + + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + + LogRel(("vgdrvFreeBSDOpen: failed. rc=%d\n", rc)); + return RTErrConvertToErrno(rc); +} + +/** + * File close handler + * + */ +static int vgdrvFreeBSDClose(struct cdev *pDev, int fFile, int DevType, struct thread *pTd) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pDev->si_drv1; + Log(("vgdrvFreeBSDClose: fFile=%#x pSession=%p\n", fFile, pSession)); + + /* + * Close the session if it's still hanging on to the device... + */ + if (RT_VALID_PTR(pSession)) + { + VGDrvCommonCloseSession(&g_DevExt, pSession); + if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, NULL, pSession)) + Log(("vgdrvFreeBSDClose: si_drv1=%p expected %p!\n", pDev->si_drv1, pSession)); + ASMAtomicDecU32(&cUsers); + /* Don't use destroy_dev here because it may sleep resulting in a hanging user process. */ + destroy_dev_sched(pDev); + } + else + Log(("vgdrvFreeBSDClose: 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 vgdrvFreeBSDIOCtl(struct cdev *pDev, u_long ulCmd, caddr_t pvData, int fFile, struct thread *pTd) +{ + PVBOXGUESTSESSION pSession; + devfs_get_cdevpriv((void **)&pSession); + + /* + * Deal with the fast ioctl path first. + */ + if (VBGL_IOCTL_IS_FAST(ulCmd)) + return VGDrvCommonIoCtlFast(ulCmd, &g_DevExt, pSession); + + return vgdrvFreeBSDIOCtlSlow(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 vgdrvFreeBSDIOCtlSlow(PVBOXGUESTSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd) +{ + PVBGLREQHDR pHdr; + uint32_t cbReq = IOCPARM_LEN(ulCmd); + void *pvUser = NULL; + + /* + * Buffered request? + */ + if ((IOC_DIRMASK & ulCmd) == IOC_INOUT) + { + pHdr = (PVBGLREQHDR)pvData; + if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd)); + return EINVAL; + } + if (RT_UNLIKELY(pHdr->uVersion != VBGLREQHDR_VERSION)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: bad uVersion=%#x; ulCmd=%#lx\n", pHdr->uVersion, ulCmd)); + return EINVAL; + } + if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq + || pHdr->cbIn < sizeof(*pHdr) + || (pHdr->cbOut < sizeof(*pHdr) && pHdr->cbOut != 0))) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: 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. + */ + VBGLREQHDR Hdr; + pvUser = *(void **)pvData; + int rc = copyin(pvUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd)); + return rc; + } + if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: bad uVersion=%#x; ulCmd=%#lx\n", Hdr.uVersion, ulCmd)); + return EINVAL; + } + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || (Hdr.cbOut < sizeof(Hdr) && Hdr.cbOut != 0) + || cbReq > _1M*16)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd)); + return EINVAL; + } + + /* + * Allocate buffer and copy in the data. + */ + pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq); + if (RT_UNLIKELY(!pHdr)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd)); + return ENOMEM; + } + rc = copyin(pvUser, pHdr, Hdr.cbIn); + if (RT_UNLIKELY(rc)) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n", + pvUser, pHdr, Hdr.cbIn, rc, ulCmd)); + RTMemTmpFree(pHdr); + return rc; + } + if (Hdr.cbIn < cbReq) + RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn); + } + else + { + Log(("vgdrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + int rc = VGDrvCommonIoCtl(ulCmd, &g_DevExt, pSession, pHdr, cbReq); + if (RT_LIKELY(!rc)) + { + /* + * If unbuffered, copy back the result before returning. + */ + if (pvUser) + { + uint32_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + LogRel(("vgdrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd)); + cbOut = cbReq; + } + rc = copyout(pHdr, pvUser, cbOut); + if (RT_UNLIKELY(rc)) + LogRel(("vgdrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd)); + + Log(("vgdrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd)); + + /* cleanup */ + RTMemTmpFree(pHdr); + } + } + else + { + /* + * The request failed, just clean up. + */ + if (pvUser) + RTMemTmpFree(pHdr); + + Log(("vgdrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc)); + rc = EINVAL; + } + + return rc; +} + + +/** + * @note This code is duplicated on other platforms with variations, so please + * keep them all up to date when making changes! + */ +int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + /* + * Simple request validation (common code does the rest). + */ + int rc; + if ( RT_VALID_PTR(pReqHdr) + && cbReq >= sizeof(*pReqHdr)) + { + /* + * All requests except the connect one requires a valid session. + */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; + if (pSession) + { + if ( RT_VALID_PTR(pSession) + && pSession->pDevExt == &g_DevExt) + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + else + rc = VERR_INVALID_HANDLE; + } + else if (uReq == VBGL_IOCTL_IDC_CONNECT) + { + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + if (RT_FAILURE(rc)) + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + } + else + rc = VERR_INVALID_HANDLE; + } + else + rc = VERR_INVALID_POINTER; + return rc; +} + + +static int vgdrvFreeBSDPoll(struct cdev *pDev, int fEvents, struct thread *td) +{ + int fEventsProcessed; + + LogFlow(("vgdrvFreeBSDPoll: fEvents=%d\n", fEvents)); + + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pDev->si_drv1; + if (RT_UNLIKELY(!RT_VALID_PTR(pSession))) { + Log(("vgdrvFreeBSDPoll: no state data for %s\n", devtoname(pDev))); + return (fEvents & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); + } + + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + fEventsProcessed = fEvents & (POLLIN | POLLRDNORM); + pSession->u32MousePosChangedSeq = u32CurSeq; + } + else + { + fEventsProcessed = 0; + + selrecord(td, &g_SelInfo); + } + + return fEventsProcessed; +} + +static int vgdrvFreeBSDWrite(struct cdev *pDev, struct uio *pUio, int fIo) +{ + return 0; +} + +static int vgdrvFreeBSDRead(struct cdev *pDev, struct uio *pUio, int fIo) +{ + return 0; +} + +static int vgdrvFreeBSDDetach(device_t pDevice) +{ + struct VBoxGuestDeviceState *pState = device_get_softc(pDevice); + + if (cUsers > 0) + return EBUSY; + + /* + * Reverse what we did in vgdrvFreeBSDAttach. + */ + if (g_vgdrvFreeBSDEHTag != NULL) + EVENTHANDLER_DEREGISTER(dev_clone, g_vgdrvFreeBSDEHTag); + + clone_cleanup(&g_pvgdrvFreeBSDClones); + + vgdrvFreeBSDRemoveIRQ(pDevice, pState); + + if (pState->pVMMDevMemRes) + bus_release_resource(pDevice, SYS_RES_MEMORY, pState->iVMMDevMemResId, pState->pVMMDevMemRes); + if (pState->pIOPortRes) + bus_release_resource(pDevice, SYS_RES_IOPORT, pState->iIOPortResId, pState->pIOPortRes); + + VGDrvCommonDeleteDevExt(&g_DevExt); + + RTR0Term(); + + return 0; +} + + +/** + * Interrupt service routine. + * + * @returns Whether the interrupt was from VMMDev. + * @param pvState Opaque pointer to the device state. + */ +static int vgdrvFreeBSDISR(void *pvState) +{ + LogFlow(("vgdrvFreeBSDISR: pvState=%p\n", pvState)); + + bool fOurIRQ = VGDrvCommonISR(&g_DevExt); + + return fOurIRQ ? 0 : 1; +} + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + LogFlow(("VGDrvNativeISRMousePollEvent:\n")); + + /* + * Wake up poll waiters. + */ + selwakeup(&g_SelInfo); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +/** + * Sets IRQ for VMMDev. + * + * @returns FreeBSD error code. + * @param pDevice Pointer to the device info structure. + * @param pvState Pointer to the state info structure. + */ +static int vgdrvFreeBSDAddIRQ(device_t pDevice, void *pvState) +{ + int iResId = 0; + int rc = 0; + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + + pState->pIrqRes = bus_alloc_resource_any(pDevice, SYS_RES_IRQ, &iResId, RF_SHAREABLE | RF_ACTIVE); + +#if __FreeBSD_version >= 700000 + rc = bus_setup_intr(pDevice, pState->pIrqRes, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)vgdrvFreeBSDISR, pState, + &pState->pfnIrqHandler); +#else + rc = bus_setup_intr(pDevice, pState->pIrqRes, INTR_TYPE_BIO, (driver_intr_t *)vgdrvFreeBSDISR, pState, &pState->pfnIrqHandler); +#endif + + if (rc) + { + pState->pfnIrqHandler = NULL; + return VERR_DEV_IO_ERROR; + } + + pState->iIrqResId = iResId; + + return VINF_SUCCESS; +} + +/** + * Removes IRQ for VMMDev. + * + * @param pDevice Pointer to the device info structure. + * @param pvState Opaque pointer to the state info structure. + */ +static void vgdrvFreeBSDRemoveIRQ(device_t pDevice, void *pvState) +{ + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + + if (pState->pIrqRes) + { + bus_teardown_intr(pDevice, pState->pIrqRes, pState->pfnIrqHandler); + bus_release_resource(pDevice, SYS_RES_IRQ, 0, pState->pIrqRes); + } +} + +static int vgdrvFreeBSDAttach(device_t pDevice) +{ + int rc; + int iResId; + struct VBoxGuestDeviceState *pState; + + cUsers = 0; + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + LogFunc(("RTR0Init failed.\n")); + return ENXIO; + } + + pState = device_get_softc(pDevice); + + /* + * Allocate I/O port resource. + */ + iResId = PCIR_BAR(0); + pState->pIOPortRes = bus_alloc_resource_any(pDevice, SYS_RES_IOPORT, &iResId, RF_ACTIVE); + pState->uIOPortBase = rman_get_start(pState->pIOPortRes); + pState->iIOPortResId = iResId; + if (pState->uIOPortBase) + { + /* + * Map the MMIO region. + */ + iResId = PCIR_BAR(1); + pState->pVMMDevMemRes = bus_alloc_resource_any(pDevice, SYS_RES_MEMORY, &iResId, RF_ACTIVE); + pState->VMMDevMemHandle = rman_get_bushandle(pState->pVMMDevMemRes); + pState->VMMDevMemSize = rman_get_size(pState->pVMMDevMemRes); + + pState->pMMIOBase = rman_get_virtual(pState->pVMMDevMemRes); + pState->iVMMDevMemResId = iResId; + if (pState->pMMIOBase) + { + /* + * Call the common device extension initializer. + */ + rc = VGDrvCommonInitDevExt(&g_DevExt, pState->uIOPortBase, + pState->pMMIOBase, pState->VMMDevMemSize, +#if ARCH_BITS == 64 + VBOXOSTYPE_FreeBSD_x64, +#else + VBOXOSTYPE_FreeBSD, +#endif + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Add IRQ of VMMDev. + */ + rc = vgdrvFreeBSDAddIRQ(pDevice, pState); + if (RT_SUCCESS(rc)) + { + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + /* + * Configure device cloning. + */ + clone_setup(&g_pvgdrvFreeBSDClones); + g_vgdrvFreeBSDEHTag = EVENTHANDLER_REGISTER(dev_clone, vgdrvFreeBSDClone, 0, 1000); + if (g_vgdrvFreeBSDEHTag) + { + printf(DEVICE_NAME ": loaded successfully\n"); + return 0; + } + + printf(DEVICE_NAME ": EVENTHANDLER_REGISTER(dev_clone,,,) failed\n"); + clone_cleanup(&g_pvgdrvFreeBSDClones); + vgdrvFreeBSDRemoveIRQ(pDevice, pState); + } + else + printf((DEVICE_NAME ": VGDrvCommonInitDevExt failed.\n")); + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + printf((DEVICE_NAME ": vgdrvFreeBSDAddIRQ failed.\n")); + } + else + printf((DEVICE_NAME ": MMIO region setup failed.\n")); + } + else + printf((DEVICE_NAME ": IOport setup failed.\n")); + + RTR0Term(); + return ENXIO; +} + +static int vgdrvFreeBSDProbe(device_t pDevice) +{ + if ((pci_get_vendor(pDevice) == VMMDEV_VENDORID) && (pci_get_device(pDevice) == VMMDEV_DEVICEID)) + return 0; + + return ENXIO; +} + +static device_method_t vgdrvFreeBSDMethods[] = +{ + /* Device interface. */ + DEVMETHOD(device_probe, vgdrvFreeBSDProbe), + DEVMETHOD(device_attach, vgdrvFreeBSDAttach), + DEVMETHOD(device_detach, vgdrvFreeBSDDetach), + {0,0} +}; + +static driver_t vgdrvFreeBSDDriver = +{ + DEVICE_NAME, + vgdrvFreeBSDMethods, + sizeof(struct VBoxGuestDeviceState), +}; + +static devclass_t vgdrvFreeBSDClass; + +DRIVER_MODULE(vboxguest, pci, vgdrvFreeBSDDriver, vgdrvFreeBSDClass, 0, 0); +MODULE_VERSION(vboxguest, 1); + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c new file mode 100644 index 00000000..e58de0b8 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c @@ -0,0 +1,471 @@ +/* $Id: VBoxGuest-haiku-stubs.c $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, stubs. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * 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. + */ + + +/* + * This file provides stubs for calling VBox runtime functions through the vboxguest module. + * It should be linked into any driver or module that uses the VBox runtime, except vboxguest + * itself (which contains the actual library and therefore doesn't need stubs to call it). + */ + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/power.h> +#include <iprt/thread.h> + +// >>> file('/tmp/stubs.c', 'w').writelines([re.sub(r'^(?P<returntype>[^(]+) \(\*_(?P<functionname>[A-Za-z0-9_]+)\)\((?P<params>[^)]+)\);', lambda m: '%s %s(%s)\n{\n %sg_VBoxGuest->_%s(%s);\n}\n' % (m.group(1), m.group(2), m.group(3), ('return ' if m.group(1) != 'void' else ''), m.group(2), (', '.join(a.split(' ')[-1].replace('*', '') for a in m.group(3).split(',')) if m.group(3) != 'void' else '')), f) for f in functions]) + +struct vboxguest_module_info *g_VBoxGuest; + +RTDECL(size_t) RTLogBackdoorPrintf(const char *pszFormat, ...) +{ + va_list args; + size_t cb; + + va_start(args, pszFormat); + cb = g_VBoxGuest->_RTLogBackdoorPrintf(pszFormat, args); + va_end(args); + + return cb; +} +RTDECL(size_t) RTLogBackdoorPrintfV(const char *pszFormat, va_list args) +{ + return g_VBoxGuest->_RTLogBackdoorPrintfV(pszFormat, args); +} +RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey) +{ + return g_VBoxGuest->_RTLogSetDefaultInstanceThread(pLogger, uKey); +} +RTDECL(int) RTMemAllocExTag(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv) +{ + return g_VBoxGuest->_RTMemAllocExTag(cb, cbAlignment, fFlags, pszTag, ppv); +} +RTR0DECL(void*) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + return g_VBoxGuest->_RTMemContAlloc(pPhys, cb); +} +RTR0DECL(void) RTMemContFree(void *pv, size_t cb) +{ + g_VBoxGuest->_RTMemContFree(pv, cb); +} +RTDECL(void) RTMemFreeEx(void *pv, size_t cb) +{ + g_VBoxGuest->_RTMemFreeEx(pv, cb); +} +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + return g_VBoxGuest->_RTMpIsCpuPossible(idCpu); +} +RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTMpNotificationDeregister(pfnCallback, pvUser); +} +RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTMpNotificationRegister(pfnCallback, pvUser); +} +RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnAll(pfnWorker, pvUser1, pvUser2); +} +RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnOthers(pfnWorker, pvUser1, pvUser2); +} +RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnSpecific(idCpu, pfnWorker, pvUser1, pvUser2); +} +RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTPowerNotificationDeregister(pfnCallback, pvUser); +} +RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTPowerNotificationRegister(pfnCallback, pvUser); +} +RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent) +{ + return g_VBoxGuest->_RTPowerSignalEvent(enmEvent); +} +RTR0DECL(void) RTR0AssertPanicSystem(void) +{ + g_VBoxGuest->_RTR0AssertPanicSystem(); +} +RTR0DECL(int) RTR0Init(unsigned fReserved) +{ + return g_VBoxGuest->_RTR0Init(fReserved); +} +RTR0DECL(void*) RTR0MemObjAddress(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjAddress(MemObj); +} +RTR0DECL(RTR3PTR) RTR0MemObjAddressR3(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjAddressR3(MemObj); +} +RTR0DECL(int) RTR0MemObjAllocContTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocContTag(pMemObj, cb, fExecutable, pszTag); +} +RTR0DECL(int) RTR0MemObjAllocLowTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocLowTag(pMemObj, cb, fExecutable, pszTag); +} +RTR0DECL(int) RTR0MemObjAllocPageTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPageTag(pMemObj, cb, fExecutable, pszTag); +} +RTR0DECL(int) RTR0MemObjAllocPhysExTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysExTag(pMemObj, cb, PhysHighest, uAlignment, pszTag); +} +RTR0DECL(int) RTR0MemObjAllocPhysNCTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysNCTag(pMemObj, cb, PhysHighest, pszTag); +} +RTR0DECL(int) RTR0MemObjAllocPhysTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysTag(pMemObj, cb, PhysHighest, pszTag); +} +RTR0DECL(int) RTR0MemObjEnterPhysTag(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjEnterPhysTag(pMemObj, Phys, cb, uCachePolicy, pszTag); +} +RTR0DECL(int) RTR0MemObjFree(RTR0MEMOBJ MemObj, bool fFreeMappings) +{ + return g_VBoxGuest->_RTR0MemObjFree(MemObj, fFreeMappings); +} +RTR0DECL(RTHCPHYS) RTR0MemObjGetPagePhysAddr(RTR0MEMOBJ MemObj, size_t iPage) +{ + return g_VBoxGuest->_RTR0MemObjGetPagePhysAddr(MemObj, iPage); +} +RTR0DECL(bool) RTR0MemObjIsMapping(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjIsMapping(MemObj); +} +RTR0DECL(int) RTR0MemObjLockKernelTag(PRTR0MEMOBJ pMemObj, void *pv, size_t cb, uint32_t fAccess, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjLockKernelTag(pMemObj, pv, cb, fAccess, pszTag); +} +RTR0DECL(int) RTR0MemObjLockUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjLockUserTag(pMemObj, R3Ptr, cb, fAccess, R0Process, pszTag); +} +RTR0DECL(int) RTR0MemObjMapKernelExTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapKernelExTag(pMemObj, MemObjToMap, pvFixed, uAlignment, fProt, offSub, cbSub, pszTag); +} +RTR0DECL(int) RTR0MemObjMapKernelTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapKernelTag(pMemObj, MemObjToMap, pvFixed, uAlignment, fProt, pszTag); +} +RTR0DECL(int) RTR0MemObjMapUserTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapUserTag(pMemObj, MemObjToMap, R3PtrFixed, uAlignment, fProt, R0Process, pszTag); +} +RTR0DECL(int) RTR0MemObjProtect(RTR0MEMOBJ hMemObj, size_t offSub, size_t cbSub, uint32_t fProt) +{ + return g_VBoxGuest->_RTR0MemObjProtect(hMemObj, offSub, cbSub, fProt); +} +RTR0DECL(int) RTR0MemObjReserveKernelTag(PRTR0MEMOBJ pMemObj, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjReserveKernelTag(pMemObj, pvFixed, cb, uAlignment, pszTag); +} +RTR0DECL(int) RTR0MemObjReserveUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjReserveUserTag(pMemObj, R3PtrFixed, cb, uAlignment, R0Process, pszTag); +} +RTR0DECL(size_t) RTR0MemObjSize(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjSize(MemObj); +} +RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void) +{ + return g_VBoxGuest->_RTR0ProcHandleSelf(); +} +RTR0DECL(void) RTR0Term(void) +{ + g_VBoxGuest->_RTR0Term(); +} +RTR0DECL(void) RTR0TermForced(void) +{ + g_VBoxGuest->_RTR0TermForced(); +} +RTDECL(RTPROCESS) RTProcSelf(void) +{ + return g_VBoxGuest->_RTProcSelf(); +} +RTDECL(uint32_t) RTSemEventGetResolution(void) +{ + return g_VBoxGuest->_RTSemEventGetResolution(); +} +RTDECL(uint32_t) RTSemEventMultiGetResolution(void) +{ + return g_VBoxGuest->_RTSemEventMultiGetResolution(); +} +RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout) +{ + return g_VBoxGuest->_RTSemEventMultiWaitEx(hEventMultiSem, fFlags, uTimeout); +} +RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return g_VBoxGuest->_RTSemEventMultiWaitExDebug(hEventMultiSem, fFlags, uTimeout, uId, pszFile, iLine, pszFunction); +} +RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) +{ + return g_VBoxGuest->_RTSemEventWaitEx(hEventSem, fFlags, uTimeout); +} +RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return g_VBoxGuest->_RTSemEventWaitExDebug(hEventSem, fFlags, uTimeout, uId, pszFile, iLine, pszFunction); +} +RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadIsInInterrupt(hThread); +} +RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState) +{ + g_VBoxGuest->_RTThreadPreemptDisable(pState); +} +RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadPreemptIsEnabled(hThread); +} +RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadPreemptIsPending(hThread); +} +RTDECL(bool) RTThreadPreemptIsPendingTrusty(void) +{ + return g_VBoxGuest->_RTThreadPreemptIsPendingTrusty(); +} +RTDECL(bool) RTThreadPreemptIsPossible(void) +{ + return g_VBoxGuest->_RTThreadPreemptIsPossible(); +} +RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState) +{ + g_VBoxGuest->_RTThreadPreemptRestore(pState); +} +RTDECL(uint32_t) RTTimerGetSystemGranularity(void) +{ + return g_VBoxGuest->_RTTimerGetSystemGranularity(); +} +RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted) +{ + return g_VBoxGuest->_RTTimerReleaseSystemGranularity(u32Granted); +} +RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted) +{ + return g_VBoxGuest->_RTTimerRequestSystemGranularity(u32Request, pu32Granted); +} +RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock) +{ + g_VBoxGuest->_RTSpinlockAcquire(Spinlock); +} +RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock) +{ + g_VBoxGuest->_RTSpinlockRelease(Spinlock); +} +RTDECL(void*) RTMemTmpAllocTag(size_t cb, const char *pszTag) +{ + return g_VBoxGuest->_RTMemTmpAllocTag(cb, pszTag); +} +RTDECL(void) RTMemTmpFree(void *pv) +{ + g_VBoxGuest->_RTMemTmpFree(pv); +} +RTDECL(PRTLOGGER) RTLogDefaultInstance(void) +{ + return g_VBoxGuest->_RTLogDefaultInstance(); +} +RTDECL(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup) +{ + return g_VBoxGuest->_RTLogDefaultInstanceEx(fFlagsAndGroup); +} +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstance(void) +{ + return g_VBoxGuest->_RTLogRelGetDefaultInstance(); +} +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstance(uint32_t fFlags, uint32_t iGroup) +{ + return g_VBoxGuest->_RTLogRelGetDefaultInstanceEx(fFlags, iGroup); +} +RTDECL(int) RTErrConvertToErrno(int iErr) +{ + return g_VBoxGuest->_RTErrConvertToErrno(iErr); +} +int VGDrvCommonIoCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, void *pvData, size_t cbData, size_t *pcbDataReturned) +{ + return g_VBoxGuest->_VGDrvCommonIoCtl(iFunction, pDevExt, pSession, pvData, cbData, pcbDataReturned); +} +int VGDrvCommonCreateUserSession(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession) +{ + return g_VBoxGuest->_VGDrvCommonCreateUserSession(pDevExt, fRequestor, ppSession); +} +void VGDrvCommonCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + g_VBoxGuest->_VGDrvCommonCloseSession(pDevExt, pSession); +} +void* VBoxGuestIDCOpen(uint32_t *pu32Version) +{ + return g_VBoxGuest->_VBoxGuestIDCOpen(pu32Version); +} +int VBoxGuestIDCClose(void *pvSession) +{ + return g_VBoxGuest->_VBoxGuestIDCClose(pvSession); +} +int VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned) +{ + return g_VBoxGuest->_VBoxGuestIDCCall(pvSession, iCmd, pvData, cbData, pcbDataReturned); +} +RTDECL(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + g_VBoxGuest->_RTAssertMsg1Weak(pszExpr, uLine, pszFile, pszFunction); +} +RTDECL(void) RTAssertMsg2Weak(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + RTAssertMsg2WeakV(pszFormat, va); + va_end(va); +} +RTDECL(void) RTAssertMsg2WeakV(const char *pszFormat, va_list va) +{ + g_VBoxGuest->_RTAssertMsg2WeakV(pszFormat, va); +} +RTDECL(bool) RTAssertShouldPanic(void) +{ + return g_VBoxGuest->_RTAssertShouldPanic(); +} +RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexCreate(phFastMtx); +} +RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexDestroy(hFastMtx); +} +RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexRelease(hFastMtx); +} +RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexRequest(hFastMtx); +} +RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phFastMtx) +{ + return g_VBoxGuest->_RTSemMutexCreate(phFastMtx); +} +RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemMutexDestroy(hFastMtx); +} +RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemMutexRelease(hFastMtx); +} +RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hFastMtx, RTMSINTERVAL cMillies) +{ + return g_VBoxGuest->_RTSemMutexRequest(hFastMtx, cMillies); +} +int RTHeapSimpleRelocate(RTHEAPSIMPLE hHeap, uintptr_t offDelta) +{ + return g_VBoxGuest->_RTHeapSimpleRelocate(hHeap, offDelta); +} +int RTHeapOffsetInit(PRTHEAPOFFSET phHeap, void *pvMemory, size_t cbMemory) +{ + return g_VBoxGuest->_RTHeapOffsetInit(phHeap, pvMemory, cbMemory); +} +int RTHeapSimpleInit(PRTHEAPSIMPLE pHeap, void *pvMemory, size_t cbMemory) +{ + return g_VBoxGuest->_RTHeapSimpleInit(pHeap, pvMemory, cbMemory); +} +void* RTHeapOffsetAlloc(RTHEAPOFFSET hHeap, size_t cb, size_t cbAlignment) +{ + return g_VBoxGuest->_RTHeapOffsetAlloc(hHeap, cb, cbAlignment); +} +void* RTHeapSimpleAlloc(RTHEAPSIMPLE Heap, size_t cb, size_t cbAlignment) +{ + return g_VBoxGuest->_RTHeapSimpleAlloc(Heap, cb, cbAlignment); +} +void RTHeapOffsetFree(RTHEAPOFFSET hHeap, void *pv) +{ + g_VBoxGuest->_RTHeapOffsetFree(hHeap, pv); +} +void RTHeapSimpleFree(RTHEAPSIMPLE Heap, void *pv) +{ + g_VBoxGuest->_RTHeapSimpleFree(Heap, pv); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c new file mode 100644 index 00000000..7a183b8e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c @@ -0,0 +1,588 @@ +/* $Id: VBoxGuest-haiku.c $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, implementation. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * 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 IN_VBOXGUEST +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <OS.h> +#include <Drivers.h> +#include <KernelExport.h> +#include <PCI.h> + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/asm.h> +#include <iprt/timer.h> +#include <iprt/heap.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define MODULE_NAME VBOXGUEST_MODULE_NAME + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +/* + * IRQ related functions. + */ +static void vgdrvHaikuRemoveIRQ(void *pvState); +static int vgdrvHaikuAddIRQ(void *pvState); +static int32 vgdrvHaikuISR(void *pvState); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static status_t std_ops(int32 op, ...); + +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +/** List of cloned device. Managed by the kernel. */ +//static struct clonedevs *g_pvgdrvHaikuClones; +/** The dev_clone event handler tag. */ +//static eventhandler_tag g_vgdrvHaikuEHTag; +/** selinfo structure used for polling. */ +//static struct selinfo g_SelInfo; +/** PCI Bus Manager Module */ +static pci_module_info *gPCI; + +static struct vboxguest_module_info g_VBoxGuest = +{ + { + MODULE_NAME, + 0, + std_ops + }, + { 0 }, + { 0 }, + 0, + RTLogBackdoorPrintf, + RTLogBackdoorPrintfV, + RTLogSetDefaultInstanceThread, + RTMemAllocExTag, + RTMemContAlloc, + RTMemContFree, + RTMemFreeEx, + RTMpIsCpuPossible, + RTMpNotificationDeregister, + RTMpNotificationRegister, + RTMpOnAll, + RTMpOnOthers, + RTMpOnSpecific, + RTPowerNotificationDeregister, + RTPowerNotificationRegister, + RTPowerSignalEvent, + RTR0AssertPanicSystem, + RTR0Init, + RTR0MemObjAddress, + RTR0MemObjAddressR3, + RTR0MemObjAllocContTag, + RTR0MemObjAllocLowTag, + RTR0MemObjAllocPageTag, + RTR0MemObjAllocPhysExTag, + RTR0MemObjAllocPhysNCTag, + RTR0MemObjAllocPhysTag, + RTR0MemObjEnterPhysTag, + RTR0MemObjFree, + RTR0MemObjGetPagePhysAddr, + RTR0MemObjIsMapping, + RTR0MemObjLockKernelTag, + RTR0MemObjLockUserTag, + RTR0MemObjMapKernelExTag, + RTR0MemObjMapKernelTag, + RTR0MemObjMapUserTag, + RTR0MemObjProtect, + RTR0MemObjReserveKernelTag, + RTR0MemObjReserveUserTag, + RTR0MemObjSize, + RTR0ProcHandleSelf, + RTR0Term, + RTR0TermForced, + RTProcSelf, + RTSemEventGetResolution, + RTSemEventMultiGetResolution, + RTSemEventMultiWaitEx, + RTSemEventMultiWaitExDebug, + RTSemEventWaitEx, + RTSemEventWaitExDebug, + RTThreadIsInInterrupt, + RTThreadPreemptDisable, + RTThreadPreemptIsEnabled, + RTThreadPreemptIsPending, + RTThreadPreemptIsPendingTrusty, + RTThreadPreemptIsPossible, + RTThreadPreemptRestore, + RTTimerGetSystemGranularity, + RTTimerReleaseSystemGranularity, + RTTimerRequestSystemGranularity, + RTSpinlockAcquire, + RTSpinlockRelease, + RTMemTmpAllocTag, + RTMemTmpFree, + RTLogDefaultInstance, + RTLogDefaultInstanceEx, + RTLogRelGetDefaultInstance, + RTLogRelGetDefaultInstanceEx, + RTErrConvertToErrno, + VGDrvCommonIoCtl, + VGDrvCommonCreateUserSession, + VGDrvCommonCloseSession, + VBoxGuestIDCOpen, + VBoxGuestIDCClose, + VBoxGuestIDCCall, + RTAssertMsg1Weak, + RTAssertMsg2Weak, + RTAssertMsg2WeakV, + RTAssertShouldPanic, + RTSemFastMutexCreate, + RTSemFastMutexDestroy, + RTSemFastMutexRelease, + RTSemFastMutexRequest, + RTSemMutexCreate, + RTSemMutexDestroy, + RTSemMutexRelease, + RTSemMutexRequest, + RTHeapSimpleRelocate, + RTHeapOffsetInit, + RTHeapSimpleInit, + RTHeapOffsetAlloc, + RTHeapSimpleAlloc, + RTHeapOffsetFree, + RTHeapSimpleFree +}; + +#if 0 +/** + * DEVFS event handler. + */ +static void vgdrvHaikuClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev) +{ + int iUnit; + int rc; + + Log(("vgdrvHaikuClone: pszName=%s ppDev=%p\n", pszName, ppDev)); + + /* + * One device node per user, si_drv1 points to the session. + * /dev/vboxguest<N> where N = {0...255}. + */ + if (!ppDev) + return; + if (strcmp(pszName, "vboxguest") == 0) + iUnit = -1; + else if (dev_stdclone(pszName, NULL, "vboxguest", &iUnit) != 1) + return; + if (iUnit >= 256) + { + Log(("vgdrvHaikuClone: iUnit=%d >= 256 - rejected\n", iUnit)); + return; + } + + Log(("vgdrvHaikuClone: pszName=%s iUnit=%d\n", pszName, iUnit)); + + rc = clone_create(&g_pvgdrvHaikuClones, &g_vgdrvHaikuDeviceHooks, &iUnit, ppDev, 0); + Log(("vgdrvHaikuClone: clone_create -> %d; iUnit=%d\n", rc, iUnit)); + if (rc) + { + *ppDev = make_dev(&g_vgdrvHaikuDeviceHooks, + iUnit, + UID_ROOT, + GID_WHEEL, + 0644, + "vboxguest%d", iUnit); + if (*ppDev) + { + dev_ref(*ppDev); + (*ppDev)->si_flags |= SI_CHEAPCLONE; + Log(("vgdrvHaikuClone: 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 + Log(("vgdrvHaikuClone: make_dev iUnit=%d failed\n", iUnit)); + } + else + Log(("vgdrvHaikuClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); +} +#endif + + +static status_t vgdrvHaikuDetach(void) +{ + struct VBoxGuestDeviceState *pState = &sState; + + if (cUsers > 0) + return EBUSY; + + /* + * Reverse what we did in vgdrvHaikuAttach. + */ + vgdrvHaikuRemoveIRQ(pState); + + if (pState->iVMMDevMemAreaId) + delete_area(pState->iVMMDevMemAreaId); + + VGDrvCommonDeleteDevExt(&g_DevExt); + +#ifdef DO_LOG + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogSetDefaultInstance(NULL); +// RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif + + RTSpinlockDestroy(g_Spinlock); + g_Spinlock = NIL_RTSPINLOCK; + + RTR0Term(); + return B_OK; +} + + +/** + * Interrupt service routine. + * + * @returns Whether the interrupt was from VMMDev. + * @param pvState Opaque pointer to the device state. + */ +static int32 vgdrvHaikuISR(void *pvState) +{ + LogFlow((MODULE_NAME ":vgdrvHaikuISR pvState=%p\n", pvState)); + + bool fOurIRQ = VGDrvCommonISR(&g_DevExt); + if (fOurIRQ) + return B_HANDLED_INTERRUPT; + return B_UNHANDLED_INTERRUPT; +} + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + LogFlow(("VGDrvNativeISRMousePollEvent:\n")); + + status_t err = B_OK; + //dprintf(MODULE_NAME ": isr mouse\n"); + + /* + * Wake up poll waiters. + */ + //selwakeup(&g_SelInfo); + //XXX:notify_select_event(); + RTSpinlockAcquire(g_Spinlock); + + if (sState.selectSync) + { + //dprintf(MODULE_NAME ": isr mouse: notify\n"); + notify_select_event(sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = NULL; + } + else + err = B_ERROR; + + RTSpinlockRelease(g_Spinlock); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +/** + * Sets IRQ for VMMDev. + * + * @returns Haiku error code. + * @param pvState Pointer to the state info structure. + */ +static int vgdrvHaikuAddIRQ(void *pvState) +{ + status_t err; + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + + AssertReturn(pState, VERR_INVALID_PARAMETER); + + err = install_io_interrupt_handler(pState->iIrqResId, vgdrvHaikuISR, pState, 0); + if (err == B_OK) + return VINF_SUCCESS; + return VERR_DEV_IO_ERROR; +} + + +/** + * Removes IRQ for VMMDev. + * + * @param pvState Opaque pointer to the state info structure. + */ +static void vgdrvHaikuRemoveIRQ(void *pvState) +{ + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + AssertPtr(pState); + + remove_io_interrupt_handler(pState->iIrqResId, vgdrvHaikuISR, pState); +} + + +static status_t vgdrvHaikuAttach(const pci_info *pDevice) +{ + status_t status; + int rc; + int iResId; + struct VBoxGuestDeviceState *pState = &sState; + static const char *const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + + AssertReturn(pDevice, B_BAD_VALUE); + + cUsers = 0; + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + dprintf(MODULE_NAME ": RTR0Init failed: %d\n", rc); + return ENXIO; + } + + rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "vgdrvHaiku"); + if (RT_FAILURE(rc)) + { + LogRel(("vgdrvHaikuAttach: RTSpinlock create failed. rc=%Rrc\n", rc)); + return ENXIO; + } + +#ifdef DO_LOG + /* + * Create the release log. + * (We do that here instead of common code because we want to log + * early failures using the LogRel macro.) + */ + rc = RTLogCreate(&pRelLogger, 0 | RTLOGFLAGS_PREFIX_THREAD /* fFlags */, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, + RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL); + dprintf(MODULE_NAME ": RTLogCreate: %d\n", rc); + if (RT_SUCCESS(rc)) + { + //RTLogGroupSettings(pRelLogger, g_szLogGrp); + //RTLogFlags(pRelLogger, g_szLogFlags); + //RTLogDestinations(pRelLogger, "/var/log/vboxguest.log"); + RTLogRelSetDefaultInstance(pRelLogger); + RTLogSetDefaultInstance(pRelLogger); //XXX + } +#endif + + /* + * Allocate I/O port resource. + */ + pState->uIOPortBase = pDevice->u.h0.base_registers[0]; + /** @todo check flags for IO? */ + if (pState->uIOPortBase) + { + /* + * Map the MMIO region. + */ + uint32 phys = pDevice->u.h0.base_registers[1]; + /** @todo Check flags for mem? */ + pState->VMMDevMemSize = pDevice->u.h0.base_register_sizes[1]; + pState->iVMMDevMemAreaId = map_physical_memory("VirtualBox Guest MMIO", phys, pState->VMMDevMemSize, + B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, + &pState->pMMIOBase); + if (pState->iVMMDevMemAreaId > 0 && pState->pMMIOBase) + { + /* + * Call the common device extension initializer. + */ + rc = VGDrvCommonInitDevExt(&g_DevExt, pState->uIOPortBase, pState->pMMIOBase, pState->VMMDevMemSize, +#if ARCH_BITS == 64 + VBOXOSTYPE_Haiku_x64, +#else + VBOXOSTYPE_Haiku, +#endif + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Add IRQ of VMMDev. + */ + pState->iIrqResId = pDevice->u.h0.interrupt_line; + rc = vgdrvHaikuAddIRQ(pState); + if (RT_SUCCESS(rc)) + { + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + LogRel((MODULE_NAME ": loaded successfully\n")); + return B_OK; + } + + LogRel((MODULE_NAME ": VGDrvCommonInitDevExt failed.\n")); + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + LogRel((MODULE_NAME ": vgdrvHaikuAddIRQ failed.\n")); + } + else + LogRel((MODULE_NAME ": MMIO region setup failed.\n")); + } + else + LogRel((MODULE_NAME ": IOport setup failed.\n")); + + RTR0Term(); + return ENXIO; +} + + +static status_t vgdrvHaikuProbe(pci_info *pDevice) +{ + if ( pDevice->vendor_id == VMMDEV_VENDORID + && pDevice->device_id == VMMDEV_DEVICEID) + return B_OK; + + return ENXIO; +} + + +status_t init_module(void) +{ + status_t err = B_ENTRY_NOT_FOUND; + pci_info info; + int ix = 0; + + err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI); + if (err != B_OK) + return err; + + while ((*gPCI->get_nth_pci_info)(ix++, &info) == B_OK) + { + if (vgdrvHaikuProbe(&info) == 0) + { + /* We found it */ + err = vgdrvHaikuAttach(&info); + return err; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +void uninit_module(void) +{ + vgdrvHaikuDetach(); + put_module(B_PCI_MODULE_NAME); +} + + +static status_t std_ops(int32 op, ...) +{ + switch (op) + { + case B_MODULE_INIT: + return init_module(); + + case B_MODULE_UNINIT: + { + uninit_module(); + return B_OK; + } + + default: + return B_ERROR; + } +} + + +_EXPORT module_info *modules[] = +{ + (module_info *)&g_VBoxGuest, + NULL +}; + +/* Common code that depend on g_DevExt. */ +#include "VBoxGuestIDC-unix.c.h" + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h new file mode 100644 index 00000000..87df1fcb --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h @@ -0,0 +1,248 @@ +/* $Id: VBoxGuest-haiku.h $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, header. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * 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. + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuest_haiku_h +#define GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuest_haiku_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <OS.h> +#include <Drivers.h> +#include <drivers/module.h> + +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/power.h> +#include <iprt/thread.h> + +/** The module name. */ +#define VBOXGUEST_MODULE_NAME "generic/vboxguest" + +struct VBoxGuestDeviceState +{ + /** Resource ID of the I/O port */ + int iIOPortResId; + /** Pointer to the I/O port resource. */ +// struct resource *pIOPortRes; + /** Start address of the IO Port. */ + uint16_t uIOPortBase; + /** Resource ID of the MMIO area */ + area_id iVMMDevMemAreaId; + /** Pointer to the MMIO resource. */ +// struct resource *pVMMDevMemRes; + /** Handle of the MMIO resource. */ +// bus_space_handle_t VMMDevMemHandle; + /** Size of the memory area. */ + size_t VMMDevMemSize; + /** Mapping of the register space */ + void *pMMIOBase; + /** IRQ number */ + int iIrqResId; + /** IRQ resource handle. */ +// struct resource *pIrqRes; + /** Pointer to the IRQ handler. */ +// void *pfnIrqHandler; + /** VMMDev version */ + uint32_t u32Version; + + /** The (only) select data we wait on. */ + //XXX: should leave in pSession ? + uint8_t selectEvent; + uint32_t selectRef; + void *selectSync; +}; + +struct vboxguest_module_info +{ + module_info module; + + VBOXGUESTDEVEXT devExt; + struct VBoxGuestDeviceState _sState; + volatile uint32_t _cUsers; + + size_t(*_RTLogBackdoorPrintf)(const char *pszFormat, ...); + size_t(*_RTLogBackdoorPrintfV)(const char *pszFormat, va_list args); + int (*_RTLogSetDefaultInstanceThread)(PRTLOGGER pLogger, uintptr_t uKey); + int (*_RTMemAllocExTag)(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv); + void* (*_RTMemContAlloc)(PRTCCPHYS pPhys, size_t cb); + void (*_RTMemContFree)(void *pv, size_t cb); + void (*_RTMemFreeEx)(void *pv, size_t cb); + bool (*_RTMpIsCpuPossible)(RTCPUID idCpu); + int (*_RTMpNotificationDeregister)(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + int (*_RTMpNotificationRegister)(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + int (*_RTMpOnAll)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTMpOnOthers)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTMpOnSpecific)(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTPowerNotificationDeregister)(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser); + int (*_RTPowerNotificationRegister)(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser); + int (*_RTPowerSignalEvent)(RTPOWEREVENT enmEvent); + void (*_RTR0AssertPanicSystem)(void); + int (*_RTR0Init)(unsigned fReserved); + void* (*_RTR0MemObjAddress)(RTR0MEMOBJ MemObj); + RTR3PTR(*_RTR0MemObjAddressR3)(RTR0MEMOBJ MemObj); + int (*_RTR0MemObjAllocContTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocLowTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocPageTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocPhysExTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag); + int (*_RTR0MemObjAllocPhysNCTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag); + int (*_RTR0MemObjAllocPhysTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag); + int (*_RTR0MemObjEnterPhysTag)(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag); + int (*_RTR0MemObjFree)(RTR0MEMOBJ MemObj, bool fFreeMappings); + RTHCPHYS(*_RTR0MemObjGetPagePhysAddr)(RTR0MEMOBJ MemObj, size_t iPage); + bool (*_RTR0MemObjIsMapping)(RTR0MEMOBJ MemObj); + int (*_RTR0MemObjLockKernelTag)(PRTR0MEMOBJ pMemObj, void *pv, size_t cb, uint32_t fAccess, const char *pszTag); + int (*_RTR0MemObjLockUserTag)(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, + RTR0PROCESS R0Process, const char *pszTag); + int (*_RTR0MemObjMapKernelExTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, + unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag); + int (*_RTR0MemObjMapKernelTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, + size_t uAlignment, unsigned fProt, const char *pszTag); + int (*_RTR0MemObjMapUserTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, + size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, const char *pszTag); + int (*_RTR0MemObjProtect)(RTR0MEMOBJ hMemObj, size_t offSub, size_t cbSub, uint32_t fProt); + int (*_RTR0MemObjReserveKernelTag)(PRTR0MEMOBJ pMemObj, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag); + int (*_RTR0MemObjReserveUserTag)(PRTR0MEMOBJ pMemObj, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, + RTR0PROCESS R0Process, const char *pszTag); + size_t(*_RTR0MemObjSize)(RTR0MEMOBJ MemObj); + RTR0PROCESS(*_RTR0ProcHandleSelf)(void); + void (*_RTR0Term)(void); + void (*_RTR0TermForced)(void); + RTPROCESS(*_RTProcSelf)(void); + uint32_t(*_RTSemEventGetResolution)(void); + uint32_t(*_RTSemEventMultiGetResolution)(void); + int (*_RTSemEventMultiWaitEx)(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout); + int (*_RTSemEventMultiWaitExDebug)(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL); + int (*_RTSemEventWaitEx)(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout); + int (*_RTSemEventWaitExDebug)(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL); + bool (*_RTThreadIsInInterrupt)(RTTHREAD hThread); + void (*_RTThreadPreemptDisable)(PRTTHREADPREEMPTSTATE pState); + bool (*_RTThreadPreemptIsEnabled)(RTTHREAD hThread); + bool (*_RTThreadPreemptIsPending)(RTTHREAD hThread); + bool (*_RTThreadPreemptIsPendingTrusty)(void); + bool (*_RTThreadPreemptIsPossible)(void); + void (*_RTThreadPreemptRestore)(PRTTHREADPREEMPTSTATE pState); + uint32_t(*_RTTimerGetSystemGranularity)(void); + int (*_RTTimerReleaseSystemGranularity)(uint32_t u32Granted); + int (*_RTTimerRequestSystemGranularity)(uint32_t u32Request, uint32_t *pu32Granted); + void (*_RTSpinlockAcquire)(RTSPINLOCK Spinlock); + void (*_RTSpinlockRelease)(RTSPINLOCK Spinlock); + void* (*_RTMemTmpAllocTag)(size_t cb, const char *pszTag); + void (*_RTMemTmpFree)(void *pv); + PRTLOGGER(*_RTLogDefaultInstance)(void); + PRTLOGGER(*_RTLogDefaultInstanceEx)(uint32_t fFlagsAndGroup); + PRTLOGGER(*_RTLogRelGetDefaultInstance)(void); + PRTLOGGER(*_RTLogRelGetDefaultInstanceEx)(uint32_t fFlagsAndGroup); + int (*_RTErrConvertToErrno)(int iErr); + int (*_VGDrvCommonIoCtl)(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + void *pvData, size_t cbData, size_t *pcbDataReturned); + int (*_VGDrvCommonCreateUserSession)(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession); + void (*_VGDrvCommonCloseSession)(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); + void* (*_VBoxGuestIDCOpen)(uint32_t *pu32Version); + int (*_VBoxGuestIDCClose)(void *pvSession); + int (*_VBoxGuestIDCCall)(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); + void (*_RTAssertMsg1Weak)(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction); + void (*_RTAssertMsg2Weak)(const char *pszFormat, ...); + void (*_RTAssertMsg2WeakV)(const char *pszFormat, va_list va); + bool (*_RTAssertShouldPanic)(void); + int (*_RTSemFastMutexCreate)(PRTSEMFASTMUTEX phFastMtx); + int (*_RTSemFastMutexDestroy)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemFastMutexRelease)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemFastMutexRequest)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemMutexCreate)(PRTSEMMUTEX phFastMtx); + int (*_RTSemMutexDestroy)(RTSEMMUTEX hFastMtx); + int (*_RTSemMutexRelease)(RTSEMMUTEX hFastMtx); + int (*_RTSemMutexRequest)(RTSEMMUTEX hFastMtx, RTMSINTERVAL cMillies); + int (*_RTHeapSimpleRelocate)(RTHEAPSIMPLE hHeap, uintptr_t offDelta); + int (*_RTHeapOffsetInit)(PRTHEAPOFFSET phHeap, void *pvMemory, size_t cbMemory); + int (*_RTHeapSimpleInit)(PRTHEAPSIMPLE pHeap, void *pvMemory, size_t cbMemory); + void* (*_RTHeapOffsetAlloc)(RTHEAPOFFSET hHeap, size_t cb, size_t cbAlignment); + void* (*_RTHeapSimpleAlloc)(RTHEAPSIMPLE Heap, size_t cb, size_t cbAlignment); + void (*_RTHeapOffsetFree)(RTHEAPOFFSET hHeap, void *pv); + void (*_RTHeapSimpleFree)(RTHEAPSIMPLE Heap, void *pv); +}; + + +#ifdef IN_VBOXGUEST +#define g_DevExt (g_VBoxGuest.devExt) +#define cUsers (g_VBoxGuest._cUsers) +#define sState (g_VBoxGuest._sState) +#else +#define g_DevExt (g_VBoxGuest->devExt) +#define cUsers (g_VBoxGuest->_cUsers) +#define sState (g_VBoxGuest->_sState) +extern struct vboxguest_module_info *g_VBoxGuest; +#endif + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuest_haiku_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c new file mode 100644 index 00000000..77d5e4b6 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c @@ -0,0 +1,1470 @@ +/* $Id: VBoxGuest-linux.c $ */ +/** @file + * VBoxGuest - Linux specifics. + * + * Note. Unfortunately, the difference between this and SUPDrv-linux.c is + * a little bit too big to be helpful. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV + +#include "the-linux-kernel.h" + +#if RTLNX_VER_MIN(2,6,15) +# define VBOXGUEST_WITH_INPUT_DRIVER +#endif + +#if RTLNX_VER_MIN(4,15,0) +# define CONST_4_15 const +#else +# define CONST_4_15 +#endif + +#include "VBoxGuestInternal.h" +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +# include <linux/input.h> +#endif +#include <linux/miscdevice.h> +#include <linux/poll.h> +#if RTLNX_VER_MIN(2,6,28) +# include <linux/tty.h> +#endif +#include <VBox/version.h> +#include "revision-generated.h" + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/ctype.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/mp.h> +#include <iprt/process.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <VBox/err.h> +#include <VBox/log.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The device name. */ +#define DEVICE_NAME "vboxguest" +/** The device name for the device node open to everyone. */ +#define DEVICE_NAME_USER "vboxuser" +/** The name of the PCI driver */ +#define DRIVER_NAME DEVICE_NAME + + +/* 2.4.x compatibility macros that may or may not be defined. */ +#ifndef IRQ_RETVAL +# define irqreturn_t void +# define IRQ_RETVAL(n) +#endif + +/* uidgid.h was introduced in 3.5.0. */ +#if RTLNX_VER_MAX(3,5,0) +# define kgid_t gid_t +# define kuid_t uid_t +#endif + +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +/** The name of input pointing device for mouse integration. */ +# define VBOX_INPUT_DEVICE_NAME "VirtualBox mouse integration" +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void vgdrvLinuxTermPci(struct pci_dev *pPciDev); +static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id); +static int __init vgdrvLinuxModInit(void); +static void __exit vgdrvLinuxModExit(void); +static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp); +static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp); +#ifdef HAVE_UNLOCKED_IOCTL +static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg); +#else +static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg); +#endif +static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession); +static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn); +static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt); +static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Device extention & session data association structure. + */ +static VBOXGUESTDEVEXT g_DevExt; +/** The PCI device. */ +static struct pci_dev *g_pPciDev = NULL; +/** The base of the I/O port range. */ +static RTIOPORT g_IOPortBase; +/** The base of the MMIO range. */ +static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS; +/** The size of the MMIO range as seen by PCI. */ +static uint32_t g_cbMMIO; +/** The pointer to the mapping of the MMIO range. */ +static void *g_pvMMIOBase; +/** Wait queue used by polling. */ +static wait_queue_head_t g_PollEventQueue; +/** Asynchronous notification stuff. */ +static struct fasync_struct *g_pFAsyncQueue; +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +/** Pre-allocated mouse status VMMDev requests for use in the IRQ handler. */ +static VMMDevReqMouseStatusEx *g_pMouseStatusReqEx; +#endif +#if RTLNX_VER_MIN(2,6,0) +/** Whether we've create the logger or not. */ +static volatile bool g_fLoggerCreated; +/** Release logger group settings. */ +static char g_szLogGrp[128]; +/** Release logger flags settings. */ +static char g_szLogFlags[128]; +/** Release logger destination settings. */ +static char g_szLogDst[128]; +# if 0 +/** Debug logger group settings. */ +static char g_szDbgLogGrp[128]; +/** Debug logger flags settings. */ +static char g_szDbgLogFlags[128]; +/** Debug logger destination settings. */ +static char g_szDbgLogDst[128]; +# endif +#endif + +/** The input device handle */ +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +static struct input_dev *g_pInputDevice = NULL; +#endif + +/** The file_operations structure. */ +static struct file_operations g_FileOps = +{ + owner: THIS_MODULE, + open: vgdrvLinuxOpen, + release: vgdrvLinuxRelease, +#ifdef HAVE_UNLOCKED_IOCTL + unlocked_ioctl: vgdrvLinuxIOCtl, +#else + ioctl: vgdrvLinuxIOCtl, +#endif + fasync: vgdrvLinuxFAsync, + read: vgdrvLinuxRead, + poll: vgdrvLinuxPoll, + llseek: no_llseek, +}; + +/** The miscdevice structure. */ +static struct miscdevice g_MiscDevice = +{ + minor: MISC_DYNAMIC_MINOR, + name: DEVICE_NAME, + fops: &g_FileOps, +}; + +/** The file_operations structure for the user device. + * @remarks For the time being we'll be using the same implementation as + * /dev/vboxguest here. */ +static struct file_operations g_FileOpsUser = +{ + owner: THIS_MODULE, + open: vgdrvLinuxOpen, + release: vgdrvLinuxRelease, +#ifdef HAVE_UNLOCKED_IOCTL + unlocked_ioctl: vgdrvLinuxIOCtl, +#else + ioctl: vgdrvLinuxIOCtl, +#endif +}; + +/** The miscdevice structure for the user device. */ +static struct miscdevice g_MiscDeviceUser = +{ + minor: MISC_DYNAMIC_MINOR, + name: DEVICE_NAME_USER, + fops: &g_FileOpsUser, +}; + + +/** PCI hotplug structure. */ +static const struct pci_device_id g_VBoxGuestPciId[] = +{ + { + vendor: VMMDEV_VENDORID, + device: VMMDEV_DEVICEID + }, + { + /* empty entry */ + } +}; + +MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId); + +/** Structure for registering the PCI driver. */ +static struct pci_driver g_PciDriver = +{ + name: DRIVER_NAME, + id_table: g_VBoxGuestPciId, + probe: vgdrvLinuxProbePci, + remove: vgdrvLinuxTermPci +}; + +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +/** Kernel IDC session to ourselves for use with the mouse events. */ +static PVBOXGUESTSESSION g_pKernelSession = NULL; +#endif + + + +/** + * Converts a VBox status code to a linux error code. + * + * @returns corresponding negative linux error code. + * @param rc supdrv error code (SUPDRV_ERR_* defines). + */ +static int vgdrvLinuxConvertToNegErrno(int rc) +{ + if ( rc > -1000 + && rc < 1000) + return -RTErrConvertToErrno(rc); + switch (rc) + { + case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH; + case VINF_HGCM_CLIENT_REJECTED: return 0; + case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT; + case VINF_HGCM_ASYNC_EXECUTE: return 0; + case VERR_HGCM_INTERNAL: return -EPROTO; + case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL; + case VINF_HGCM_SAVE_STATE: return 0; + /* No reason to return this to a guest */ + // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST; + default: + AssertMsgFailed(("Unhandled error code %Rrc\n", rc)); + return -EPROTO; + } +} + + +/** + * Does the PCI detection and init of the device. + * + * @returns 0 on success, negated errno on failure. + */ +static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id) +{ + int rc; + + NOREF(id); + AssertReturn(!g_pPciDev, -EINVAL); + rc = pci_enable_device(pPciDev); + if (rc >= 0) + { + /* I/O Ports are mandatory, the MMIO bit is not. */ + g_IOPortBase = pci_resource_start(pPciDev, 0); + if (g_IOPortBase != 0) + { + /* + * Map the register address space. + */ + g_MMIOPhysAddr = pci_resource_start(pPciDev, 1); + g_cbMMIO = pci_resource_len(pPciDev, 1); + if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL) + { + g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO); + if (g_pvMMIOBase) + { + /** @todo why aren't we requesting ownership of the I/O ports as well? */ + g_pPciDev = pPciDev; + return 0; + } + + /* failure cleanup path */ + LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MMIOPhysAddr, g_cbMMIO)); + rc = -ENOMEM; + release_mem_region(g_MMIOPhysAddr, g_cbMMIO); + } + else + { + LogRel((DEVICE_NAME ": failed to obtain adapter memory\n")); + rc = -EBUSY; + } + g_MMIOPhysAddr = NIL_RTHCPHYS; + g_cbMMIO = 0; + g_IOPortBase = 0; + } + else + { + LogRel((DEVICE_NAME ": did not find expected hardware resources\n")); + rc = -ENXIO; + } + pci_disable_device(pPciDev); + } + else + LogRel((DEVICE_NAME ": could not enable device: %d\n", rc)); + return rc; +} + + +/** + * Clean up the usage of the PCI device. + */ +static void vgdrvLinuxTermPci(struct pci_dev *pPciDev) +{ + g_pPciDev = NULL; + if (pPciDev) + { + iounmap(g_pvMMIOBase); + g_pvMMIOBase = NULL; + + release_mem_region(g_MMIOPhysAddr, g_cbMMIO); + g_MMIOPhysAddr = NIL_RTHCPHYS; + g_cbMMIO = 0; + + pci_disable_device(pPciDev); + } +} + + +/** + * Interrupt service routine. + * + * @returns In 2.4 it returns void. + * In 2.6 we indicate whether we've handled the IRQ or not. + * + * @param iIrq The IRQ number. + * @param pvDevId The device ID, a pointer to g_DevExt. + * @param pRegs Register set. Removed in 2.6.19. + */ +#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING) +static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId) +#else +static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId, struct pt_regs *pRegs) +#endif +{ + bool fTaken = VGDrvCommonISR(&g_DevExt); + return IRQ_RETVAL(fTaken); +} + + +/** + * Registers the ISR and initializes the poll wait queue. + */ +static int __init vgdrvLinuxInitISR(void) +{ + int rc; + + init_waitqueue_head(&g_PollEventQueue); + rc = request_irq(g_pPciDev->irq, + vgdrvLinuxISR, +#if RTLNX_VER_MIN(2,6,20) + IRQF_SHARED, +#else + SA_SHIRQ, +#endif + DEVICE_NAME, + &g_DevExt); + if (rc) + { + LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc)); + return rc; + } + return 0; +} + + +/** + * Deregisters the ISR. + */ +static void vgdrvLinuxTermISR(void) +{ + free_irq(g_pPciDev->irq, &g_DevExt); +} + + +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +/** + * Check if extended mouse pointer state request protocol is currently used by driver. + * + * @returns True if extended protocol is used, False otherwise. + */ +static bool vgdrvLinuxUsesMouseStatusEx(void) +{ + return g_pMouseStatusReqEx->Core.header.requestType == VMMDevReq_GetMouseStatusEx; +} + +/** + * Reports the mouse integration status to the host. + * + * Calls the kernel IOCtl to report mouse status to the host on behalf of + * our kernel session. + * + * @param fStatus The mouse status to report. + */ +static int vgdrvLinuxSetMouseStatus(uint32_t fStatus) +{ + int rc; + VBGLIOCSETMOUSESTATUS Req; + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fStatus; + rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &Req.Hdr, sizeof(Req)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +/** + * Called when the input device is first opened. + * + * Sets up absolute mouse reporting. + */ +static int vboxguestOpenInputDevice(struct input_dev *pDev) +{ + int rc = vgdrvLinuxSetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE + | VMMDEV_MOUSE_NEW_PROTOCOL + | (vgdrvLinuxUsesMouseStatusEx() ? VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL : 0)); + if (RT_FAILURE(rc)) + return ENODEV; + NOREF(pDev); + return 0; +} + + +/** + * Called if all open handles to the input device are closed. + * + * Disables absolute reporting. + */ +static void vboxguestCloseInputDevice(struct input_dev *pDev) +{ + NOREF(pDev); + vgdrvLinuxSetMouseStatus(0); +} + + +/** + * Free corresponding mouse status request structure. + */ +static void vgdrvLinuxFreeMouseStatusReq(void) +{ + VbglR0GRFree(&g_pMouseStatusReqEx->Core.header); + g_pMouseStatusReqEx = NULL; +} + +/** + * Creates the kernel input device. + */ +static int __init vgdrvLinuxCreateInputDevice(void) +{ + /* Try to allocate legacy request data first, and check if host supports extended protocol. */ + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReqEx, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus); + if (RT_SUCCESS(rc)) + { + /* Check if host supports extended mouse state reporting. */ + g_pMouseStatusReqEx->Core.mouseFeatures = 0; + rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header); + if (RT_SUCCESS(rc)) + { + if (g_pMouseStatusReqEx->Core.mouseFeatures & VMMDEV_MOUSE_HOST_USES_FULL_STATE_PROTOCOL) + { + VMMDevReqMouseStatusEx *pReqEx = NULL; + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqEx, sizeof(*pReqEx), VMMDevReq_GetMouseStatusEx); + if (RT_SUCCESS(rc)) + { + /* De-allocate legacy request data, */ + VbglR0GRFree(&g_pMouseStatusReqEx->Core.header); + /* ..and switch to extended requests. */ + g_pMouseStatusReqEx = pReqEx; + LogRel(("Host supports full mouse state reporting, switching to extended mouse integration protocol\n")); + } + else + LogRel(("Host supports full mouse state reporting, but feature cannot be initialized, switching to legacy mouse integration protocol\n")); + } + else + LogRel(("Host does not support full mouse state reporting, switching to legacy mouse integration protocol\n")); + } + else + LogRel(("Unable to get host mouse capabilities, switching to legacy mouse integration protocol\n")); + } + else + rc = -ENOMEM; + + if (RT_SUCCESS(rc)) + { + g_pInputDevice = input_allocate_device(); + if (g_pInputDevice) + { + g_pInputDevice->name = VBOX_INPUT_DEVICE_NAME; + g_pInputDevice->id.bustype = BUS_PCI; + g_pInputDevice->id.vendor = VMMDEV_VENDORID; + g_pInputDevice->id.product = VMMDEV_DEVICEID; + g_pInputDevice->id.version = VBOX_SHORT_VERSION; + g_pInputDevice->open = vboxguestOpenInputDevice; + g_pInputDevice->close = vboxguestCloseInputDevice; +# if RTLNX_VER_MAX(2,6,22) + g_pInputDevice->cdev.dev = &g_pPciDev->dev; +# else + g_pInputDevice->dev.parent = &g_pPciDev->dev; +# endif + /* Set up input device capabilities. */ + ASMBitSet(g_pInputDevice->evbit, EV_ABS); + ASMBitSet(g_pInputDevice->evbit, EV_KEY); +# ifdef EV_SYN + ASMBitSet(g_pInputDevice->evbit, EV_SYN); +# endif + ASMBitSet(g_pInputDevice->absbit, ABS_X); + ASMBitSet(g_pInputDevice->absbit, ABS_Y); + + input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0); + input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0); + + ASMBitSet(g_pInputDevice->keybit, BTN_MOUSE); + + /* Report extra capabilities to input layer if extended mouse state protocol + * will be used in communication with host. */ + if (vgdrvLinuxUsesMouseStatusEx()) + { + ASMBitSet(g_pInputDevice->evbit, EV_REL); + ASMBitSet(g_pInputDevice->evbit, EV_KEY); + + ASMBitSet(g_pInputDevice->relbit, REL_WHEEL); + ASMBitSet(g_pInputDevice->relbit, REL_HWHEEL); + + ASMBitSet(g_pInputDevice->keybit, BTN_LEFT); + ASMBitSet(g_pInputDevice->keybit, BTN_RIGHT); + ASMBitSet(g_pInputDevice->keybit, BTN_MIDDLE); + ASMBitSet(g_pInputDevice->keybit, BTN_SIDE); + ASMBitSet(g_pInputDevice->keybit, BTN_EXTRA); + } + + rc = input_register_device(g_pInputDevice); + if (rc == 0) + return 0; + + input_free_device(g_pInputDevice); + } + else + rc = -ENOMEM; + + vgdrvLinuxFreeMouseStatusReq(); + } + + return rc; +} + + +/** + * Terminates the kernel input device. + */ +static void vgdrvLinuxTermInputDevice(void) +{ + /* Notify host that mouse integration is no longer available. */ + vgdrvLinuxSetMouseStatus(0); + + vgdrvLinuxFreeMouseStatusReq(); + + /* See documentation of input_register_device(): input_free_device() + * should not be called after a device has been registered. */ + input_unregister_device(g_pInputDevice); +} + +#endif /* VBOXGUEST_WITH_INPUT_DRIVER */ + +/** + * Creates the device nodes. + * + * @returns 0 on success, negated errno on failure. + */ +static int __init vgdrvLinuxInitDeviceNodes(void) +{ + /* + * The full feature device node. + */ + int rc = misc_register(&g_MiscDevice); + if (!rc) + { + /* + * The device node intended to be accessible by all users. + */ + rc = misc_register(&g_MiscDeviceUser); + if (!rc) + return 0; + LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc)); + misc_deregister(&g_MiscDevice); + } + else + LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc)); + return rc; +} + + +/** + * Deregisters the device nodes. + */ +static void vgdrvLinuxTermDeviceNodes(void) +{ + misc_deregister(&g_MiscDevice); + misc_deregister(&g_MiscDeviceUser); +} + + +/** + * Initialize module. + * + * @returns appropriate status code. + */ +static int __init vgdrvLinuxModInit(void) +{ + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + int rc; + + /* + * Initialize IPRT first. + */ + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc); + return -EINVAL; + } + + /* + * Create the release log. + * (We do that here instead of common code because we want to log + * early failures using the LogRel macro.) + */ + rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, + RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL); + if (RT_SUCCESS(rc)) + { +#if RTLNX_VER_MIN(2,6,0) + RTLogGroupSettings(pRelLogger, g_szLogGrp); + RTLogFlags(pRelLogger, g_szLogFlags); + RTLogDestinations(pRelLogger, g_szLogDst); +#endif + RTLogRelSetDefaultInstance(pRelLogger); + } +#if RTLNX_VER_MIN(2,6,0) + g_fLoggerCreated = true; +#endif + + /* + * Locate and initialize the PCI device. + */ + rc = pci_register_driver(&g_PciDriver); + if (rc >= 0 && g_pPciDev) + { + /* + * Call the common device extension initializer. + */ +#if RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_X86) + VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26; +#elif RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_AMD64) + VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64; +#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_X86) + VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24; +#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_AMD64) + VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64; +#else +# warning "huh? which arch + version is this?" + VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux; +#endif + rc = VGDrvCommonInitDevExt(&g_DevExt, + g_IOPortBase, + g_pvMMIOBase, + g_cbMMIO, + enmOSType, + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Register the interrupt service routine for it now that g_DevExt can handle IRQs. + */ + rc = vgdrvLinuxInitISR(); + if (rc >= 0) /** @todo r=bird: status check differs from that inside vgdrvLinuxInitISR. */ + { +#ifdef VBOXGUEST_WITH_INPUT_DRIVER + /* + * Create the kernel session for this driver. + */ + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession); + if (RT_SUCCESS(rc)) + { + /* + * Create the kernel input device. + */ + rc = vgdrvLinuxCreateInputDevice(); + if (rc >= 0) + { +#endif + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + /* + * Finally, create the device nodes. + */ + rc = vgdrvLinuxInitDeviceNodes(); + if (rc >= 0) + { + /* some useful information for the user but don't show this on the console */ + LogRel((DEVICE_NAME ": Successfully loaded version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n")); + LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n", + g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO)); + printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version " + VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) " (interface " RT_XSTR(VMMDEV_VERSION) ")\n"); + return rc; + } + + /* bail out */ +#ifdef VBOXGUEST_WITH_INPUT_DRIVER + vgdrvLinuxTermInputDevice(); + } + else + { + LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc)); + rc = RTErrConvertFromErrno(rc); + } + VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession); + } +#endif + vgdrvLinuxTermISR(); + } + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + { + LogRel((DEVICE_NAME ": VGDrvCommonInitDevExt failed with rc=%Rrc\n", rc)); + rc = RTErrConvertFromErrno(rc); + } + } + else + { + LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n")); + rc = -ENODEV; + } + pci_unregister_driver(&g_PciDriver); + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + RTR0Term(); + return rc; +} + + +/** + * Unload the module. + */ +static void __exit vgdrvLinuxModExit(void) +{ + /* + * Inverse order of init. + */ + vgdrvLinuxTermDeviceNodes(); +#ifdef VBOXGUEST_WITH_INPUT_DRIVER + vgdrvLinuxTermInputDevice(); + VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession); +#endif + vgdrvLinuxTermISR(); + VGDrvCommonDeleteDevExt(&g_DevExt); + pci_unregister_driver(&g_PciDriver); + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + RTR0Term(); +} + + +/** + * Get the process user ID. + * + * @returns UID. + */ +DECLINLINE(RTUID) vgdrvLinuxGetUid(void) +{ +#if RTLNX_VER_MIN(2,6,29) +# if RTLNX_VER_MIN(3,5,0) + return from_kuid(current_user_ns(), current->cred->uid); +# else + return current->cred->uid; +# endif +#else + return current->uid; +#endif +} + + +/** + * Checks if the given group number is zero or not. + * + * @returns true / false. + * @param gid The group to check for. + */ +DECLINLINE(bool) vgdrvLinuxIsGroupZero(kgid_t gid) +{ +#if RTLNX_VER_MIN(3,5,0) + return from_kgid(current_user_ns(), gid); +#else + return gid == 0; +#endif +} + + +/** + * Searches the effective group and supplementary groups for @a gid. + * + * @returns true if member, false if not. + * @param gid The group to check for. + */ +DECLINLINE(RTGID) vgdrvLinuxIsInGroupEff(kgid_t gid) +{ + return in_egroup_p(gid) != 0; +} + + +/** + * Check if we can positively or negatively determine that the process is + * running under a login on the physical machine console. + * + * Havne't found a good way to figure this out for graphical sessions, so this + * is mostly pointless. But let us try do what we can do. + * + * @returns VMMDEV_REQUESTOR_CON_XXX. + */ +static uint32_t vgdrvLinuxRequestorOnConsole(void) +{ + uint32_t fRet = VMMDEV_REQUESTOR_CON_DONT_KNOW; + +#if RTLNX_VER_MIN(2,6,28) /* First with tty_kref_put(). */ + /* + * Check for tty0..63, ASSUMING that these are only used for the physical console. + */ + struct tty_struct *pTty = get_current_tty(); + if (pTty) + { +# if RTLNX_VER_MIN(4,2,0) + const char *pszName = tty_name(pTty); +# else + char szBuf[64]; + const char *pszName = tty_name(pTty, szBuf); +# endif + if ( pszName + && pszName[0] == 't' + && pszName[1] == 't' + && pszName[2] == 'y' + && RT_C_IS_DIGIT(pszName[3]) + && ( pszName[4] == '\0' + || ( RT_C_IS_DIGIT(pszName[4]) + && pszName[5] == '\0' + && (pszName[3] - '0') * 10 + (pszName[4] - '0') <= 63)) ) + fRet = VMMDEV_REQUESTOR_CON_YES; + tty_kref_put(pTty); + } +#endif + + return fRet; +} + + +/** + * Device open. Called on open /dev/vboxdrv + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp) +{ + int rc; + PVBOXGUESTSESSION pSession; + uint32_t fRequestor; + Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm)); + + /* + * Figure out the requestor flags. + * ASSUMES that the gid of /dev/vboxuser is what we should consider the special vbox group. + */ + fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + if (vgdrvLinuxGetUid() == 0) + fRequestor |= VMMDEV_REQUESTOR_USR_ROOT; + else + fRequestor |= VMMDEV_REQUESTOR_USR_USER; + if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor) + { + fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE; + if (!vgdrvLinuxIsGroupZero(pInode->i_gid) && vgdrvLinuxIsInGroupEff(pInode->i_gid)) + fRequestor |= VMMDEV_REQUESTOR_GRP_VBOX; + } + fRequestor |= vgdrvLinuxRequestorOnConsole(); + + /* + * Call common code to create the user session. Associate it with + * the file so we can access it in the other methods. + */ + rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession); + if (RT_SUCCESS(rc)) + pFilp->private_data = pSession; + + Log(("vgdrvLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n", + &g_DevExt, pSession, rc, vgdrvLinuxConvertToNegErrno(rc), RTProcSelf(), current->pid, current->comm)); + return vgdrvLinuxConvertToNegErrno(rc); +} + + +/** + * Close device. + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp) +{ + Log(("vgdrvLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n", + pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm)); + +#if RTLNX_VER_MAX(2,6,28) + /* This housekeeping was needed in older kernel versions to ensure that + * the file pointer didn't get left on the polling queue. */ + vgdrvLinuxFAsync(-1, pFilp, 0); +#endif + VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data); + pFilp->private_data = NULL; + return 0; +} + + +/** + * 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(). + */ +#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING) +static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg) +#else +static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg) +#endif +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data; + int rc; +#ifndef HAVE_UNLOCKED_IOCTL + unlock_kernel(); +#endif + +#if 0 /* no fast I/O controls defined atm. */ + if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || uCmd == SUP_IOCTL_FAST_DO_HM_RUN + || uCmd == SUP_IOCTL_FAST_DO_NOP) + && pSession->fUnrestricted == true)) + rc = VGDrvCommonIoCtlFast(uCmd, ulArg, &g_DevExt, pSession); + else +#endif + rc = vgdrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession); + +#ifndef HAVE_UNLOCKED_IOCTL + lock_kernel(); +#endif + return rc; +} + + +/** + * Device I/O Control entry point, slow variant. + * + * @param pFilp Associated file pointer. + * @param uCmd The function specified to ioctl(). + * @param ulArg The argument specified to ioctl(). + * @param pSession The session instance. + */ +static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession) +{ + int rc; + VBGLREQHDR Hdr; + PVBGLREQHDR pHdr; + uint32_t cbBuf; + + Log6(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid)); + + /* + * Read the header. + */ + if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr)))) + { + Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd)); + return -EFAULT; + } + if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION)) + { + Log(("vgdrvLinuxIOCtlSlow: bad header version %#x; uCmd=%#x\n", Hdr.uVersion, uCmd)); + return -EINVAL; + } + + /* + * Buffer the request. + * Note! The header is revalidated by the common code. + */ + cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY(cbBuf > _1M*16)) + { + Log(("vgdrvLinuxIOCtlSlow: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd)); + return -E2BIG; + } + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || (cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd) != 0))) + { + Log(("vgdrvLinuxIOCtlSlow: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd)); + return -EINVAL; + } + pHdr = RTMemAlloc(cbBuf); + if (RT_UNLIKELY(!pHdr)) + { + LogRel(("vgdrvLinuxIOCtlSlow: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd)); + return -ENOMEM; + } + if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn))) + { + Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd)); + RTMemFree(pHdr); + return -EFAULT; + } + if (Hdr.cbIn < cbBuf) + RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn); + + /* + * Process the IOCtl. + */ + rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf); + + /* + * Copy ioctl data and output buffer back to user space. + */ + if (RT_SUCCESS(rc)) + { + uint32_t cbOut = pHdr->cbOut; + if (RT_UNLIKELY(cbOut > cbBuf)) + { + LogRel(("vgdrvLinuxIOCtlSlow: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd)); + cbOut = cbBuf; + } + if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut))) + { + /* this is really bad! */ + LogRel(("vgdrvLinuxIOCtlSlow: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd)); + rc = -EFAULT; + } + } + else + { + Log(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc)); + rc = -EINVAL; + } + RTMemFree(pHdr); + + Log6(("vgdrvLinuxIOCtlSlow: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid)); + return rc; +} + + +/** + * @note This code is duplicated on other platforms with variations, so please + * keep them all up to date when making changes! + */ +int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + /* + * Simple request validation (common code does the rest). + */ + int rc; + if ( RT_VALID_PTR(pReqHdr) + && cbReq >= sizeof(*pReqHdr)) + { + /* + * All requests except the connect one requires a valid session. + */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; + if (pSession) + { + if ( RT_VALID_PTR(pSession) + && pSession->pDevExt == &g_DevExt) + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + else + rc = VERR_INVALID_HANDLE; + } + else if (uReq == VBGL_IOCTL_IDC_CONNECT) + { + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + if (RT_FAILURE(rc)) + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + } + else + rc = VERR_INVALID_HANDLE; + } + else + rc = VERR_INVALID_POINTER; + return rc; +} +EXPORT_SYMBOL(VBoxGuestIDC); + + +/** + * Asynchronous notification activation method. + * + * @returns 0 on success, negative errno on failure. + * + * @param fd The file descriptor. + * @param pFile The file structure. + * @param fOn On/off indicator. + */ +static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn) +{ + return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue); +} + + +/** + * Poll function. + * + * This returns ready to read if the mouse pointer mode or the pointer position + * has changed since last call to read. + * + * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes. + * + * @param pFile The file structure. + * @param pPt The poll table. + * + * @remarks This is probably not really used, X11 is said to use the fasync + * interface instead. + */ +static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data; + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq + ? POLLIN | POLLRDNORM + : 0; + poll_wait(pFile, &g_PollEventQueue, pPt); + return fMask; +} + + +/** + * Read to go with our poll/fasync response. + * + * @returns 1 or -EINVAL. + * + * @param pFile The file structure. + * @param pbBuf The buffer to read into. + * @param cbRead The max number of bytes to read. + * @param poff The current file position. + * + * @remarks This is probably not really used as X11 lets the driver do its own + * event reading. The poll condition is therefore also cleared when we + * see VMMDevReq_GetMouseStatus in vgdrvIoCtl_VMMRequest. + */ +static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data; + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + + if (*poff != 0) + return -EINVAL; + + /* + * Fake a single byte read if we're not up to date with the current mouse position. + */ + if ( pSession->u32MousePosChangedSeq != u32CurSeq + && cbRead > 0) + { + pSession->u32MousePosChangedSeq = u32CurSeq; + pbBuf[0] = 0; + return 1; + } + return 0; +} + + +#ifdef VBOXGUEST_WITH_INPUT_DRIVER +/** + * Get host mouse state. + * + * @returns IPRT status code. + * @param pfMouseFeatures Where to store host mouse capabilities. + * @param pX Where to store X axis coordinate. + * @param pY Where to store Y axis coordinate. + * @param pDz Where to store vertical wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request). + * @param pDw Where to store horizontal wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request). + * @param pfButtons Where to store mouse buttons state (only set if in case of VMMDevReq_GetMouseStatusEx request). + */ +static int vgdrvLinuxGetHostMouseState(uint32_t *pfMouseFeatures, int32_t *pX, int32_t *pY, int32_t *pDz, int32_t *pDw, uint32_t *pfButtons) +{ + int rc = VERR_INVALID_PARAMETER; + + Assert(pfMouseFeatures); + Assert(pX); + Assert(pY); + Assert(pDz); + Assert(pDw); + Assert(pfButtons); + + /* Initialize legacy request data. */ + g_pMouseStatusReqEx->Core.mouseFeatures = 0; + g_pMouseStatusReqEx->Core.pointerXPos = 0; + g_pMouseStatusReqEx->Core.pointerYPos = 0; + + /* Initialize extended request data if VMMDevReq_GetMouseStatusEx is used. */ + if (vgdrvLinuxUsesMouseStatusEx()) + { + g_pMouseStatusReqEx->dz = 0; + g_pMouseStatusReqEx->dw = 0; + g_pMouseStatusReqEx->fButtons = 0; + } + + /* Get host mouse state - either lagacy or extended version. */ + rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header); + if (RT_SUCCESS(rc)) + { + *pfMouseFeatures = g_pMouseStatusReqEx->Core.mouseFeatures; + *pX = g_pMouseStatusReqEx->Core.pointerXPos; + *pY = g_pMouseStatusReqEx->Core.pointerYPos; + + /* Get extended mouse state data in case of VMMDevReq_GetMouseStatusEx. */ + if (vgdrvLinuxUsesMouseStatusEx()) + { + *pDz = g_pMouseStatusReqEx->dz; + *pDw = g_pMouseStatusReqEx->dw; + *pfButtons = g_pMouseStatusReqEx->fButtons; + } + } + + return rc; +} +#endif /* VBOXGUEST_WITH_INPUT_DRIVER */ + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ +#ifdef VBOXGUEST_WITH_INPUT_DRIVER + int rc; + uint32_t fMouseFeatures = 0; + int32_t x = 0; + int32_t y = 0; + int32_t dz = 0; + int32_t dw = 0; + uint32_t fButtons = 0; +#endif + NOREF(pDevExt); + + /* + * Wake up everyone that's in a poll() and post anyone that has + * subscribed to async notifications. + */ + Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n")); + wake_up_all(&g_PollEventQueue); + Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n")); + kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN); +#ifdef VBOXGUEST_WITH_INPUT_DRIVER + rc = vgdrvLinuxGetHostMouseState(&fMouseFeatures, &x, &y, &dz, &dw, &fButtons); + if (RT_SUCCESS(rc)) + { + input_report_abs(g_pInputDevice, ABS_X, x); + input_report_abs(g_pInputDevice, ABS_Y, y); + + if ( vgdrvLinuxUsesMouseStatusEx() + && fMouseFeatures & VMMDEV_MOUSE_HOST_USES_FULL_STATE_PROTOCOL + && fMouseFeatures & VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL) + { + /* Vertical and horizontal scroll values come as-is from GUI. + * Invert values here as it is done in PS/2 mouse driver, so + * scrolling direction will be exectly the same. */ + input_report_rel(g_pInputDevice, REL_WHEEL, -dz); + input_report_rel(g_pInputDevice, REL_HWHEEL, -dw); + + input_report_key(g_pInputDevice, BTN_LEFT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_LEFT)); + input_report_key(g_pInputDevice, BTN_RIGHT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_RIGHT)); + input_report_key(g_pInputDevice, BTN_MIDDLE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_MIDDLE)); + input_report_key(g_pInputDevice, BTN_SIDE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X1)); + input_report_key(g_pInputDevice, BTN_EXTRA, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X2)); + } + +# ifdef EV_SYN + input_sync(g_pInputDevice); +# endif + } +#endif + Log3(("VGDrvNativeISRMousePollEvent: done\n")); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +#if RTLNX_VER_MIN(2,6,0) + +/** log and dbg_log parameter setter. */ +static int vgdrvLinuxParamLogGrpSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam) +{ + if (g_fLoggerCreated) + { + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + if (pLogger) + RTLogGroupSettings(pLogger, pszValue); + } + else if (pParam->name[0] != 'd') + strlcpy(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp)); + + return 0; +} + +/** log and dbg_log parameter getter. */ +static int vgdrvLinuxParamLogGrpGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam) +{ + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + *pszBuf = '\0'; + if (pLogger) + RTLogQueryGroupSettings(pLogger, pszBuf, _4K); + return strlen(pszBuf); +} + + +/** log and dbg_log_flags parameter setter. */ +static int vgdrvLinuxParamLogFlagsSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam) +{ + if (g_fLoggerCreated) + { + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + if (pLogger) + RTLogFlags(pLogger, pszValue); + } + else if (pParam->name[0] != 'd') + strlcpy(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags)); + return 0; +} + +/** log and dbg_log_flags parameter getter. */ +static int vgdrvLinuxParamLogFlagsGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam) +{ + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + *pszBuf = '\0'; + if (pLogger) + RTLogQueryFlags(pLogger, pszBuf, _4K); + return strlen(pszBuf); +} + + +/** log and dbg_log_dest parameter setter. */ +static int vgdrvLinuxParamLogDstSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam) +{ + if (g_fLoggerCreated) + { + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + if (pLogger) + RTLogDestinations(pLogger, pszValue); + } + else if (pParam->name[0] != 'd') + strlcpy(&g_szLogDst[0], pszValue, sizeof(g_szLogDst)); + return 0; +} + +/** log and dbg_log_dest parameter getter. */ +static int vgdrvLinuxParamLogDstGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam) +{ + PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance(); + *pszBuf = '\0'; + if (pLogger) + RTLogQueryDestinations(pLogger, pszBuf, _4K); + return strlen(pszBuf); +} + + +/** r3_log_to_host parameter setter. */ +static int vgdrvLinuxParamR3LogToHostSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam) +{ + g_DevExt.fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue); + return 0; +} + +/** r3_log_to_host parameter getter. */ +static int vgdrvLinuxParamR3LogToHostGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam) +{ + strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled"); + return strlen(pszBuf); +} + + +/* + * Define module parameters. + */ +module_param_call(log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664); +module_param_call(log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664); +module_param_call(log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664); +# ifdef LOG_ENABLED +module_param_call(dbg_log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664); +module_param_call(dbg_log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664); +module_param_call(dbg_log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664); +# endif +module_param_call(r3_log_to_host, vgdrvLinuxParamR3LogToHostSet, vgdrvLinuxParamR3LogToHostGet, NULL, 0664); + +#endif /* 2.6.0 and later */ + + +module_init(vgdrvLinuxModInit); +module_exit(vgdrvLinuxModExit); + +MODULE_AUTHOR(VBOX_VENDOR); +MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV)); +#endif + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c new file mode 100644 index 00000000..cd60e4aa --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c @@ -0,0 +1,1094 @@ +/* $Id: VBoxGuest-netbsd.c $ */ +/** @file + * VirtualBox Guest Additions Driver for NetBSD. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/select.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/kmem.h> +#include <sys/module.h> +#include <sys/device.h> +#include <sys/bus.h> +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/kauth.h> +#include <sys/stat.h> +#include <sys/selinfo.h> +#include <sys/queue.h> +#include <sys/lock.h> +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/vfs_syscalls.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> +#include <dev/wscons/tpcalibvar.h> + +#ifdef PVM +# undef PVM +#endif +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/err.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The module name. */ +#define DEVICE_NAME "vboxguest" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct VBoxGuestDeviceState +{ + device_t sc_dev; + pci_chipset_tag_t sc_pc; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_addr_t sc_iobase; + bus_size_t sc_iosize; + + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + + /** Size of the memory area. */ + bus_size_t sc_memsize; + + /** IRQ resource handle. */ + pci_intr_handle_t ih; + /** Pointer to the IRQ handler. */ + void *pfnIrqHandler; + + /** Controller features, limits and status. */ + u_int vboxguest_state; + + device_t sc_wsmousedev; + VMMDevReqMouseStatus *sc_vmmmousereq; + PVBOXGUESTSESSION sc_session; + struct tpcalib_softc sc_tpcalib; +} vboxguest_softc; + + +struct vboxguest_fdata +{ + vboxguest_softc *sc; + PVBOXGUESTSESSION session; +}; + +#define VBOXGUEST_STATE_INITOK 1 << 0 + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +/* + * Driver(9) autoconf machinery. + */ +static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux); +static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux); +static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc); +static int VBoxGuestNetBSDDetach(device_t self, int flags); + +/* + * IRQ related functions. + */ +static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa); +static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc); +static int VBoxGuestNetBSDISR(void *pvState); + +/* + * Character device file handlers. + */ +static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *process); +static int VBoxGuestNetBSDClose(struct file *fp); +static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long cmd, void *addr); +static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data); +static int VBoxGuestNetBSDPoll(struct file *fp, int events); + +/* + * wsmouse(4) accessops + */ +static int VBoxGuestNetBSDWsmEnable(void *cookie); +static void VBoxGuestNetBSDWsmDisable(void *cookie); +static int VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l); + +static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern struct cfdriver vboxguest_cd; /* CFDRIVER_DECL */ +extern struct cfattach vboxguest_ca; /* CFATTACH_DECL */ + +/* + * The /dev/vboxguest character device entry points. + */ +static struct cdevsw g_VBoxGuestNetBSDChrDevSW = +{ + .d_open = VBoxGuestNetBSDOpen, + .d_close = noclose, + .d_read = noread, + .d_write = nowrite, + .d_ioctl = noioctl, + .d_stop = nostop, + .d_tty = notty, + .d_poll = nopoll, + .d_mmap = nommap, + .d_kqfilter = nokqfilter, +}; + +static const struct fileops vboxguest_fileops = { + .fo_read = fbadop_read, + .fo_write = fbadop_write, + .fo_ioctl = VBoxGuestNetBSDIOCtl, + .fo_fcntl = fnullop_fcntl, + .fo_poll = VBoxGuestNetBSDPoll, + .fo_stat = fbadop_stat, + .fo_close = VBoxGuestNetBSDClose, + .fo_kqfilter = fnullop_kqfilter, + .fo_restart = fnullop_restart +}; + + +const struct wsmouse_accessops vboxguest_wsm_accessops = { + VBoxGuestNetBSDWsmEnable, + VBoxGuestNetBSDWsmIOCtl, + VBoxGuestNetBSDWsmDisable, +}; + + +/* + * XXX: wsmux(4) doesn't properly handle the case when two mice with + * absolute position events but different calibration data are being + * multiplexed. Without GAs the absolute events will be reported + * through the tablet ums(4) device with the range of 32k, but with + * GAs the absolute events will be reported through the VMM device + * (wsmouse at vboxguest) and VMM uses the range of 64k. Which one + * responds to the calibration ioctl depends on the order of + * attachment. On boot kernel attaches ums first and GAs later, so + * it's VMM (this driver) that gets the ioctl. After save/restore the + * ums will be detached and re-attached and after that it's ums that + * will get the ioctl, but the events (with a wider range) will still + * come via the VMM, confusing X, wsmoused, etc. Hack around that by + * forcing the range here to match the tablet's range. + * + * We force VMM range into the ums range and rely on the fact that no + * actual calibration is done and both devices are used in the raw + * mode. See tpcalib_trans call below. + * + * Cf. src/VBox/Devices/Input/UsbMouse.cpp + */ +#define USB_TABLET_RANGE_MIN 0 +#define USB_TABLET_RANGE_MAX 0x7fff + +static struct wsmouse_calibcoords vboxguest_wsm_default_calib = { + .minx = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN, + .miny = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN, + .maxx = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX, + .maxy = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX, + .samplelen = WSMOUSE_CALIBCOORDS_RESET, +}; + +/** Device extention & session data association structure. */ +static VBOXGUESTDEVEXT g_DevExt; + +static vboxguest_softc *g_SC; + +/** Reference counter */ +static volatile uint32_t cUsers; +/** selinfo structure used for polling. */ +static struct selinfo g_SelInfo; + + +CFATTACH_DECL_NEW(vboxguest, sizeof(vboxguest_softc), + VBoxGuestNetBSDMatch, VBoxGuestNetBSDAttach, VBoxGuestNetBSDDetach, NULL); + + +static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux) +{ + const struct pci_attach_args *pa = aux; + + if (RT_UNLIKELY(g_SC != NULL)) /* should not happen */ + return 0; + + if ( PCI_VENDOR(pa->pa_id) == VMMDEV_VENDORID + && PCI_PRODUCT(pa->pa_id) == VMMDEV_DEVICEID) + { + return 1; + } + + return 0; +} + + +static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux) +{ + int rc = VINF_SUCCESS; + int iResId = 0; + vboxguest_softc *sc; + struct pci_attach_args *pa = aux; + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + bus_dma_segment_t seg; + int ioh_valid, memh_valid; + + KASSERT(g_SC == NULL); + + cUsers = 0; + + aprint_normal(": VirtualBox Guest\n"); + + sc = device_private(self); + sc->sc_dev = self; + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + LogFunc(("RTR0Init failed.\n")); + aprint_error_dev(sc->sc_dev, "RTR0Init failed\n"); + return; + } + + sc->sc_pc = pa->pa_pc; + + /* + * Allocate I/O port resource. + */ + ioh_valid = (pci_mapreg_map(pa, PCI_BAR0, + PCI_MAPREG_TYPE_IO, 0, + &sc->sc_iot, &sc->sc_ioh, + &sc->sc_iobase, &sc->sc_iosize) == 0); + + if (ioh_valid) + { + + /* + * Map the MMIO region. + */ + memh_valid = (pci_mapreg_map(pa, PCI_BAR1, + PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR, + &sc->sc_memt, &sc->sc_memh, + NULL, &sc->sc_memsize) == 0); + if (memh_valid) + { + /* + * Call the common device extension initializer. + */ + rc = VGDrvCommonInitDevExt(&g_DevExt, sc->sc_iobase, + bus_space_vaddr(sc->sc_memt, sc->sc_memh), + sc->sc_memsize, +#if ARCH_BITS == 64 + VBOXOSTYPE_NetBSD_x64, +#else + VBOXOSTYPE_NetBSD, +#endif + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Add IRQ of VMMDev. + */ + rc = VBoxGuestNetBSDAddIRQ(sc, pa); + if (RT_SUCCESS(rc)) + { + sc->vboxguest_state |= VBOXGUEST_STATE_INITOK; + + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + /* + * Attach wsmouse. + */ + VBoxGuestNetBSDWsmAttach(sc); + + g_SC = sc; + return; + } + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + { + aprint_error_dev(sc->sc_dev, "init failed\n"); + } + bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize); + } + else + { + aprint_error_dev(sc->sc_dev, "MMIO mapping failed\n"); + } + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); + } + else + { + aprint_error_dev(sc->sc_dev, "IO mapping failed\n"); + } + + RTR0Term(); + return; +} + + +/** + * Sets IRQ for VMMDev. + * + * @returns NetBSD error code. + * @param sc Pointer to the state info structure. + * @param pa Pointer to the PCI attach arguments. + */ +static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa) +{ + int iResId = 0; + int rc = 0; + const char *intrstr; +#if __NetBSD_Prereq__(6, 99, 39) + char intstrbuf[100]; +#endif + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + if (pci_intr_map(pa, &sc->ih)) + { + aprint_error_dev(sc->sc_dev, "couldn't map interrupt.\n"); + return VERR_DEV_IO_ERROR; + } + + intrstr = pci_intr_string(sc->sc_pc, sc->ih +#if __NetBSD_Prereq__(6, 99, 39) + , intstrbuf, sizeof(intstrbuf) +#endif + ); + aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); + + sc->pfnIrqHandler = pci_intr_establish(sc->sc_pc, sc->ih, IPL_BIO, VBoxGuestNetBSDISR, sc); + if (sc->pfnIrqHandler == NULL) + { + aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n"); + return VERR_DEV_IO_ERROR; + } + + return VINF_SUCCESS; +} + + +/* + * Optionally attach wsmouse(4) device as a child. + */ +static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc) +{ + struct wsmousedev_attach_args am = { &vboxguest_wsm_accessops, sc }; + + PVBOXGUESTSESSION session = NULL; + VMMDevReqMouseStatus *req = NULL; + int rc; + + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &session); + if (RT_FAILURE(rc)) + goto fail; + + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(*req), + VMMDevReq_GetMouseStatus); + if (RT_FAILURE(rc)) + goto fail; + +#if __NetBSD_Prereq__(9,99,88) + sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint, + CFARGS(.iattr = "wsmousedev")); +#elif __NetBSD_Prereq__(9,99,82) + sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint, + CFARG_IATTR, "wsmousedev", + CFARG_EOL); +#else + sc->sc_wsmousedev = config_found_ia(sc->sc_dev, "wsmousedev", + &am, wsmousedevprint); +#endif + + if (sc->sc_wsmousedev == NULL) + goto fail; + + sc->sc_session = session; + sc->sc_vmmmousereq = req; + + tpcalib_init(&sc->sc_tpcalib); + tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, + &vboxguest_wsm_default_calib, 0, 0); + return; + + fail: + if (session != NULL) + VGDrvCommonCloseSession(&g_DevExt, session); + if (req != NULL) + VbglR0GRFree((VMMDevRequestHeader *)req); +} + + +static int VBoxGuestNetBSDDetach(device_t self, int flags) +{ + vboxguest_softc *sc; + sc = device_private(self); + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + if (cUsers > 0) + return EBUSY; + + if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0) + return 0; + + /* + * Reverse what we did in VBoxGuestNetBSDAttach. + */ + if (sc->sc_vmmmousereq != NULL) + VbglR0GRFree((VMMDevRequestHeader *)sc->sc_vmmmousereq); + + VBoxGuestNetBSDRemoveIRQ(sc); + + VGDrvCommonDeleteDevExt(&g_DevExt); + + bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); + + RTR0Term(); + + return config_detach_children(self, flags); +} + + +/** + * Removes IRQ for VMMDev. + * + * @param sc Opaque pointer to the state info structure. + */ +static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc) +{ + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + if (sc->pfnIrqHandler) + { + pci_intr_disestablish(sc->sc_pc, sc->pfnIrqHandler); + } +} + + +/** + * Interrupt service routine. + * + * @returns Whether the interrupt was from VMMDev. + * @param pvState Opaque pointer to the device state. + */ +static int VBoxGuestNetBSDISR(void *pvState) +{ + LogFlow((DEVICE_NAME ": %s: pvState=%p\n", __func__, pvState)); + + bool fOurIRQ = VGDrvCommonISR(&g_DevExt); + + return fOurIRQ ? 1 : 0; +} + + +/* + * Called by VGDrvCommonISR() if mouse position changed + */ +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + vboxguest_softc *sc = g_SC; + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + /* + * Wake up poll waiters. + */ + selnotify(&g_SelInfo, 0, 0); + + if (sc->sc_vmmmousereq != NULL) { + int x, y; + int rc; + + sc->sc_vmmmousereq->mouseFeatures = 0; + sc->sc_vmmmousereq->pointerXPos = 0; + sc->sc_vmmmousereq->pointerYPos = 0; + + rc = VbglR0GRPerform(&sc->sc_vmmmousereq->header); + if (RT_FAILURE(rc)) + return; + + /* XXX: see the comment for vboxguest_wsm_default_calib */ + int rawx = (unsigned)sc->sc_vmmmousereq->pointerXPos >> 1; + int rawy = (unsigned)sc->sc_vmmmousereq->pointerYPos >> 1; + tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y); + + wsmouse_input(sc->sc_wsmousedev, + 0, /* buttons */ + x, y, + 0, 0, /* z, w */ + WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); + } +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus) +{ + VBGLIOCSETMOUSESTATUS Req; + int rc; + + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fStatus; + rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS, + &g_DevExt, + sc->sc_session, + &Req.Hdr, sizeof(Req)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + + return rc; +} + + +static int +VBoxGuestNetBSDWsmEnable(void *cookie) +{ + vboxguest_softc *sc = cookie; + int rc; + + rc = VBoxGuestNetBSDSetMouseStatus(sc, VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE + | VMMDEV_MOUSE_NEW_PROTOCOL); + if (RT_FAILURE(rc)) + return RTErrConvertToErrno(rc); + + return 0; +} + + +static void +VBoxGuestNetBSDWsmDisable(void *cookie) +{ + vboxguest_softc *sc = cookie; + VBoxGuestNetBSDSetMouseStatus(sc, 0); +} + + +static int +VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l) +{ + vboxguest_softc *sc = cookie; + + switch (cmd) { + case WSMOUSEIO_GTYPE: + *(u_int *)data = WSMOUSE_TYPE_TPANEL; + break; + + case WSMOUSEIO_SCALIBCOORDS: + case WSMOUSEIO_GCALIBCOORDS: + return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l); + + default: + return EPASSTHROUGH; + } + return 0; +} + + +/** + * File open handler + * + */ +static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *pLwp) +{ + vboxguest_softc *sc; + struct vboxguest_fdata *fdata; + file_t *fp; + int fd, error; + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + if ((sc = device_lookup_private(&vboxguest_cd, minor(device))) == NULL) + { + printf("device_lookup_private failed\n"); + return (ENXIO); + } + + if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0) + { + aprint_error_dev(sc->sc_dev, "device not configured\n"); + return (ENXIO); + } + + fdata = kmem_alloc(sizeof(*fdata), KM_SLEEP); + if (fdata != NULL) + { + fdata->sc = sc; + + error = fd_allocfile(&fp, &fd); + if (error == 0) + { + /* + * Create a new session. + */ + struct kauth_cred *pCred = pLwp->l_cred; + int fHaveCred = (pCred != NULL && pCred != NOCRED && pCred != FSCRED); + uint32_t fRequestor; + int fIsWheel; + int rc; + + fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + + /* uid */ + if (fHaveCred && kauth_cred_geteuid(pCred) == (uid_t)0) + fRequestor |= VMMDEV_REQUESTOR_USR_ROOT; + else + fRequestor |= VMMDEV_REQUESTOR_USR_USER; + + /* gid */ + if (fHaveCred + && (kauth_cred_getegid(pCred) == (gid_t)0 + || (kauth_cred_ismember_gid(pCred, 0, &fIsWheel) == 0 + && fIsWheel))) + fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL; + +#if 0 /** @todo implement /dev/vboxuser */ + if (!fUnrestricted) + fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE; +#else + fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; +#endif + + /** @todo can we find out if pLwp is on the console? */ + fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW; + + rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &fdata->session); + if (RT_SUCCESS(rc)) + { + ASMAtomicIncU32(&cUsers); + return fd_clone(fp, fd, flags, &vboxguest_fileops, fdata); + } + + aprint_error_dev(sc->sc_dev, "VBox session creation failed\n"); + closef(fp); /* ??? */ + error = RTErrConvertToErrno(rc); + } + kmem_free(fdata, sizeof(*fdata)); + } + else + error = ENOMEM; + return error; +} + +/** + * File close handler + * + */ +static int VBoxGuestNetBSDClose(struct file *fp) +{ + struct vboxguest_fdata *fdata = fp->f_data; + vboxguest_softc *sc = fdata->sc; + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + VGDrvCommonCloseSession(&g_DevExt, fdata->session); + ASMAtomicDecU32(&cUsers); + + kmem_free(fdata, sizeof(*fdata)); + + return 0; +} + +/** + * IOCTL handler + * + */ +static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long command, void *data) +{ + struct vboxguest_fdata *fdata = fp->f_data; + + if (VBGL_IOCTL_IS_FAST(command)) + return VGDrvCommonIoCtlFast(command, &g_DevExt, fdata->session); + + return VBoxGuestNetBSDIOCtlSlow(fdata, command, data); +} + +static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data) +{ + vboxguest_softc *sc = fdata->sc; + size_t cbReq = IOCPARM_LEN(command); + PVBGLREQHDR pHdr = NULL; + void *pvUser = NULL; + int err, rc; + + LogFlow(("%s: command=%#lx data=%p\n", __func__, command, data)); + + /* + * Buffered request? + */ + if ((command & IOC_DIRMASK) == IOC_INOUT) + { + /* will be validated by VGDrvCommonIoCtl() */ + pHdr = (PVBGLREQHDR)data; + } + + /* + * Big unbuffered request? "data" is the userland pointer. + */ + else if ((command & IOC_DIRMASK) == IOC_VOID && cbReq != 0) + { + /* + * Read the header, validate it and figure out how much that + * needs to be buffered. + */ + VBGLREQHDR Hdr; + + if (RT_UNLIKELY(cbReq < sizeof(Hdr))) + return ENOTTY; + + pvUser = data; + err = copyin(pvUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(err != 0)) + return err; + + if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION)) + return ENOTTY; + + if (cbReq > 16 * _1M) + return EINVAL; + + if (Hdr.cbOut == 0) + Hdr.cbOut = Hdr.cbIn; + + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbIn > cbReq + || Hdr.cbOut < sizeof(Hdr) || Hdr.cbOut > cbReq)) + return EINVAL; + + /* + * Allocate buffer and copy in the data. + */ + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + + pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq); + if (RT_UNLIKELY(pHdr == NULL)) + { + LogRel(("%s: command=%#lx data=%p: unable to allocate %zu bytes\n", + __func__, command, data, cbReq)); + return ENOMEM; + } + + err = copyin(pvUser, pHdr, Hdr.cbIn); + if (err != 0) + { + RTMemTmpFree(pHdr); + return err; + } + + if (Hdr.cbIn < cbReq) + memset((uint8_t *)pHdr + Hdr.cbIn, '\0', cbReq - Hdr.cbIn); + } + + /* + * Process the IOCtl. + */ + rc = VGDrvCommonIoCtl(command, &g_DevExt, fdata->session, pHdr, cbReq); + if (RT_SUCCESS(rc)) + { + err = 0; + + /* + * If unbuffered, copy back the result before returning. + */ + if (pvUser != NULL) + { + size_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + LogRel(("%s: command=%#lx data=%p: too much output: %zu > %zu\n", + __func__, command, data, cbOut, cbReq)); + cbOut = cbReq; + } + + err = copyout(pHdr, pvUser, cbOut); + RTMemTmpFree(pHdr); + } + } + else + { + LogRel(("%s: command=%#lx data=%p: error %Rrc\n", + __func__, command, data, rc)); + + if (pvUser != NULL) + RTMemTmpFree(pHdr); + + err = RTErrConvertToErrno(rc); + } + + return err; +} + +static int VBoxGuestNetBSDPoll(struct file *fp, int events) +{ + struct vboxguest_fdata *fdata = fp->f_data; + vboxguest_softc *sc = fdata->sc; + + int rc = 0; + int events_processed; + + uint32_t u32CurSeq; + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (fdata->session->u32MousePosChangedSeq != u32CurSeq) + { + events_processed = events & (POLLIN | POLLRDNORM); + fdata->session->u32MousePosChangedSeq = u32CurSeq; + } + else + { + events_processed = 0; + + selrecord(curlwp, &g_SelInfo); + } + + return events_processed; +} + + +/** + * @note This code is duplicated on other platforms with variations, so please + * keep them all up to date when making changes! + */ +int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + /* + * Simple request validation (common code does the rest). + */ + int rc; + if ( RT_VALID_PTR(pReqHdr) + && cbReq >= sizeof(*pReqHdr)) + { + /* + * All requests except the connect one requires a valid session. + */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; + if (pSession) + { + if ( RT_VALID_PTR(pSession) + && pSession->pDevExt == &g_DevExt) + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + else + rc = VERR_INVALID_HANDLE; + } + else if (uReq == VBGL_IOCTL_IDC_CONNECT) + { + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + if (RT_FAILURE(rc)) + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + } + else + rc = VERR_INVALID_HANDLE; + } + else + rc = VERR_INVALID_POINTER; + return rc; +} + + +MODULE(MODULE_CLASS_DRIVER, vboxguest, "pci"); + +/* + * XXX: See netbsd/vboxguest.ioconf for the details. +*/ +#if 0 +#include "ioconf.c" +#else + +static const struct cfiattrdata wsmousedevcf_iattrdata = { + "wsmousedev", 1, { + { "mux", "0", 0 }, + } +}; + +/* device vboxguest: wsmousedev */ +static const struct cfiattrdata * const vboxguest_attrs[] = { &wsmousedevcf_iattrdata, NULL }; +CFDRIVER_DECL(vboxguest, DV_DULL, vboxguest_attrs); + +static struct cfdriver * const cfdriver_ioconf_vboxguest[] = { + &vboxguest_cd, NULL +}; + + +static const struct cfparent vboxguest_pspec = { + "pci", "pci", DVUNIT_ANY +}; +static int vboxguest_loc[] = { -1, -1 }; + + +static const struct cfparent wsmousedev_pspec = { + "wsmousedev", "vboxguest", DVUNIT_ANY +}; +static int wsmousedev_loc[] = { 0 }; + + +static struct cfdata cfdata_ioconf_vboxguest[] = { + /* vboxguest0 at pci? dev ? function ? */ + { + .cf_name = "vboxguest", + .cf_atname = "vboxguest", + .cf_unit = 0, /* Only unit 0 is ever used */ + .cf_fstate = FSTATE_NOTFOUND, + .cf_loc = vboxguest_loc, + .cf_flags = 0, + .cf_pspec = &vboxguest_pspec, + }, + + /* wsmouse* at vboxguest? */ + { "wsmouse", "wsmouse", 0, FSTATE_STAR, wsmousedev_loc, 0, &wsmousedev_pspec }, + + { NULL, NULL, 0, 0, NULL, 0, NULL } +}; + +static struct cfattach * const vboxguest_cfattachinit[] = { + &vboxguest_ca, NULL +}; + +static const struct cfattachinit cfattach_ioconf_vboxguest[] = { + { "vboxguest", vboxguest_cfattachinit }, + { NULL, NULL } +}; +#endif + + +static int +vboxguest_modcmd(modcmd_t cmd, void *opaque) +{ + devmajor_t bmajor, cmajor; +#if !__NetBSD_Prereq__(8,99,46) + register_t retval; +#endif + int error; + + LogFlow((DEVICE_NAME ": %s\n", __func__)); + + switch (cmd) + { + case MODULE_CMD_INIT: + error = config_init_component(cfdriver_ioconf_vboxguest, + cfattach_ioconf_vboxguest, + cfdata_ioconf_vboxguest); + if (error) + break; + + bmajor = cmajor = NODEVMAJOR; + error = devsw_attach("vboxguest", + NULL, &bmajor, + &g_VBoxGuestNetBSDChrDevSW, &cmajor); + if (error) + { + if (error == EEXIST) + error = 0; /* maybe built-in ... improve eventually */ + else + break; + } + + error = do_sys_mknod(curlwp, "/dev/vboxguest", + 0666|S_IFCHR, makedev(cmajor, 0), +#if !__NetBSD_Prereq__(8,99,46) + &retval, +#endif + UIO_SYSSPACE); + if (error == EEXIST) { + error = 0; + + /* + * Since NetBSD doesn't yet have a major reserved for + * vboxguest, the (first free) major we get will + * change when new devices are added, so an existing + * /dev/vboxguest may now point to some other device, + * creating confusion (tripped me up a few times). + */ + aprint_normal("vboxguest: major %d:" + " check existing /dev/vboxguest\n", cmajor); + } + break; + + case MODULE_CMD_FINI: + error = config_fini_component(cfdriver_ioconf_vboxguest, + cfattach_ioconf_vboxguest, + cfdata_ioconf_vboxguest); + if (error) + break; + + devsw_detach(NULL, &g_VBoxGuestNetBSDChrDevSW); + break; + + default: + return ENOTTY; + } + return error; +} diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp new file mode 100644 index 00000000..75f797d6 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp @@ -0,0 +1,698 @@ +/* $Id: VBoxGuest-os2.cpp $ */ +/** @file + * VBoxGuest - OS/2 specifics. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * VBoxDrv - OS/2 specifics. + * + * Copyright (c) 2007-2012 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 * +*********************************************************************************************************************************/ +#include <os2ddk/bsekee.h> + +#include "VBoxGuestInternal.h" +#include <VBox/version.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/log.h> +#include <iprt/memobj.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/process.h> +#include <iprt/spinlock.h> +#include <iprt/string.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Device extention & session data association structure. + */ +static VBOXGUESTDEVEXT g_DevExt; +/** The memory object for the MMIO memory. */ +static RTR0MEMOBJ g_MemObjMMIO = NIL_RTR0MEMOBJ; +/** The memory mapping object the MMIO memory. */ +static RTR0MEMOBJ g_MemMapMMIO = NIL_RTR0MEMOBJ; + +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PVBOXGUESTSESSION g_apSessionHashTab[19]; +/** Calculates the index into g_apSessionHashTab.*/ +#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab)) + +RT_C_DECLS_BEGIN +/* Defined in VBoxGuestA-os2.asm */ +extern uint32_t g_PhysMMIOBase; +extern uint32_t g_cbMMIO; /* 0 currently not set. */ +extern uint16_t g_IOPortBase; +extern uint8_t g_bInterruptLine; +extern uint8_t g_bPciBusNo; +extern uint8_t g_bPciDevFunNo; +extern RTFAR16 g_fpfnVBoxGuestOs2IDCService16; +extern RTFAR16 g_fpfnVBoxGuestOs2IDCService16Asm; +#ifdef DEBUG_READ +/* (debugging) */ +extern uint16_t g_offLogHead; +extern uint16_t volatile g_offLogTail; +extern uint16_t const g_cchLogMax; +extern char g_szLog[]; +#endif +/* (init only:) */ +extern char g_szInitText[]; +extern uint16_t g_cchInitText; +extern uint16_t g_cchInitTextMax; +RT_C_DECLS_END + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int vgdrvOS2MapMemory(void); +static VBOXOSTYPE vgdrvOS2DetectVersion(void); + +/* in VBoxGuestA-os2.asm */ +DECLASM(int) vgdrvOS2DevHlpSetIRQ(uint8_t bIRQ); + + +/** + * 32-bit Ring-0 initialization. + * + * This is called from VBoxGuestA-os2.asm upon the first open call to the vboxgst$ device. + * + * @returns 0 on success, non-zero on failure. + * @param pszArgs Pointer to the device arguments. + */ +DECLASM(int) vgdrvOS2Init(const char *pszArgs) +{ + //Log(("vgdrvOS2Init: pszArgs='%s' MMIO=0x%RX32 IOPort=0x%RX16 Int=%#x Bus=%#x Dev=%#x Fun=%d\n", + // pszArgs, g_PhysMMIOBase, g_IOPortBase, g_bInterruptLine, g_bPciBusNo, g_bPciDevFunNo >> 3, g_bPciDevFunNo & 7)); + + /* + * Initialize the runtime. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Process the command line. + */ + bool fVerbose = true; + if (pszArgs) + { + char ch; + while ((ch = *pszArgs++) != '\0') + if (ch == '-' || ch == '/') + { + ch = *pszArgs++; + if (ch == 'Q' || ch == 'q') + fVerbose = false; + else if (ch == 'V' || ch == 'v') + fVerbose = true; + else if (ch == '\0') + break; + /*else: ignore stuff we don't know what is */ + } + /* else: skip spaces and unknown stuff */ + } + + /* + * Map the MMIO memory if found. + */ + rc = vgdrvOS2MapMemory(); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + if (g_MemMapMMIO != NIL_RTR0MEMOBJ) + rc = VGDrvCommonInitDevExt(&g_DevExt, g_IOPortBase, + RTR0MemObjAddress(g_MemMapMMIO), + RTR0MemObjSize(g_MemMapMMIO), + vgdrvOS2DetectVersion(), + 0); + else + rc = VGDrvCommonInitDevExt(&g_DevExt, g_IOPortBase, NULL, 0, vgdrvOS2DetectVersion(), 0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the session hash table. + */ + rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestOS2"); + if (RT_SUCCESS(rc)) + { + /* + * Configure the interrupt handler. + */ + if (g_bInterruptLine) + { + rc = vgdrvOS2DevHlpSetIRQ(g_bInterruptLine); + if (rc) + { + Log(("vgdrvOS2DevHlpSetIRQ(%d) -> %d\n", g_bInterruptLine, rc)); + rc = RTErrConvertFromOS2(rc); + } + } + if (RT_SUCCESS(rc)) + { + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + /* + * Success + */ + if (fVerbose) + { + strcpy(&g_szInitText[0], + "\r\n" + "VirtualBox Guest Additions Driver for OS/2 version " VBOX_VERSION_STRING "\r\n" + "Copyright (C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\r\n"); + g_cchInitText = strlen(&g_szInitText[0]); + } + Log(("vgdrvOS2Init: Successfully loaded\n%s", g_szInitText)); + return VINF_SUCCESS; + } + + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxGuest.sys: SetIrq failed for IRQ %#d, rc=%Rrc\n", + g_bInterruptLine, rc); + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxGuest.sys: RTSpinlockCreate failed, rc=%Rrc\n", rc); + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxGuest.sys: vgdrvOS2InitDevExt failed, rc=%Rrc\n", rc); + + int rc2 = RTR0MemObjFree(g_MemObjMMIO, true /* fFreeMappings */); AssertRC(rc2); + g_MemObjMMIO = g_MemMapMMIO = NIL_RTR0MEMOBJ; + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxGuest.sys: VBoxGuestOS2MapMMIO failed, rc=%Rrc\n", rc); + RTR0Term(); + } + else + g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxGuest.sys: RTR0Init failed, rc=%Rrc\n", rc); + + RTLogBackdoorPrintf("vgdrvOS2Init: failed rc=%Rrc - %s", rc, &g_szInitText[0]); + return rc; +} + + +/** + * Maps the VMMDev memory. + * + * @returns VBox status code. + * @retval VERR_VERSION_MISMATCH The VMMDev memory didn't meet our expectations. + */ +static int vgdrvOS2MapMemory(void) +{ + const RTCCPHYS PhysMMIOBase = g_PhysMMIOBase; + + /* + * Did we find any MMIO region (0 or NIL)? + */ + if ( !PhysMMIOBase + || PhysMMIOBase == NIL_RTCCPHYS) + { + Assert(g_MemMapMMIO != NIL_RTR0MEMOBJ); + return VINF_SUCCESS; + } + + /* + * Create a physical memory object for it. + * + * Since we don't know the actual size (OS/2 doesn't at least), we make + * a qualified guess using the VMMDEV_RAM_SIZE. + */ + size_t cb = RT_ALIGN_Z(VMMDEV_RAM_SIZE, PAGE_SIZE); + int rc = RTR0MemObjEnterPhys(&g_MemObjMMIO, PhysMMIOBase, cb, RTMEM_CACHE_POLICY_DONT_CARE); + if (RT_FAILURE(rc)) + { + cb = _4K; + rc = RTR0MemObjEnterPhys(&g_MemObjMMIO, PhysMMIOBase, cb, RTMEM_CACHE_POLICY_DONT_CARE); + } + if (RT_FAILURE(rc)) + { + Log(("vgdrvOS2MapMemory: RTR0MemObjEnterPhys(,%RCp,%zx) -> %Rrc\n", PhysMMIOBase, cb, rc)); + return rc; + } + + /* + * Map the object into kernel space. + * + * We want a normal mapping with normal caching, which good in two ways. First + * since the API doesn't have any flags indicating how the mapping should be cached. + * And second, because PGM doesn't necessarily respect the cache/writethru bits + * anyway for normal RAM. + */ + rc = RTR0MemObjMapKernel(&g_MemMapMMIO, g_MemObjMMIO, (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + if (RT_SUCCESS(rc)) + { + /* + * Validate the VMM memory. + */ + VMMDevMemory *pVMMDev = (VMMDevMemory *)RTR0MemObjAddress(g_MemMapMMIO); + Assert(pVMMDev); + if ( pVMMDev->u32Version == VMMDEV_MEMORY_VERSION + && pVMMDev->u32Size >= 32 /* just for checking sanity */) + { + /* + * Did we hit the correct size? If not we'll have to + * redo the mapping using the correct size. + */ + if (RT_ALIGN_32(pVMMDev->u32Size, PAGE_SIZE) == cb) + return VINF_SUCCESS; + + Log(("vgdrvOS2MapMemory: Actual size %#RX32 (tried %#zx)\n", pVMMDev->u32Size, cb)); + cb = RT_ALIGN_32(pVMMDev->u32Size, PAGE_SIZE); + + rc = RTR0MemObjFree(g_MemObjMMIO, true); AssertRC(rc); + g_MemObjMMIO = g_MemMapMMIO = NIL_RTR0MEMOBJ; + + rc = RTR0MemObjEnterPhys(&g_MemObjMMIO, PhysMMIOBase, cb, RTMEM_CACHE_POLICY_DONT_CARE); + if (RT_SUCCESS(rc)) + { + rc = RTR0MemObjMapKernel(&g_MemMapMMIO, g_MemObjMMIO, (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + Log(("vgdrvOS2MapMemory: RTR0MemObjMapKernel [%RCp,%zx] -> %Rrc (2nd)\n", PhysMMIOBase, cb, rc)); + } + else + Log(("vgdrvOS2MapMemory: RTR0MemObjEnterPhys(,%RCp,%zx) -> %Rrc (2nd)\n", PhysMMIOBase, cb, rc)); + } + else + { + rc = VERR_VERSION_MISMATCH; + LogRel(("vgdrvOS2MapMemory: Bogus VMMDev memory; u32Version=%RX32 (expected %RX32) u32Size=%RX32\n", + pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size)); + } + } + else + Log(("vgdrvOS2MapMemory: RTR0MemObjMapKernel [%RCp,%zx] -> %Rrc\n", PhysMMIOBase, cb, rc)); + + int rc2 = RTR0MemObjFree(g_MemObjMMIO, true /* fFreeMappings */); AssertRC(rc2); + g_MemObjMMIO = g_MemMapMMIO = NIL_RTR0MEMOBJ; + return rc; +} + + +/** + * Called fromn vgdrvOS2Init to determine which OS/2 version this is. + * + * @returns VBox OS/2 type. + */ +static VBOXOSTYPE vgdrvOS2DetectVersion(void) +{ + VBOXOSTYPE enmOSType = VBOXOSTYPE_OS2; + +#if 0 /** @todo dig up the version stuff from GIS later and verify that the numbers are actually decimal. */ + unsigned uMajor, uMinor; + if (uMajor == 2) + { + if (uMinor >= 30 && uMinor < 40) + enmOSType = VBOXOSTYPE_OS2Warp3; + else if (uMinor >= 40 && uMinor < 45) + enmOSType = VBOXOSTYPE_OS2Warp4; + else if (uMinor >= 45 && uMinor < 50) + enmOSType = VBOXOSTYPE_OS2Warp45; + } +#endif + return enmOSType; +} + + +DECLASM(int) vgdrvOS2Open(uint16_t sfn) +{ + int rc; + PVBOXGUESTSESSION pSession; + + /* + * Create a new session. + */ + uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE + | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN + | VMMDEV_REQUESTOR_USR_ROOT /* everyone is root on OS/2 */ + | VMMDEV_REQUESTOR_GRP_WHEEL /* and their admins */ + | VMMDEV_REQUESTOR_NO_USER_DEVICE /** @todo implement /dev/vboxuser? */ + | VMMDEV_REQUESTOR_CON_DONT_KNOW; /** @todo check screen group/whatever of process to see if console */ + rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession); + if (RT_SUCCESS(rc)) + { + pSession->sfn = sfn; + + /* + * Insert it into the hash table. + */ + unsigned iHash = SESSION_HASH(sfn); + RTSpinlockAcquire(g_Spinlock); + pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = pSession; + RTSpinlockRelease(g_Spinlock); + } + + Log(("vgdrvOS2Open: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf())); + return rc; +} + + +DECLASM(int) vgdrvOS2Close(uint16_t sfn) +{ + Log(("vgdrvOS2Close: pid=%d sfn=%d\n", (int)RTProcSelf(), sfn)); + + /* + * Remove from the hash table. + */ + PVBOXGUESTSESSION pSession; + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + RTSpinlockAcquire(g_Spinlock); + + pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if ( pSession->sfn == sfn + && pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + } + else + { + PVBOXGUESTSESSION 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; + } + } + } + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("VBoxGuestIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d sfn=%d\n", (int)Process, sfn)); + return VERR_INVALID_PARAMETER; + } + + /* + * Close the session. + */ + VGDrvCommonCloseSession(&g_DevExt, pSession); + return 0; +} + + +DECLASM(int) vgdrvOS2IOCtlFast(uint16_t sfn, uint8_t iFunction, int32_t *prc) +{ + /* + * Find the session. + */ + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + PVBOXGUESTSESSION pSession; + + RTSpinlockAcquire(g_Spinlock); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while ( pSession + && ( pSession->sfn != sfn + || pSession->Process != Process)); + } + RTSpinlockRelease(g_Spinlock); + if (RT_UNLIKELY(!pSession)) + { + Log(("VBoxGuestIoctl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); + return VERR_INVALID_PARAMETER; + } + + /* + * Dispatch the fast IOCtl. + */ + *prc = VGDrvCommonIoCtlFast(iFunction, &g_DevExt, pSession); + return 0; +} + + +/** + * 32-bit IDC service routine. + * + * @returns VBox status code. + * @param u32Session The session handle (PVBOXGUESTSESSION). + * @param iFunction The requested function. + * @param pReqHdr The input/output data buffer. The caller + * ensures that this cannot be swapped out, or that + * it's acceptable to take a page in fault in the + * current context. If the request doesn't take + * input or produces output, apssing NULL is okay. + * @param cbReq The size of the data buffer. + * + * @remark This is called from the 16-bit thunker as well as directly from the 32-bit clients. + */ +DECLASM(int) VGDrvOS2IDCService(uint32_t u32Session, unsigned iFunction, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)u32Session; + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertMsgReturn(pSession->sfn == 0xffff, ("%RX16\n", pSession->sfn), VERR_INVALID_HANDLE); + AssertMsgReturn(pSession->pDevExt == &g_DevExt, ("%p != %p\n", pSession->pDevExt, &g_DevExt), VERR_INVALID_HANDLE); + + return VGDrvCommonIoCtl(iFunction, &g_DevExt, pSession, pReqHdr, cbReq); +} + + +/** + * Worker for VBoxGuestOS2IDC, it creates the kernel session. + * + * @returns Pointer to the session. + */ +DECLASM(PVBOXGUESTSESSION) vgdrvOS2IDCConnect(void) +{ + PVBOXGUESTSESSION pSession; + int rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + pSession->sfn = 0xffff; + return pSession; + } + return NULL; +} + + +DECLASM(int) vgdrvOS2IOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *pvParm, void *pvData, + uint16_t *pcbParm, uint16_t *pcbData) +{ + /* + * Find the session. + */ + const RTPROCESS Process = RTProcSelf(); + const unsigned iHash = SESSION_HASH(sfn); + PVBOXGUESTSESSION pSession; + + RTSpinlockAcquire(g_Spinlock); + pSession = g_apSessionHashTab[iHash]; + if (pSession && pSession->Process != Process) + { + do pSession = pSession->pNextHash; + while ( pSession + && ( pSession->sfn != sfn + || pSession->Process != Process)); + } + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("VBoxGuestIoctl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); + return VERR_INVALID_PARAMETER; + } + + /* + * Verify the category and dispatch the IOCtl. + * + * The IOCtl call uses the parameter buffer as generic data input/output + * buffer similar to the one unix ioctl buffer argument. While the data + * buffer is not used. + */ + if (RT_LIKELY(iCat == VBGL_IOCTL_CATEGORY)) + { + Log(("vgdrvOS2IOCtl: pSession=%p iFunction=%#x pvParm=%p pvData=%p *pcbParm=%d *pcbData=%d\n", pSession, iFunction, pvParm, pvData, *pcbParm, *pcbData)); + if ( pvParm + && *pcbParm >= sizeof(VBGLREQHDR) + && *pcbData == 0) + { + /* + * Lock the buffer. + */ + KernVMLock_t ParmLock; + int32_t rc = KernVMLock(VMDHL_WRITE, pvParm, *pcbParm, &ParmLock, (KernPageList_t *)-1, NULL); + if (rc == 0) + { + /* + * Process the IOCtl. + */ + PVBGLREQHDR pReqHdr = (PVBGLREQHDR)pvParm; + rc = VGDrvCommonIoCtl(iFunction, &g_DevExt, pSession, pReqHdr, *pcbParm); + + /* + * Unlock the buffer. + */ + *pcbParm = RT_SUCCESS(rc) ? pReqHdr->cbOut : sizeof(*pReqHdr); + int rc2 = KernVMUnlock(&ParmLock); + AssertMsg(rc2 == 0, ("rc2=%d\n", rc2)); NOREF(rc2); + + Log2(("vgdrvOS2IOCtl: returns %d\n", rc)); + return rc; + } + AssertMsgFailed(("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pvParm, *pcbParm, &ParmLock, rc)); + return VERR_LOCK_FAILED; + } + Log2(("vgdrvOS2IOCtl: returns VERR_INVALID_PARAMETER (iFunction=%#x)\n", iFunction)); + return VERR_INVALID_PARAMETER; + } + return VERR_NOT_SUPPORTED; +} + + +/** + * 32-bit ISR, called by 16-bit assembly thunker in VBoxGuestA-os2.asm. + * + * @returns true if it's our interrupt, false it isn't. + */ +DECLASM(bool) vgdrvOS2ISR(void) +{ + Log(("vgdrvOS2ISR\n")); + + return VGDrvCommonISR(&g_DevExt); +} + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + /* No polling on OS/2 */ + NOREF(pDevExt); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +#ifdef DEBUG_READ /** @todo figure out this one once and for all... */ + +/** + * 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) vgdrvOS2LogOutput(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; +} + + +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(vgdrvOS2LogOutput, NULL, pszFormat, va); + va_end(va); + + return cch; +} + +#endif /* DEBUG_READ */ + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def new file mode 100644 index 00000000..8d44f3b7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def @@ -0,0 +1,55 @@ +; $Id: VBoxGuest-os2.def $ +;; @file +; VBoxGuest - OS/2 definition file. +; + +; +; Copyright (C) 2007-2022 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox 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. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + +PHYSICAL DEVICE VBoxGst +DESCRIPTION 'VirtualBox Guest Additions 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/Additions/common/VBoxGuest/VBoxGuest-solaris.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c new file mode 100644 index 00000000..a56bb253 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c @@ -0,0 +1,1138 @@ +/* $Id: VBoxGuest-solaris.c $ */ +/** @file + * VirtualBox Guest Additions Driver for Solaris. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/mutex.h> +#include <sys/pci.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/ddi_intr.h> +#include <sys/sunddi.h> +#include <sys/open.h> +#include <sys/sunldi.h> +#include <sys/policy.h> +#include <sys/file.h> +#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */ + +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <VBox/version.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/cdefs.h> +#include <iprt/asm.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The module name. */ +#define DEVICE_NAME "vboxguest" +/** The module description as seen in 'modinfo'. */ +#define DEVICE_DESC "VirtualBox GstDrv" + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int vgdrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred); +static int vgdrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred); +static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal); +static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArgs); +static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead); + +static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult); +static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd); +static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd); +static int vgdrvSolarisQuiesce(dev_info_t *pDip); + +static int vgdrvSolarisAddIRQ(dev_info_t *pDip); +static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip); +static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg); +static uint_t vgdrvSolarisISR(caddr_t Arg); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * cb_ops: for drivers that support char/block entry points + */ +static struct cb_ops g_vgdrvSolarisCbOps = +{ + vgdrvSolarisOpen, + vgdrvSolarisClose, + nodev, /* b strategy */ + nodev, /* b dump */ + nodev, /* b print */ + vgdrvSolarisRead, + vgdrvSolarisWrite, + vgdrvSolarisIOCtl, + nodev, /* c devmap */ + nodev, /* c mmap */ + nodev, /* c segmap */ + vgdrvSolarisPoll, + 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_vgdrvSolarisDevOps = +{ + DEVO_REV, /* driver build revision */ + 0, /* ref count */ + vgdrvSolarisGetInfo, + nulldev, /* identify */ + nulldev, /* probe */ + vgdrvSolarisAttach, + vgdrvSolarisDetach, + nodev, /* reset */ + &g_vgdrvSolarisCbOps, + (struct bus_ops *)0, + nodev, /* power */ + vgdrvSolarisQuiesce +}; + +/** + * modldrv: export driver specifics to the kernel + */ +static struct modldrv g_vgdrvSolarisModule = +{ + &mod_driverops, /* extern from kernel */ + DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV), + &g_vgdrvSolarisDevOps +}; + +/** + * modlinkage: export install/remove/info to the kernel + */ +static struct modlinkage g_vgdrvSolarisModLinkage = +{ + MODREV_1, /* loadable module system revision */ + &g_vgdrvSolarisModule, + NULL /* terminate array of linkage structures */ +}; + +/** + * State info for each open file handle. + */ +typedef struct +{ + /** Pointer to the session handle. */ + PVBOXGUESTSESSION pSession; + /** The process reference for posting signals */ + void *pvProcRef; +} vboxguest_state_t; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Device handle (we support only one instance). */ +static dev_info_t *g_pDip = NULL; +/** Opaque pointer to file-descriptor states */ +static void *g_pvgdrvSolarisState = NULL; +/** Device extention & session data association structure. */ +static VBOXGUESTDEVEXT g_DevExt; +/** IO port handle. */ +static ddi_acc_handle_t g_PciIOHandle; +/** MMIO handle. */ +static ddi_acc_handle_t g_PciMMIOHandle; +/** IO Port. */ +static uint16_t g_uIOPortBase; +/** Address of the MMIO region.*/ +static caddr_t g_pMMIOBase; +/** Size of the MMIO region. */ +static off_t g_cbMMIO; +/** Pointer to an array of interrupt handles. */ +static ddi_intr_handle_t *g_pahIntrs; +/** Handle to the soft interrupt. */ +static ddi_softint_handle_t g_hSoftIntr; +/** The pollhead structure */ +static pollhead_t g_PollHead; +/** The IRQ Mutex */ +static kmutex_t g_IrqMtx; +/** The IRQ high-level Mutex. */ +static kmutex_t g_HighLevelIrqMtx; +/** Whether soft-ints are setup. */ +static bool g_fSoftIntRegistered = false; + +/** Additional IPRT function we need to drag in for vboxfs. */ +PFNRT g_Deps[] = +{ + (PFNRT)RTErrConvertToErrno, +}; + + +/** + * Kernel entry points + */ +int _init(void) +{ + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + PRTLOGGER pRelLogger; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + 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); + else + cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc); + + /* + * Prevent module autounloading. + */ + modctl_t *pModCtl = mod_getctl(&g_vgdrvSolarisModLinkage); + if (pModCtl) + pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD; + else + LogRel((DEVICE_NAME ": failed to disable autounloading!\n")); + + rc = ddi_soft_state_init(&g_pvgdrvSolarisState, sizeof(vboxguest_state_t), 1); + if (!rc) + { + rc = mod_install(&g_vgdrvSolarisModLinkage); + if (rc) + ddi_soft_state_fini(&g_pvgdrvSolarisState); + } + } + else + { + cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc); + return EINVAL; + } + + return rc; +} + + +int _fini(void) +{ + LogFlow((DEVICE_NAME ":_fini\n")); + int rc = mod_remove(&g_vgdrvSolarisModLinkage); + if (!rc) + ddi_soft_state_fini(&g_pvgdrvSolarisState); + + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + + if (!rc) + RTR0Term(); + return rc; +} + + +int _info(struct modinfo *pModInfo) +{ + /* LogFlow((DEVICE_NAME ":_info\n")); - Called too early, causing RTThreadPreemtIsEnabled warning. */ + return mod_info(&g_vgdrvSolarisModLinkage, pModInfo); +} + + +/** + * Attach entry point, to attach a device to the system or resume it. + * + * @param pDip The module structure instance. + * @param enmCmd Attach type (ddi_attach_cmd_t) + * + * @return corresponding solaris error code. + */ +static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd) +{ + LogFlow(("vgdrvSolarisAttach:\n")); + switch (enmCmd) + { + case DDI_ATTACH: + { + if (g_pDip) + { + LogRel(("vgdrvSolarisAttach: Only one instance supported.\n")); + return DDI_FAILURE; + } + + /* + * Enable resources for PCI access. + */ + ddi_acc_handle_t PciHandle; + int rc = pci_config_setup(pDip, &PciHandle); + if (rc == DDI_SUCCESS) + { + /* + * Map the register address space. + */ + caddr_t baseAddr; + ddi_device_acc_attr_t deviceAttr; + deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; + deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC; + rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle); + if (rc == DDI_SUCCESS) + { + /* + * Read size of the MMIO region. + */ + g_uIOPortBase = (uintptr_t)baseAddr; + rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO); + if (rc == DDI_SUCCESS) + { + rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr, &g_PciMMIOHandle); + if (rc == DDI_SUCCESS) + { + /* + * Call the common device extension initializer. + */ + rc = VGDrvCommonInitDevExt(&g_DevExt, g_uIOPortBase, g_pMMIOBase, g_cbMMIO, +#if ARCH_BITS == 64 + VBOXOSTYPE_Solaris_x64, +#else + VBOXOSTYPE_Solaris, +#endif + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Add IRQ of VMMDev. + */ + rc = vgdrvSolarisAddIRQ(pDip); + if (rc == DDI_SUCCESS) + { + /* + * Read host configuration. + */ + VGDrvCommonProcessOptionsFromHost(&g_DevExt); + + rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, + 0 /* fFlags */); + if (rc == DDI_SUCCESS) + { + g_pDip = pDip; + pci_config_teardown(&PciHandle); + return DDI_SUCCESS; + } + + LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n")); + vgdrvSolarisRemoveIRQ(pDip); + } + else + LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n")); + VGDrvCommonDeleteDevExt(&g_DevExt); + } + else + LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n")); + ddi_regs_map_free(&g_PciMMIOHandle); + } + else + LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n")); + } + else + LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n")); + ddi_regs_map_free(&g_PciIOHandle); + } + else + LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n")); + pci_config_teardown(&PciHandle); + } + else + LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc)); + return DDI_FAILURE; + } + + case DDI_RESUME: + { + /** @todo implement resume for guest driver. */ + return DDI_SUCCESS; + } + + default: + return DDI_FAILURE; + } +} + + +/** + * Detach entry point, to detach a device to the system or suspend it. + * + * @param pDip The module structure instance. + * @param enmCmd Attach type (ddi_attach_cmd_t) + * + * @return corresponding solaris error code. + */ +static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd) +{ + LogFlow(("vgdrvSolarisDetach:\n")); + switch (enmCmd) + { + case DDI_DETACH: + { + vgdrvSolarisRemoveIRQ(pDip); + ddi_regs_map_free(&g_PciIOHandle); + ddi_regs_map_free(&g_PciMMIOHandle); + ddi_remove_minor_node(pDip, NULL); + VGDrvCommonDeleteDevExt(&g_DevExt); + g_pDip = NULL; + return DDI_SUCCESS; + } + + case DDI_SUSPEND: + { + /** @todo implement suspend for guest driver. */ + return DDI_SUCCESS; + } + + default: + return DDI_FAILURE; + } +} + + +/** + * Quiesce entry point, called by solaris kernel for disabling the device from + * generating any interrupts or doing in-bound DMA. + * + * @param pDip The module structure instance. + * + * @return corresponding solaris error code. + */ +static int vgdrvSolarisQuiesce(dev_info_t *pDip) +{ + int rc = ddi_intr_disable(g_pahIntrs[0]); + if (rc != DDI_SUCCESS) + return DDI_FAILURE; + + /** @todo What about HGCM/HGSMI touching guest-memory? */ + + return DDI_SUCCESS; +} + + +/** + * 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. + * + * @return corresponding solaris error code. + */ +static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult) +{ + LogFlow(("vgdrvSolarisGetInfo:\n")); + + int rc = DDI_SUCCESS; + switch (enmCmd) + { + case DDI_INFO_DEVT2DEVINFO: + { + *ppvResult = (void *)g_pDip; + if (!*ppvResult) + rc = DDI_FAILURE; + break; + } + + case DDI_INFO_DEVT2INSTANCE: + { + /* There can only be a single-instance of this driver and thus its instance number is 0. */ + *ppvResult = (void *)0; + break; + } + + default: + rc = DDI_FAILURE; + break; + } + + NOREF(pvArg); + return rc; +} + + +/** + * User context entry points + * + * @remarks fFlags are the flags passed to open() or to ldi_open_by_name. In + * the latter case the FKLYR flag is added to indicate that the caller + * is a kernel component rather than user land. + */ +static int vgdrvSolarisOpen(dev_t *pDev, int fFlags, int fType, cred_t *pCred) +{ + int rc; + PVBOXGUESTSESSION pSession = NULL; + + LogFlow(("vgdrvSolarisOpen:\n")); + + /* + * Verify we are being opened as a character device. + */ + if (fType != OTYP_CHR) + return EINVAL; + + vboxguest_state_t *pState = NULL; + unsigned iOpenInstance; + for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++) + { + if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */ + && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS) + { + pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance); + break; + } + } + if (!pState) + { + Log(("vgdrvSolarisOpen: too many open instances.")); + return ENXIO; + } + + /* + * Create a new session. + * + * Note! The devfs inode with the gid isn't readily available here, so we cannot easily + * to the vbox group detection like on linux. Read config instead? + */ + if (!(fFlags & FKLYR)) + { + uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + if (crgetruid(pCred) == 0) + fRequestor |= VMMDEV_REQUESTOR_USR_ROOT; + else + fRequestor |= VMMDEV_REQUESTOR_USR_USER; + if (secpolicy_coreadm(pCred) == 0) + fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL; + /** @todo is there any way of detecting that the process belongs to someone on the physical console? + * secpolicy_console() [== PRIV_SYS_DEVICES] doesn't look quite right, or does it? */ + fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW; + fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; /** @todo implement vboxuser device node. */ + + rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession); + } + else + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + if (!(fFlags & FKLYR)) + pState->pvProcRef = proc_ref(); + else + pState->pvProcRef = NULL; + pState->pSession = pSession; + *pDev = makedevice(getmajor(*pDev), iOpenInstance); + Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf())); + return 0; + } + + /* Failed, clean up. */ + ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance); + + LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc)); + return EFAULT; +} + + +static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred) +{ + LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf())); + + PVBOXGUESTSESSION pSession = NULL; + vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev)); + if (!pState) + { + Log(("vgdrvSolarisClose: failed to get pState.\n")); + return EFAULT; + } + + if (pState->pvProcRef != NULL) + { + proc_unref(pState->pvProcRef); + pState->pvProcRef = NULL; + } + pSession = pState->pSession; + pState->pSession = NULL; + Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState)); + ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev)); + if (!pSession) + { + Log(("vgdrvSolarisClose: failed to get pSession.\n")); + return EFAULT; + } + + /* + * Close the session. + */ + if (pSession) + VGDrvCommonCloseSession(&g_DevExt, pSession); + return 0; +} + + +static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFlow((DEVICE_NAME "::Read\n")); + + vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev)); + if (!pState) + { + Log((DEVICE_NAME "::Close: failed to get pState.\n")); + return EFAULT; + } + + PVBOXGUESTSESSION pSession = pState->pSession; + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + pSession->u32MousePosChangedSeq = u32CurSeq; + + return 0; +} + + +static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFlow(("vgdrvSolarisWrite:\n")); + return 0; +} + + +/** @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 + + +/** + * Driver ioctl, an alternate entry point for this character driver. + * + * @param Dev Device number + * @param iCmd Operation identifier + * @param iArgs 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 vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal) +{ + /* + * Get the session from the soft state item. + */ + vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev)); + if (!pState) + { + LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev))); + return EINVAL; + } + + PVBOXGUESTSESSION pSession = pState->pSession; + if (!pSession) + { + LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev))); + return DDI_SUCCESS; + } + + /* + * Deal with fast requests. + */ + if (VBGL_IOCTL_IS_FAST(iCmd)) + { + *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession); + return 0; + } + + /* + * It's kind of simple if this is a kernel session, take slow path if user land. + */ + if (pSession->R0Process == NIL_RTR0PROCESS) + { + if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR)) + { + PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs; + int rc; + if (iCmd != VBGL_IOCTL_IDC_DISCONNECT) + rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut)); + else + { + pState->pSession = NULL; + rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut)); + if (RT_FAILURE(rc)) + pState->pSession = pSession; + } + return rc; + } + } + + return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs); +} + + +/** + * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions. + * + * @returns Solaris errno. + * + * @param pSession The session. + * @param iCmd The IOCtl command. + * @param Mode Information bitfield (for specifying ownership of data) + * @param iArg User space address of the request buffer. + */ +static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg) +{ + int rc; + uint32_t cbBuf = 0; + union + { + VBGLREQHDR Hdr; + uint8_t abBuf[64]; + } StackBuf; + PVBGLREQHDR pHdr; + + + /* + * Read the header. + */ + if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr))) + { + LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr))); + return EINVAL; + } + rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode); + if (RT_UNLIKELY(rc)) + { + LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc)); + return EFAULT; + } + if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION)) + { + LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd)); + return EINVAL; + } + cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut); + if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr) + || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0) + || cbBuf > _1M*16)) + { + LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd)); + return EINVAL; + } + + /* + * Buffer the request. + * + * Note! Common code revalidates the header sizes and version. So it's + * fine to read it once more. + */ + if (cbBuf <= sizeof(StackBuf)) + pHdr = &StackBuf.Hdr; + else + { + pHdr = RTMemTmpAlloc(cbBuf); + if (RT_UNLIKELY(!pHdr)) + { + LogRel(("vgdrvSolarisIOCtlSlow: 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(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc)); + if (pHdr != &StackBuf.Hdr) + RTMemFree(pHdr); + return EFAULT; + } + + /* + * Process the IOCtl. + */ + rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf); + + /* + * Copy ioctl data and output buffer back to user space. + */ + if (RT_SUCCESS(rc)) + { + uint32_t cbOut = pHdr->cbOut; + if (RT_UNLIKELY(cbOut > cbBuf)) + { + LogRel(("vgdrvSolarisIOCtlSlow: 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(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc)); + rc = EFAULT; + } + } + else + rc = EINVAL; + + if (pHdr != &StackBuf.Hdr) + RTMemTmpFree(pHdr); + return rc; +} + + +#if 0 +/** + * @note This code is duplicated on other platforms with variations, so please + * keep them all up to date when making changes! + */ +int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + /* + * Simple request validation (common code does the rest). + */ + int rc; + if ( RT_VALID_PTR(pReqHdr) + && cbReq >= sizeof(*pReqHdr)) + { + /* + * All requests except the connect one requires a valid session. + */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; + if (pSession) + { + if ( RT_VALID_PTR(pSession) + && pSession->pDevExt == &g_DevExt) + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + else + rc = VERR_INVALID_HANDLE; + } + else if (uReq == VBGL_IOCTL_IDC_CONNECT) + { + rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq); + if (RT_FAILURE(rc)) + VGDrvCommonCloseSession(&g_DevExt, pSession); + } + } + else + rc = VERR_INVALID_HANDLE; + } + else + rc = VERR_INVALID_POINTER; + return rc; +} +#endif + + +static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead) +{ + LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet)); + + vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev)); + if (RT_LIKELY(pState)) + { + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession; + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + *pReqEvents |= (POLLIN | POLLRDNORM); + pSession->u32MousePosChangedSeq = u32CurSeq; + } + else + { + *pReqEvents = 0; + if (!fAnyYet) + *ppPollHead = &g_PollHead; + } + + return 0; + } + + Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev))); + return EINVAL; +} + + +/** + * Sets IRQ for VMMDev. + * + * @returns Solaris error code. + * @param pDip Pointer to the device info structure. + */ +static int vgdrvSolarisAddIRQ(dev_info_t *pDip) +{ + LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip)); + + /* Get the types of interrupt supported for this hardware. */ + int fIntrType = 0; + int rc = ddi_intr_get_supported_types(pDip, &fIntrType); + if (rc == DDI_SUCCESS) + { + /* We only support fixed interrupts at this point, not MSIs. */ + if (fIntrType & DDI_INTR_TYPE_FIXED) + { + /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */ + int cIntrCount = 0; + rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount); + if ( rc == DDI_SUCCESS + && cIntrCount == 1) + { + /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */ + g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t)); + if (g_pahIntrs) + { + /* Allocate the interrupt for this device and verify the allocation. */ + int cIntrAllocated; + rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated, + DDI_INTR_ALLOC_NORMAL); + if ( rc == DDI_SUCCESS + && cIntrAllocated == 1) + { + /* Get the interrupt priority assigned by the system. */ + uint_t uIntrPriority; + rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority); + if (rc == DDI_SUCCESS) + { + /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level + and low-level interrupt handlers with corresponding mutexes. */ + cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri()); + if (uIntrPriority >= ddi_intr_get_hilevel_pri()) + { + /* Initialize the high-level mutex. */ + mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority)); + + /* Assign interrupt handler function to the interrupt handle. */ + rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR, + NULL /* pvArg1 */, NULL /* pvArg2 */); + + if (rc == DDI_SUCCESS) + { + /* Add the low-level interrupt handler. */ + rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX, + (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */); + if (rc == DDI_SUCCESS) + { + /* Initialize the low-level mutex at the corresponding level. */ + mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, + DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX)); + + g_fSoftIntRegistered = true; + /* Enable the high-level interrupt. */ + rc = ddi_intr_enable(g_pahIntrs[0]); + if (rc == DDI_SUCCESS) + return rc; + + LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc)); + mutex_destroy(&g_IrqMtx); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc)); + + ddi_intr_remove_handler(g_pahIntrs[0]); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc)); + + mutex_destroy(&g_HighLevelIrqMtx); + } + else + { + /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */ + mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority)); + + /* Assign interrupt handler function to the interrupt handle. */ + rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR, + NULL /* pvArg1 */, NULL /* pvArg2 */); + if (rc == DDI_SUCCESS) + { + /* Enable the interrupt. */ + rc = ddi_intr_enable(g_pahIntrs[0]); + if (rc == DDI_SUCCESS) + return rc; + + LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc)); + mutex_destroy(&g_IrqMtx); + } + } + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc)); + + Assert(cIntrAllocated == 1); + ddi_intr_free(g_pahIntrs[0]); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount)); + RTMemFree(g_pahIntrs); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount)); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount)); + } + else + LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType)); + } + else + LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc)); + return rc; +} + + +/** + * Removes IRQ for VMMDev. + * + * @param pDip Pointer to the device info structure. + */ +static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip) +{ + LogFlow(("vgdrvSolarisRemoveIRQ:\n")); + + int rc = ddi_intr_disable(g_pahIntrs[0]); + if (rc == DDI_SUCCESS) + { + rc = ddi_intr_remove_handler(g_pahIntrs[0]); + if (rc == DDI_SUCCESS) + ddi_intr_free(g_pahIntrs[0]); + } + + if (g_fSoftIntRegistered) + { + ddi_intr_remove_softint(g_hSoftIntr); + mutex_destroy(&g_HighLevelIrqMtx); + g_fSoftIntRegistered = false; + } + + mutex_destroy(&g_IrqMtx); + RTMemFree(g_pahIntrs); +} + + +/** + * High-level Interrupt Service Routine for VMMDev. + * + * This routine simply dispatches a soft-interrupt at an acceptable IPL as + * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher) + * due to pollwakeup() in VGDrvNativeISRMousePollEvent(). + * + * @param Arg Private data (unused, will be NULL). + * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't. + */ +static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg) +{ + bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt); + if (fOurIrq) + { + ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */); + return DDI_INTR_CLAIMED; + } + return DDI_INTR_UNCLAIMED; +} + + +/** + * Interrupt Service Routine for VMMDev. + * + * @param Arg Private data (unused, will be NULL). + * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't. + */ +static uint_t vgdrvSolarisISR(caddr_t Arg) +{ + LogFlow(("vgdrvSolarisISR:\n")); + + /* The mutex is required to protect against parallel executions (if possible?) and also the + mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */ + mutex_enter(&g_IrqMtx); + bool fOurIRQ = VGDrvCommonISR(&g_DevExt); + mutex_exit(&g_IrqMtx); + + return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED; +} + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + LogFlow(("VGDrvNativeISRMousePollEvent:\n")); + + /* + * Wake up poll waiters. + */ + pollwakeup(&g_PollHead, POLLIN | POLLRDNORM); +} + + +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +/** + * Sets the mouse notification callback. + * + * @returns VBox status code. + * @param pDevExt Pointer to the device extension. + * @param pNotify Pointer to the mouse notify struct. + */ +int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify) +{ + /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */ + mutex_enter(&g_IrqMtx); + pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify; + pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser; + mutex_exit(&g_IrqMtx); + return VINF_SUCCESS; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf new file mode 100644 index 00000000..9f24a7b5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf @@ -0,0 +1,41 @@ +# $Id: VBoxGuest-solaris.conf $ +## @file +# OpenSolaris Guest Driver Configuration + +# +# Copyright (C) 2007-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# 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="vboxguest" parent="/pci@0,0/pci80ee,cafe" instance=0; diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp new file mode 100644 index 00000000..a7c72dcd --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp @@ -0,0 +1,3481 @@ +/* $Id: VBoxGuest-win.cpp $ */ +/** @file + * VBoxGuest - Windows specifics. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include <iprt/nt/nt.h> + +#include "VBoxGuestInternal.h" +#include <VBox/VBoxGuestLib.h> +#include <VBox/log.h> + +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/critsect.h> +#include <iprt/dbg.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/memobj.h> +#include <iprt/mem.h> +#include <iprt/mp.h> +#include <iprt/spinlock.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#ifdef TARGET_NT4 +# include <VBox/pci.h> +# define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_IPRT +# include <iprt/formats/mz.h> +# include <iprt/formats/pecoff.h> +extern "C" IMAGE_DOS_HEADER __ImageBase; +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#undef ExFreePool + +#ifndef PCI_MAX_BUSES +# define PCI_MAX_BUSES 256 +#endif + +/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */ +#define VBOX_CM_PRE_VISTA_MASK (0x3f) + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Possible device states for our state machine. + */ +typedef enum VGDRVNTDEVSTATE +{ + /** @name Stable states + * @{ */ + VGDRVNTDEVSTATE_REMOVED = 0, + VGDRVNTDEVSTATE_STOPPED, + VGDRVNTDEVSTATE_OPERATIONAL, + /** @} */ + + /** @name Transitional states + * @{ */ + VGDRVNTDEVSTATE_PENDINGSTOP, + VGDRVNTDEVSTATE_PENDINGREMOVE, + VGDRVNTDEVSTATE_SURPRISEREMOVED + /** @} */ +} VGDRVNTDEVSTATE; + + +/** + * Subclassing the device extension for adding windows-specific bits. + */ +typedef struct VBOXGUESTDEVEXTWIN +{ + /** The common device extension core. */ + VBOXGUESTDEVEXT Core; + + /** Our functional driver object. */ + PDEVICE_OBJECT pDeviceObject; + /** Top of the stack. */ + PDEVICE_OBJECT pNextLowerDriver; + + /** @name PCI bus and slot (device+function) set by for legacy NT only. + * @{ */ + /** Bus number where the device is located. */ + ULONG uBus; + /** Slot number where the device is located (PCI_SLOT_NUMBER). */ + ULONG uSlot; + /** @} */ + + /** @name Interrupt stuff. + * @{ */ + /** Interrupt object pointer. */ + PKINTERRUPT pInterruptObject; + /** Device interrupt level. */ + ULONG uInterruptLevel; + /** Device interrupt vector. */ + ULONG uInterruptVector; + /** Affinity mask. */ + KAFFINITY fInterruptAffinity; + /** LevelSensitive or Latched. */ + KINTERRUPT_MODE enmInterruptMode; + /** @} */ + + /** Physical address and length of VMMDev memory. */ + PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr; + /** Length of VMMDev memory. */ + ULONG cbVmmDevMemory; + + /** Device state. */ + VGDRVNTDEVSTATE volatile enmDevState; + /** The previous stable device state. */ + VGDRVNTDEVSTATE enmPrevDevState; + + /** Last system power action set (see VBoxGuestPower). */ + POWER_ACTION enmLastSystemPowerAction; + /** Preallocated generic request for shutdown. */ + VMMDevPowerStateRequest *pPowerStateRequest; + + /** Spinlock protecting MouseNotifyCallback. Required since the consumer is + * in a DPC callback and not the ISR. */ + KSPIN_LOCK MouseEventAccessSpinLock; + + /** Read/write critical section for handling race between checking for idle + * driver (in IRP_MN_QUERY_REMOVE_DEVICE & IRP_MN_QUERY_STOP_DEVICE) and + * creating new sessions. The session creation code enteres the critical + * section in read (shared) access mode, whereas the idle checking code + * enteres is in write (exclusive) access mode. */ + RTCRITSECTRW SessionCreateCritSect; +} VBOXGUESTDEVEXTWIN; +typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN; + + +/** NT (windows) version identifier. */ +typedef enum VGDRVNTVER +{ + VGDRVNTVER_INVALID = 0, + VGDRVNTVER_WINNT310, + VGDRVNTVER_WINNT350, + VGDRVNTVER_WINNT351, + VGDRVNTVER_WINNT4, + VGDRVNTVER_WIN2K, + VGDRVNTVER_WINXP, + VGDRVNTVER_WIN2K3, + VGDRVNTVER_WINVISTA, + VGDRVNTVER_WIN7, + VGDRVNTVER_WIN8, + VGDRVNTVER_WIN81, + VGDRVNTVER_WIN10, + VGDRVNTVER_WIN11 +} VGDRVNTVER; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +RT_C_DECLS_BEGIN +static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj); +static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj); +static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + PIRP pIrp, PIO_STACK_LOCATION pStack); +static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt); +static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS NTAPI vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer); +static VOID NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext); +static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext); +#ifdef VBOX_STRICT +static void vgdrvNtDoTests(void); +#endif +#ifdef TARGET_NT4 +static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot, + void *pvData, ULONG offData, ULONG cbData); +static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot, + void *pvData, ULONG offData, ULONG cbData); +#endif + +/* + * We only do INIT allocations. PAGE is too much work and risk for little gain. + */ +#ifdef ALLOC_PRAGMA +NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath); +# pragma alloc_text(INIT, DriverEntry) +# ifdef TARGET_NT4 +static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath); +# pragma alloc_text(INIT, vgdrvNt4CreateDevice) +static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber); +# pragma alloc_text(INIT, vgdrvNt4FindPciDevice) +# endif +#endif +RT_C_DECLS_END + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The detected NT (windows) version. */ +static VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID; +/** Pointer to the PoStartNextPowerIrp routine (in the NT kernel). + * Introduced in Windows 2000. */ +static decltype(PoStartNextPowerIrp) *g_pfnPoStartNextPowerIrp = NULL; +/** Pointer to the PoCallDriver routine (in the NT kernel). + * Introduced in Windows 2000. */ +static decltype(PoCallDriver) *g_pfnPoCallDriver = NULL; +#ifdef TARGET_NT4 +/** Pointer to the HalAssignSlotResources routine (in the HAL). + * Introduced in NT 3.50. */ +static decltype(HalAssignSlotResources) *g_pfnHalAssignSlotResources= NULL; +/** Pointer to the HalGetBusDataByOffset routine (in the HAL). + * Introduced in NT 3.50. */ +static decltype(HalGetBusDataByOffset) *g_pfnHalGetBusDataByOffset = NULL; +/** Pointer to the HalSetBusDataByOffset routine (in the HAL). + * Introduced in NT 3.50 (we provide fallback and use it only for NT 3.1). */ +static decltype(HalSetBusDataByOffset) *g_pfnHalSetBusDataByOffset = NULL; +#endif +/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel). + * Introduced in Windows 3.50. */ +static decltype(KeRegisterBugCheckCallback) *g_pfnKeRegisterBugCheckCallback = NULL; +/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel). + * Introduced in Windows 3.50. */ +static decltype(KeDeregisterBugCheckCallback) *g_pfnKeDeregisterBugCheckCallback = NULL; +/** Pointer to the KiBugCheckData array (in the NT kernel). + * Introduced in Windows 4. */ +static uintptr_t const *g_pauKiBugCheckData = NULL; +/** Set if the callback was successfully registered and needs deregistering. */ +static bool g_fBugCheckCallbackRegistered = false; +/** The bugcheck callback record. */ +static KBUGCHECK_CALLBACK_RECORD g_BugCheckCallbackRec; + + + +/** + * Driver entry point. + * + * @returns appropriate status code. + * @param pDrvObj Pointer to driver object. + * @param pRegPath Registry base path. + */ +NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) +{ + RT_NOREF1(pRegPath); +#ifdef TARGET_NT4 + /* + * Looks like NT 3.1 doesn't necessarily zero our uninitialized data segments + * (like ".bss"), at least not when loading at runtime, so do that. + */ + PIMAGE_DOS_HEADER pMzHdr = &__ImageBase; + PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)((uint8_t *)pMzHdr + pMzHdr->e_lfanew); + if ( pNtHdrs->Signature == IMAGE_NT_SIGNATURE + && pNtHdrs->FileHeader.NumberOfSections > 2 + && pNtHdrs->FileHeader.NumberOfSections < 64) + { + uint32_t iShdr = pNtHdrs->FileHeader.NumberOfSections; + uint32_t uRvaEnd = pNtHdrs->OptionalHeader.SizeOfImage; /* (may be changed to exclude tail sections) */ + PIMAGE_SECTION_HEADER paShdrs; + paShdrs = (PIMAGE_SECTION_HEADER)&pNtHdrs->OptionalHeader.DataDirectory[pNtHdrs->OptionalHeader.NumberOfRvaAndSizes]; + while (iShdr-- > 0) + { + if ( !(paShdrs[iShdr].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + && paShdrs[iShdr].VirtualAddress < uRvaEnd) + { + uint32_t const cbSection = uRvaEnd - paShdrs[iShdr].VirtualAddress; + uint32_t const offUninitialized = paShdrs[iShdr].SizeOfRawData; + //RTLogBackdoorPrintf("section #%u: rva=%#x size=%#x calcsize=%#x) rawsize=%#x\n", iShdr, + // paShdrs[iShdr].VirtualAddress, paShdrs[iShdr].Misc.VirtualSize, cbSection, offUninitialized); + if ( offUninitialized < cbSection + && (paShdrs[iShdr].Characteristics & IMAGE_SCN_MEM_WRITE)) + memset((uint8_t *)pMzHdr + paShdrs[iShdr].VirtualAddress + offUninitialized, 0, cbSection - offUninitialized); + uRvaEnd = paShdrs[iShdr].VirtualAddress; + } + } + } + else + RTLogBackdoorPrintf("VBoxGuest: Bad pNtHdrs=%p: %#x\n", pNtHdrs, pNtHdrs->Signature); +#endif + + /* + * Start by initializing IPRT. + */ + int rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed: %Rrc!\n", rc); + return STATUS_UNSUCCESSFUL; + } + VGDrvCommonInitLoggers(); + + LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__)); + + /* + * Check if the NT version is supported and initialize g_enmVGDrvNtVer. + */ + ULONG ulMajorVer; + ULONG ulMinorVer; + ULONG ulBuildNo; + BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL); + + /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log on the host. */ + RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo); + if (fCheckedBuild) + RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n"); + +#ifdef VBOX_STRICT + vgdrvNtDoTests(); +#endif + NTSTATUS rcNt = STATUS_SUCCESS; + switch (ulMajorVer) + { + case 10: + /* Windows 10 Preview builds starting with 9926. */ + g_enmVGDrvNtVer = VGDRVNTVER_WIN10; + /* Windows 11 Preview builds starting with 22000. */ + if (ulBuildNo >= 22000) + g_enmVGDrvNtVer = VGDRVNTVER_WIN11; + break; + case 6: /* Windows Vista or Windows 7 (based on minor ver) */ + switch (ulMinorVer) + { + case 0: /* Note: Also could be Windows 2008 Server! */ + g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA; + break; + case 1: /* Note: Also could be Windows 2008 Server R2! */ + g_enmVGDrvNtVer = VGDRVNTVER_WIN7; + break; + case 2: + g_enmVGDrvNtVer = VGDRVNTVER_WIN8; + break; + case 3: + g_enmVGDrvNtVer = VGDRVNTVER_WIN81; + break; + case 4: /* Windows 10 Preview builds. */ + default: + g_enmVGDrvNtVer = VGDRVNTVER_WIN10; + break; + } + break; + case 5: + switch (ulMinorVer) + { + default: + case 2: + g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3; + break; + case 1: + g_enmVGDrvNtVer = VGDRVNTVER_WINXP; + break; + case 0: + g_enmVGDrvNtVer = VGDRVNTVER_WIN2K; + break; + } + break; + case 4: + g_enmVGDrvNtVer = VGDRVNTVER_WINNT4; + break; + case 3: + if (ulMinorVer > 50) + g_enmVGDrvNtVer = VGDRVNTVER_WINNT351; + else if (ulMinorVer >= 50) + g_enmVGDrvNtVer = VGDRVNTVER_WINNT350; + else + g_enmVGDrvNtVer = VGDRVNTVER_WINNT310; + break; + default: + /* Major versions above 6 gets classified as windows 10. */ + if (ulMajorVer > 6) + g_enmVGDrvNtVer = VGDRVNTVER_WIN10; + else + { + RTLogBackdoorPrintf("At least Windows NT 3.10 required! Found %u.%u!\n", ulMajorVer, ulMinorVer); + rcNt = STATUS_DRIVER_UNABLE_TO_LOAD; + } + break; + } + if (NT_SUCCESS(rcNt)) + { + /* + * Dynamically resolve symbols not present in NT4. + */ + RTDBGKRNLINFO hKrnlInfo; + rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + g_pfnKeRegisterBugCheckCallback = (decltype(KeRegisterBugCheckCallback) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeRegisterBugCheckCallback"); + g_pfnKeDeregisterBugCheckCallback = (decltype(KeDeregisterBugCheckCallback) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeDeregisterBugCheckCallback"); + g_pauKiBugCheckData = (uintptr_t const *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KiBugCheckData"); + g_pfnPoCallDriver = (decltype(PoCallDriver) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoCallDriver"); + g_pfnPoStartNextPowerIrp = (decltype(PoStartNextPowerIrp) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoStartNextPowerIrp"); +#ifdef TARGET_NT4 + if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4) +#endif + { + if (!g_pfnPoCallDriver) { LogRelFunc(("Missing PoCallDriver!\n")); rc = VERR_SYMBOL_NOT_FOUND; } + if (!g_pfnPoStartNextPowerIrp) { LogRelFunc(("Missing PoStartNextPowerIrp!\n")); rc = VERR_SYMBOL_NOT_FOUND; } + } + +#ifdef TARGET_NT4 + g_pfnHalAssignSlotResources = (decltype(HalAssignSlotResources) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalAssignSlotResources"); + if (!g_pfnHalAssignSlotResources && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K) + { + RTLogBackdoorPrintf("VBoxGuest: Missing HalAssignSlotResources!\n"); + rc = VERR_SYMBOL_NOT_FOUND; + } + + g_pfnHalGetBusDataByOffset = (decltype(HalGetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalGetBusDataByOffset"); + if (!g_pfnHalGetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K) + { + RTLogBackdoorPrintf("VBoxGuest: Missing HalGetBusDataByOffset!\n"); + rc = VERR_SYMBOL_NOT_FOUND; + } + if (!g_pfnHalGetBusDataByOffset) + g_pfnHalGetBusDataByOffset = vgdrvNt31GetBusDataByOffset; + + g_pfnHalSetBusDataByOffset = (decltype(HalSetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalSetBusDataByOffset"); + if (!g_pfnHalSetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K) + { + RTLogBackdoorPrintf("VBoxGuest: Missing HalSetBusDataByOffset!\n"); + rc = VERR_SYMBOL_NOT_FOUND; + } + if (!g_pfnHalSetBusDataByOffset) + g_pfnHalSetBusDataByOffset = vgdrvNt31SetBusDataByOffset; +#endif + RTR0DbgKrnlInfoRelease(hKrnlInfo); + } + if (RT_SUCCESS(rc)) + { + /* + * Setup the driver entry points in pDrvObj. + */ + pDrvObj->DriverUnload = vgdrvNtUnload; + pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate; + pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose; + pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl; + pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl; + /** @todo Need to call IoRegisterShutdownNotification or + * IoRegisterLastChanceShutdownNotification, possibly hooking the + * HalReturnToFirmware import in NTOSKRNL on older systems (<= ~NT4) and + * check for power off requests. */ + pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown; + pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub; + pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub; +#ifdef TARGET_NT4 + if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4) + rcNt = vgdrvNt4CreateDevice(pDrvObj, pRegPath); + else +#endif + { + pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP; + pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower; + pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl; + pDrvObj->DriverExtension->AddDevice = vgdrvNtNt5PlusAddDevice; + } + if (NT_SUCCESS(rcNt)) + { + /* + * Try register the bugcheck callback (non-fatal). + */ + if ( g_pfnKeRegisterBugCheckCallback + && g_pfnKeDeregisterBugCheckCallback) + { + AssertCompile(BufferEmpty == 0); + KeInitializeCallbackRecord(&g_BugCheckCallbackRec); + if (g_pfnKeRegisterBugCheckCallback(&g_BugCheckCallbackRec, vgdrvNtBugCheckCallback, + NULL, 0, (PUCHAR)"VBoxGuest")) + g_fBugCheckCallbackRegistered = true; + else + g_fBugCheckCallbackRegistered = false; + } + else + Assert(g_pfnKeRegisterBugCheckCallback == NULL && g_pfnKeDeregisterBugCheckCallback == NULL); + + LogFlowFunc(("Returning %#x\n", rcNt)); + return rcNt; + } + } + else + rcNt = STATUS_PROCEDURE_NOT_FOUND; + } + + /* + * Failed. + */ + LogRelFunc(("Failed! rcNt=%#x\n", rcNt)); + VGDrvCommonDestroyLoggers(); + RTR0Term(); + return rcNt; +} + + +/** + * Translates our internal NT version enum to VBox OS. + * + * @returns VBox OS type. + * @param enmNtVer The NT version. + */ +static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer) +{ + VBOXOSTYPE enmOsType; + switch (enmNtVer) + { + case VGDRVNTVER_WINNT310: enmOsType = VBOXOSTYPE_WinNT3x; break; + case VGDRVNTVER_WINNT350: enmOsType = VBOXOSTYPE_WinNT3x; break; + case VGDRVNTVER_WINNT351: enmOsType = VBOXOSTYPE_WinNT3x; break; + case VGDRVNTVER_WINNT4: enmOsType = VBOXOSTYPE_WinNT4; break; + case VGDRVNTVER_WIN2K: enmOsType = VBOXOSTYPE_Win2k; break; + case VGDRVNTVER_WINXP: enmOsType = VBOXOSTYPE_WinXP; break; + case VGDRVNTVER_WIN2K3: enmOsType = VBOXOSTYPE_Win2k3; break; + case VGDRVNTVER_WINVISTA: enmOsType = VBOXOSTYPE_WinVista; break; + case VGDRVNTVER_WIN7: enmOsType = VBOXOSTYPE_Win7; break; + case VGDRVNTVER_WIN8: enmOsType = VBOXOSTYPE_Win8; break; + case VGDRVNTVER_WIN81: enmOsType = VBOXOSTYPE_Win81; break; + case VGDRVNTVER_WIN10: enmOsType = VBOXOSTYPE_Win10; break; + case VGDRVNTVER_WIN11: enmOsType = VBOXOSTYPE_Win11_x64; break; + + default: + /* We don't know, therefore NT family. */ + enmOsType = VBOXOSTYPE_WinNT; + break; + } +#if ARCH_BITS == 64 + enmOsType = (VBOXOSTYPE)((int)enmOsType | VBOXOSTYPE_x64); +#endif + return enmOsType; +} + + +/** + * Does the fundamental device extension initialization. + * + * @returns NT status. + * @param pDevExt The device extension. + * @param pDevObj The device object. + */ +static NTSTATUS vgdrvNtInitDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj) +{ + RT_ZERO(*pDevExt); + + KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock); + pDevExt->pDeviceObject = pDevObj; + pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED; + pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED; + + int rc = RTCritSectRwInit(&pDevExt->SessionCreateCritSect); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonInitDevExtFundament(&pDevExt->Core); + if (RT_SUCCESS(rc)) + { + LogFlow(("vgdrvNtInitDevExtFundament: returning success\n")); + return STATUS_SUCCESS; + } + + RTCritSectRwDelete(&pDevExt->SessionCreateCritSect); + } + Log(("vgdrvNtInitDevExtFundament: failed: rc=%Rrc\n", rc)); + return STATUS_UNSUCCESSFUL; +} + + +/** + * Counter part to vgdrvNtInitDevExtFundament. + * + * @param pDevExt The device extension. + */ +static void vgdrvNtDeleteDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt) +{ + LogFlow(("vgdrvNtDeleteDevExtFundament:\n")); + VGDrvCommonDeleteDevExtFundament(&pDevExt->Core); + RTCritSectRwDelete(&pDevExt->SessionCreateCritSect); +} + + +#ifdef LOG_ENABLED +/** + * Debug helper to dump a device resource list. + * + * @param pResourceList list of device resources. + */ +static void vgdrvNtShowDeviceResources(PCM_RESOURCE_LIST pRsrcList) +{ + for (uint32_t iList = 0; iList < pRsrcList->Count; iList++) + { + PCM_FULL_RESOURCE_DESCRIPTOR pList = &pRsrcList->List[iList]; + LogFunc(("List #%u: InterfaceType=%#x BusNumber=%#x ListCount=%u ListRev=%#x ListVer=%#x\n", + iList, pList->InterfaceType, pList->BusNumber, pList->PartialResourceList.Count, + pList->PartialResourceList.Revision, pList->PartialResourceList.Version )); + + PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pList->PartialResourceList.PartialDescriptors; + for (ULONG i = 0; i < pList->PartialResourceList.Count; ++i, ++pResource) + { + ULONG uType = pResource->Type; + static char const * const s_apszName[] = + { + "CmResourceTypeNull", + "CmResourceTypePort", + "CmResourceTypeInterrupt", + "CmResourceTypeMemory", + "CmResourceTypeDma", + "CmResourceTypeDeviceSpecific", + "CmResourceTypeuBusNumber", + "CmResourceTypeDevicePrivate", + "CmResourceTypeAssignedResource", + "CmResourceTypeSubAllocateFrom", + }; + + if (uType < RT_ELEMENTS(s_apszName)) + LogFunc((" %.30s Flags=%#x Share=%#x", s_apszName[uType], pResource->Flags, pResource->ShareDisposition)); + else + LogFunc((" Type=%#x Flags=%#x Share=%#x", uType, pResource->Flags, pResource->ShareDisposition)); + switch (uType) + { + case CmResourceTypePort: + case CmResourceTypeMemory: + Log((" Start %#RX64, length=%#x\n", pResource->u.Port.Start.QuadPart, pResource->u.Port.Length)); + break; + + case CmResourceTypeInterrupt: + Log((" Level=%X, vector=%#x, affinity=%#x\n", + pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity)); + break; + + case CmResourceTypeDma: + Log((" Channel %d, Port %#x\n", pResource->u.Dma.Channel, pResource->u.Dma.Port)); + break; + + default: + Log(("\n")); + break; + } + } + } +} +#endif /* LOG_ENABLED */ + + +/** + * Helper to scan the PCI resource list and remember stuff. + * + * @param pDevExt The device extension. + * @param pResList Resource list + * @param fTranslated Whether the addresses are translated or not. + */ +static NTSTATUS vgdrvNtScanPCIResourceList(PVBOXGUESTDEVEXTWIN pDevExt, PCM_RESOURCE_LIST pResList, bool fTranslated) +{ + LogFlowFunc(("Found %d resources\n", pResList->List->PartialResourceList.Count)); + PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL; + bool fGotIrq = false; + bool fGotMmio = false; + bool fGotIoPorts = false; + NTSTATUS rc = STATUS_SUCCESS; + for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++) + { + pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i]; + switch (pPartialData->Type) + { + case CmResourceTypePort: + LogFlowFunc(("I/O range: Base=%#RX64, length=%08x\n", + pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length)); + /* Save the first I/O port base. */ + if (!fGotIoPorts) + { + pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart; + fGotIoPorts = true; + LogFunc(("I/O range for VMMDev found! Base=%#RX64, length=%08x\n", + pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length)); + } + else + LogRelFunc(("More than one I/O port range?!?\n")); + break; + + case CmResourceTypeInterrupt: + LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n", + pPartialData->u.Interrupt.Level, pPartialData->u.Interrupt.Vector, pPartialData->Flags)); + if (!fGotIrq) + { + /* Save information. */ + pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level; + pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector; + pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity; + + /* Check interrupt mode. */ + if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED) + pDevExt->enmInterruptMode = Latched; + else + pDevExt->enmInterruptMode = LevelSensitive; + fGotIrq = true; + LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n", pDevExt->uInterruptVector, + pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode)); + } + else + LogFunc(("More than one IRQ resource!\n")); + break; + + case CmResourceTypeMemory: + LogFlowFunc(("Memory range: Base=%#RX64, length=%08x\n", + pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length)); + /* We only care about the first read/write memory range. */ + if ( !fGotMmio + && (pPartialData->Flags & CM_RESOURCE_MEMORY_WRITEABILITY_MASK) == CM_RESOURCE_MEMORY_READ_WRITE) + { + /* Save physical MMIO base + length for VMMDev. */ + pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start; + pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length; + + if (!fTranslated) + { + /* Technically we need to make the HAL translate the address. since we + didn't used to do this and it probably just returns the input address, + we allow ourselves to ignore failures. */ + ULONG uAddressSpace = 0; + PHYSICAL_ADDRESS PhysAddr = pPartialData->u.Memory.Start; + if (HalTranslateBusAddress(pResList->List->InterfaceType, pResList->List->BusNumber, PhysAddr, + &uAddressSpace, &PhysAddr)) + { + Log(("HalTranslateBusAddress(%#RX64) -> %RX64, type %#x\n", + pPartialData->u.Memory.Start.QuadPart, PhysAddr.QuadPart, uAddressSpace)); + if (pPartialData->u.Memory.Start.QuadPart != PhysAddr.QuadPart) + pDevExt->uVmmDevMemoryPhysAddr = PhysAddr; + } + else + Log(("HalTranslateBusAddress(%#RX64) -> failed!\n", pPartialData->u.Memory.Start.QuadPart)); + } + + fGotMmio = true; + LogFunc(("Found memory range for VMMDev! Base = %#RX64, Length = %08x\n", + pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length)); + } + else + LogFunc(("Ignoring memory: Flags=%08x Base=%#RX64\n", + pPartialData->Flags, pPartialData->u.Memory.Start.QuadPart)); + break; + + default: + LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type)); + break; + } + } + return rc; +} + + +#ifdef TARGET_NT4 + +/** + * Scans the PCI resources on NT 3.1. + * + * @returns STATUS_SUCCESS or STATUS_DEVICE_CONFIGURATION_ERROR. + * @param pDevExt The device extension. + * @param uBus The bus number. + * @param uSlot The PCI slot to scan. + */ +static NTSTATUS vgdrvNt31ScanSlotResources(PVBOXGUESTDEVEXTWIN pDevExt, ULONG uBus, ULONG uSlot) +{ + /* + * Disable memory mappings so we can determin the BAR lengths + * without upsetting other mappings. + */ + uint16_t fCmd = 0; + g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd)); + if (fCmd & VBOX_PCI_COMMAND_MEMORY) + { + uint16_t fCmdTmp = fCmd & ~VBOX_PCI_COMMAND_MEMORY; + g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdTmp, VBOX_PCI_COMMAND, sizeof(fCmdTmp)); + } + + /* + * Scan the address resources first. + */ + uint32_t aBars[6] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX }; + g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &aBars, VBOX_PCI_BASE_ADDRESS_0, sizeof(aBars)); + + bool fGotMmio = false; + bool fGotIoPorts = false; + for (uint32_t i = 0; i < RT_ELEMENTS(aBars); i++) + { + uint32_t uBar = aBars[i]; + if (uBar == UINT32_MAX) + continue; + if ((uBar & 1) == PCI_ADDRESS_SPACE_IO) + { + uint32_t uAddr = uBar & UINT32_C(0xfffffffc); + if (!uAddr) + continue; + if (!fGotIoPorts) + { + pDevExt->Core.IOPortBase = (uint16_t)uAddr & UINT16_C(0xfffc); + fGotIoPorts = true; + LogFunc(("I/O range for VMMDev found in BAR%u! %#x\n", i, pDevExt->Core.IOPortBase)); + } + else + LogRelFunc(("More than one I/O port range?!? BAR%u=%#x\n", i, uBar)); + } + else + { + uint32_t uAddr = uBar & UINT32_C(0xfffffff0); + if (!uAddr) + continue; + + if (!fGotMmio) + { + /* Figure the length by trying to set all address bits and seeing + how many we're allowed to set. */ + uint32_t iBit = 4; + while (!(uAddr & RT_BIT_32(iBit))) + iBit++; + + uint32_t const offPciBar = VBOX_PCI_BASE_ADDRESS_0 + i * 4; + uint32_t uTmpBar = uBar | ((RT_BIT_32(iBit) - 1) & UINT32_C(0xfffffff0)); + g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar)); + uTmpBar = uBar; + g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar)); + g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uBar, offPciBar, sizeof(uBar)); + + while (iBit > 4 && (uTmpBar & RT_BIT_32(iBit - 1))) + iBit--; + + /* got it */ + pDevExt->cbVmmDevMemory = RT_BIT_32(iBit); + pDevExt->uVmmDevMemoryPhysAddr.QuadPart = uAddr; + fGotMmio = true; + LogFunc(("Found memory range for VMMDev in BAR%u! %#RX64 LB %#x (raw %#x)\n", + i, pDevExt->uVmmDevMemoryPhysAddr.QuadPart, pDevExt->cbVmmDevMemory, uBar)); + } + else + LogFunc(("Ignoring memory: BAR%u=%#x\n", i, uBar)); + } + } + + /* + * Get the IRQ + */ + struct + { + uint8_t bInterruptLine; + uint8_t bInterruptPin; + } Buf = { 0, 0 }; + g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &Buf, VBOX_PCI_INTERRUPT_LINE, sizeof(Buf)); + if (Buf.bInterruptPin != 0) + { + pDevExt->uInterruptVector = Buf.bInterruptLine; + pDevExt->uInterruptLevel = Buf.bInterruptLine; + pDevExt->enmInterruptMode = LevelSensitive; + pDevExt->fInterruptAffinity = RT_BIT_32(RTMpGetCount()) - 1; + LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n", + pDevExt->uInterruptVector, pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode)); + } + + /* + * Got what we need? + */ + if (fGotIoPorts && (!fGotMmio || Buf.bInterruptPin != 0)) + { + /* + * Enable both MMIO, I/O space and busmastering so we can use the device. + */ + uint16_t fCmdNew = fCmd | VBOX_PCI_COMMAND_IO | VBOX_PCI_COMMAND_MEMORY | VBOX_PCI_COMMAND_MASTER; + g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdNew, VBOX_PCI_COMMAND, sizeof(fCmdNew)); + + return STATUS_SUCCESS; + } + + /* No. Complain, restore device command value and return failure. */ + if (!fGotIoPorts) + LogRel(("VBoxGuest: Did not find I/O port range: %#x %#x %#x %#x %#x %#x\n", + aBars[0], aBars[1], aBars[2], aBars[3], aBars[4], aBars[5])); + if (!fGotMmio || Buf.bInterruptPin != 0) + LogRel(("VBoxGuest: Got MMIO but no interrupts!\n")); + + g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd)); + return STATUS_DEVICE_CONFIGURATION_ERROR; +} + +#endif /* TARGET_NT4 */ + +/** + * Unmaps the VMMDev I/O range from kernel space. + * + * @param pDevExt The device extension. + */ +static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt) +{ + LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory)); + if (pDevExt->Core.pVMMDevMemory) + { + MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory); + pDevExt->Core.pVMMDevMemory = NULL; + } + + pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0; + pDevExt->cbVmmDevMemory = 0; +} + + +/** + * Maps the I/O space from VMMDev to virtual kernel address space. + * + * @return NTSTATUS + * + * @param pDevExt The device extension. + * @param PhysAddr Physical address to map. + * @param cbToMap Number of bytes to map. + * @param ppvMMIOBase Pointer of mapped I/O base. + * @param pcbMMIO Length of mapped I/O base. + */ +static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap, + void **ppvMMIOBase, uint32_t *pcbMMIO) +{ + AssertPtrReturn(pDevExt, VERR_INVALID_POINTER); + AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER); + /* pcbMMIO is optional. */ + + NTSTATUS rc = STATUS_SUCCESS; + if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */ + { + VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached); + LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory)); + if (pVMMDevMemory) + { + LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size)); + + /* Check version of the structure; do we have the right memory version? */ + if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION) + { + /* Save results. */ + *ppvMMIOBase = pVMMDevMemory; + if (pcbMMIO) /* Optional. */ + *pcbMMIO = pVMMDevMemory->u32Size; + + LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase)); + } + else + { + /* Not our version, refuse operation and unmap the memory. */ + LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version)); + + vgdrvNtUnmapVMMDevMemory(pDevExt); + rc = STATUS_UNSUCCESSFUL; + } + } + else + rc = STATUS_UNSUCCESSFUL; + } + return rc; +} + + +/** + * Sets up the device and its resources. + * + * @param pDevExt Our device extension data. + * @param pDevObj The device object. + * @param pIrp The request packet if NT5+, NULL for NT4 and earlier. + * @param pDrvObj The driver object for NT4, NULL for NT5+. + * @param pRegPath The registry path for NT4, NULL for NT5+. + */ +static NTSTATUS vgdrvNtSetupDevice(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj, + PIRP pIrp, PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) +{ + LogFlowFunc(("ENTER: pDevExt=%p pDevObj=%p pIrq=%p pDrvObj=%p pRegPath=%p\n", pDevExt, pDevObj, pIrp, pDrvObj, pRegPath)); + + NTSTATUS rcNt; + if (!pIrp) + { +#ifdef TARGET_NT4 + /* + * NT4, NT3.x: Let's have a look at what our PCI adapter offers. + */ + LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n")); + + /* Assign the PCI resources. */ + UNICODE_STRING ClassName; + RtlInitUnicodeString(&ClassName, L"VBoxGuestAdapter"); + PCM_RESOURCE_LIST pResourceList = NULL; + if (g_pfnHalAssignSlotResources) + { + rcNt = g_pfnHalAssignSlotResources(pRegPath, &ClassName, pDrvObj, pDevObj, PCIBus, pDevExt->uBus, pDevExt->uSlot, + &pResourceList); +# ifdef LOG_ENABLED + if (pResourceList) + vgdrvNtShowDeviceResources(pResourceList); +# endif + if (NT_SUCCESS(rcNt)) + { + rcNt = vgdrvNtScanPCIResourceList(pDevExt, pResourceList, false /*fTranslated*/); + ExFreePool(pResourceList); + } + } + else + rcNt = vgdrvNt31ScanSlotResources(pDevExt, pDevExt->uBus, pDevExt->uSlot); + +# else /* !TARGET_NT4 */ + AssertFailed(); + RT_NOREF(pDevObj, pDrvObj, pRegPath); + rcNt = STATUS_INTERNAL_ERROR; +# endif /* !TARGET_NT4 */ + } + else + { + /* + * NT5+: Scan the PCI resource list from the IRP. + */ + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); +# ifdef LOG_ENABLED + vgdrvNtShowDeviceResources(pStack->Parameters.StartDevice.AllocatedResourcesTranslated); +# endif + rcNt = vgdrvNtScanPCIResourceList(pDevExt, pStack->Parameters.StartDevice.AllocatedResourcesTranslated, + true /*fTranslated*/); + } + if (NT_SUCCESS(rcNt)) + { + /* + * Map physical address of VMMDev memory into MMIO region + * and init the common device extension bits. + */ + void *pvMMIOBase = NULL; + uint32_t cbMMIO = 0; + rcNt = vgdrvNtMapVMMDevMemory(pDevExt, + pDevExt->uVmmDevMemoryPhysAddr, + pDevExt->cbVmmDevMemory, + &pvMMIOBase, + &cbMMIO); + if (NT_SUCCESS(rcNt)) + { + pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase; + + LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n", + pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL)); + + int vrc = VGDrvCommonInitDevExtResources(&pDevExt->Core, + pDevExt->Core.IOPortBase, + pvMMIOBase, cbMMIO, + vgdrvNtVersionToOSType(g_enmVGDrvNtVer), + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(vrc)) + { + + vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest, + sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus); + if (RT_SUCCESS(vrc)) + { + /* + * Register DPC and ISR. + */ + LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject)); + IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler); + + ULONG uInterruptVector = pDevExt->uInterruptVector; + KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel; +#ifdef TARGET_NT4 + if (!pIrp) + { + /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */ + if ( uInterruptVector + || pDevExt->uInterruptLevel) + { + LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n", + pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector)); + uInterruptVector = HalGetInterruptVector(g_enmVGDrvNtVer == VGDRVNTVER_WINNT310 ? Isa : PCIBus, + pDevExt->uBus, + pDevExt->uInterruptLevel, + pDevExt->uInterruptVector, + &uHandlerIrql, + &pDevExt->fInterruptAffinity); + LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector)); + } + else + LogFunc(("Device does not provide an interrupt!\n")); + } +#endif + if (uInterruptVector) + { + LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n", + uInterruptVector, uHandlerIrql)); + + rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */ + vgdrvNtIsrHandler, /* Our ISR handler. */ + pDevExt, /* Device context. */ + NULL, /* Optional spinlock. */ + uInterruptVector, /* Interrupt vector. */ + uHandlerIrql, /* Irql. */ + uHandlerIrql, /* SynchronizeIrql. */ + pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */ + TRUE, /* Shareable interrupt. */ + pDevExt->fInterruptAffinity, /* CPU affinity. */ + FALSE); /* Don't save FPU stack. */ + if (NT_ERROR(rcNt)) + LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt)); + } + else + LogFunc(("No interrupt vector found!\n")); + if (NT_SUCCESS(rcNt)) + { + /* + * Once we've read configuration from register and host, we're finally read. + */ + /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */ + pDevExt->Core.fLoggingEnabled = true; + vgdrvNtReadConfiguration(pDevExt); + + /* Ready to rumble! */ + LogRelFunc(("Device is ready!\n")); + pDevExt->enmDevState = VGDRVNTDEVSTATE_OPERATIONAL; + pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_OPERATIONAL; + return STATUS_SUCCESS; + } + + pDevExt->pInterruptObject = NULL; + + VbglR0GRFree(&pDevExt->pPowerStateRequest->header); + pDevExt->pPowerStateRequest = NULL; + } + else + { + LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc)); + rcNt = STATUS_UNSUCCESSFUL; + } + + VGDrvCommonDeleteDevExtResources(&pDevExt->Core); + } + else + { + LogFunc(("Could not init device extension resources: vrc=%Rrc\n", vrc)); + rcNt = STATUS_DEVICE_CONFIGURATION_ERROR; + } + vgdrvNtUnmapVMMDevMemory(pDevExt); + } + else + LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt)); + } + + LogFunc(("Returned with rcNt=%#x\n", rcNt)); + return rcNt; +} + + + + +#ifdef TARGET_NT4 +# define PCI_CFG_ADDR 0xcf8 +# define PCI_CFG_DATA 0xcfc + +/** + * NT 3.1 doesn't do PCI nor HalSetBusDataByOffset, this is our fallback. + */ +static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot, + void *pvData, ULONG offData, ULONG cbData) +{ + /* + * Validate input a little bit. + */ + RT_NOREF(enmBusDataType); + Assert(idxBus <= 255); + Assert(uSlot <= 255); + Assert(offData <= 255); + Assert(cbData > 0); + + PCI_SLOT_NUMBER PciSlot; + PciSlot.u.AsULONG = uSlot; + uint32_t const idxAddrTop = UINT32_C(0x80000000) + | (idxBus << 16) + | (PciSlot.u.bits.DeviceNumber << 11) + | (PciSlot.u.bits.FunctionNumber << 8); + + /* + * Write the given bytes. + */ + uint8_t const *pbData = (uint8_t const *)pvData; + uint32_t off = offData; + uint32_t cbRet = 0; + + /* Unaligned start. */ + if (off & 3) + { + ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3)); + switch (off & 3) + { + case 1: + ASMOutU8(PCI_CFG_DATA + 1, pbData[cbRet++]); + if (cbRet >= cbData) + break; + RT_FALL_THRU(); + case 2: + ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet++]); + if (cbRet >= cbData) + break; + RT_FALL_THRU(); + case 3: + ASMOutU8(PCI_CFG_DATA + 3, pbData[cbRet++]); + break; + } + off = (off | 3) + 1; + } + + /* Bulk. */ + while (off < 256 && cbRet < cbData) + { + ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off); + switch (cbData - cbRet) + { + case 1: + ASMOutU8(PCI_CFG_DATA, pbData[cbRet]); + cbRet += 1; + break; + case 2: + ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1])); + cbRet += 2; + break; + case 3: + ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1])); + ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet + 2]); + cbRet += 3; + break; + default: + ASMOutU32(PCI_CFG_DATA, RT_MAKE_U32_FROM_U8(pbData[cbRet], pbData[cbRet + 1], + pbData[cbRet + 2], pbData[cbRet + 3])); + cbRet += 4; + break; + } + off += 4; + } + + return cbRet; +} + + +/** + * NT 3.1 doesn't do PCI nor HalGetBusDataByOffset, this is our fallback. + */ +static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot, + void *pvData, ULONG offData, ULONG cbData) +{ + /* + * Validate input a little bit. + */ + RT_NOREF(enmBusDataType); + Assert(idxBus <= 255); + Assert(uSlot <= 255); + Assert(offData <= 255); + Assert(cbData > 0); + + PCI_SLOT_NUMBER PciSlot; + PciSlot.u.AsULONG = uSlot; + uint32_t const idxAddrTop = UINT32_C(0x80000000) + | (idxBus << 16) + | (PciSlot.u.bits.DeviceNumber << 11) + | (PciSlot.u.bits.FunctionNumber << 8); + + /* + * Read the header type. + */ + ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (VBOX_PCI_HEADER_TYPE & ~3)); + uint8_t bHdrType = ASMInU8(PCI_CFG_DATA + (VBOX_PCI_HEADER_TYPE & 3)); + if (bHdrType == 0xff) + return idxBus < 8 ? 2 : 0; /* No device here */ + if ( offData == VBOX_PCI_HEADER_TYPE + && cbData == 1) + { + *(uint8_t *)pvData = bHdrType; + /*Log("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %02x\n", idxAddrTop, offData, bHdrType);*/ + return 1; + } + + /* + * Read the requested bytes. + */ + uint8_t *pbData = (uint8_t *)pvData; + uint32_t off = offData; + uint32_t cbRet = 0; + + /* Unaligned start. */ + if (off & 3) + { + ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3)); + uint32_t uValue = ASMInU32(PCI_CFG_DATA); + switch (off & 3) + { + case 1: + pbData[cbRet++] = (uint8_t)(uValue >> 8); + if (cbRet >= cbData) + break; + RT_FALL_THRU(); + case 2: + pbData[cbRet++] = (uint8_t)(uValue >> 16); + if (cbRet >= cbData) + break; + RT_FALL_THRU(); + case 3: + pbData[cbRet++] = (uint8_t)(uValue >> 24); + break; + } + off = (off | 3) + 1; + } + + /* Bulk. */ + while (off < 256 && cbRet < cbData) + { + ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off); + uint32_t uValue = ASMInU32(PCI_CFG_DATA); + switch (cbData - cbRet) + { + case 1: + pbData[cbRet++] = (uint8_t)uValue; + break; + case 2: + pbData[cbRet++] = (uint8_t)uValue; + pbData[cbRet++] = (uint8_t)(uValue >> 8); + break; + case 3: + pbData[cbRet++] = (uint8_t)uValue; + pbData[cbRet++] = (uint8_t)(uValue >> 8); + pbData[cbRet++] = (uint8_t)(uValue >> 16); + break; + default: + pbData[cbRet++] = (uint8_t)uValue; + pbData[cbRet++] = (uint8_t)(uValue >> 8); + pbData[cbRet++] = (uint8_t)(uValue >> 16); + pbData[cbRet++] = (uint8_t)(uValue >> 24); + break; + } + off += 4; + } + + Log(("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %.*Rhxs\n", idxAddrTop, offData, cbRet, pvData)); + return cbRet; +} + + +/** + * Helper function to handle the PCI device lookup. + * + * @returns NT status code. + * + * @param puBus Where to return the bus number on success. + * @param pSlot Where to return the slot number on success. + */ +static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot) +{ + Log(("vgdrvNt4FindPciDevice\n")); + + PCI_SLOT_NUMBER Slot; + Slot.u.AsULONG = 0; + + /* Scan each bus. */ + for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++) + { + /* Scan each device. */ + for (ULONG idxDevice = 0; idxDevice < PCI_MAX_DEVICES; idxDevice++) + { + Slot.u.bits.DeviceNumber = idxDevice; + Slot.u.bits.FunctionNumber = 0; + + /* Check the device header. */ + uint8_t bHeaderType = 0xff; + ULONG cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, + &bHeaderType, VBOX_PCI_HEADER_TYPE, sizeof(bHeaderType)); + if (cbRet == 0) + break; + if (cbRet == 2 || bHeaderType == 0xff) + continue; + + /* Scan functions. */ + uint32_t const cFunctionStep = bHeaderType & 0x80 ? 1 : 8; + Log(("vgdrvNt4FindPciDevice: %#x:%#x cFunctionStep=%d bHeaderType=%#x\n", uBus, idxDevice, cFunctionStep, bHeaderType)); + for (ULONG idxFunction = 0; idxFunction < PCI_MAX_FUNCTION; idxFunction += cFunctionStep) + { + Slot.u.bits.FunctionNumber = idxFunction; + + /* Read the vendor and device IDs of this device and compare with the VMMDev. */ + struct + { + uint16_t idVendor; + uint16_t idDevice; + } Buf = { PCI_INVALID_VENDORID, PCI_INVALID_VENDORID }; + cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, &Buf, VBOX_PCI_VENDOR_ID, sizeof(Buf)); + if ( cbRet == sizeof(Buf) + && Buf.idVendor == VMMDEV_VENDORID + && Buf.idDevice == VMMDEV_DEVICEID) + { + /* Hooray, we've found it! */ + Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n", + uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved)); + + *puBus = uBus; + *pSlot = Slot; + return STATUS_SUCCESS; + } + } + } + } + + return STATUS_DEVICE_DOES_NOT_EXIST; +} + + +/** + * Legacy helper function to create the device object. + * + * @returns NT status code. + * + * @param pDrvObj The driver object. + * @param pRegPath The driver registry path. + */ +static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) +{ + Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath)); + + /* + * Find our virtual PCI device + */ + ULONG uBus; + PCI_SLOT_NUMBER uSlot; + NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot); + if (NT_ERROR(rc)) + { + Log(("vgdrvNt4CreateDevice: Device not found!\n")); + return rc; + } + + /* + * Create device. + */ + UNICODE_STRING DevName; + RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT); + PDEVICE_OBJECT pDeviceObject = NULL; + rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); + if (NT_SUCCESS(rc)) + { + Log(("vgdrvNt4CreateDevice: Device created\n")); + + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + rc = IoCreateSymbolicLink(&DosName, &DevName); + if (NT_SUCCESS(rc)) + { + Log(("vgdrvNt4CreateDevice: Symlink created\n")); + + /* + * Setup the device extension. + */ + Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n")); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension; + int vrc = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject); + if (RT_SUCCESS(vrc)) + { + /* Store bus and slot number we've queried before. */ + pDevExt->uBus = uBus; + pDevExt->uSlot = uSlot.u.AsULONG; + + Log(("vgdrvNt4CreateDevice: Device extension created\n")); + + /* Do the actual VBox init ... */ + rc = vgdrvNtSetupDevice(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath); + if (NT_SUCCESS(rc)) + { + Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (success)\n", rc)); + return rc; + } + + /* bail out */ + vgdrvNtDeleteDevExtFundament(pDevExt); + } + IoDeleteSymbolicLink(&DosName); + } + else + Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc)); + IoDeleteDevice(pDeviceObject); + } + else + Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc)); + Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc)); + return rc; +} + +#endif /* TARGET_NT4 */ + +/** + * Handle request from the Plug & Play subsystem. + * + * @returns NT status code + * @param pDrvObj Driver object + * @param pDevObj Device object + * + * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp. + */ +static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj) +{ + LogFlowFuncEnter(); + + /* + * Create device. + */ + UNICODE_STRING DevName; + RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT); + PDEVICE_OBJECT pDeviceObject = NULL; + NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); + if (NT_SUCCESS(rcNt)) + { + /* + * Create symbolic link (DOS devices). + */ + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + rcNt = IoCreateSymbolicLink(&DosName, &DevName); + if (NT_SUCCESS(rcNt)) + { + /* + * Setup the device extension. + */ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension; + rcNt = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject); + if (NT_SUCCESS(rcNt)) + { + pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj); + if (pDevExt->pNextLowerDriver != NULL) + { + /* Ensure we are not called at elevated IRQL, even if our code isn't pagable any more. */ + pDeviceObject->Flags |= DO_POWER_PAGABLE; + + /* Driver is ready now. */ + pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + LogFlowFunc(("Returning with rcNt=%#x (success)\n", rcNt)); + return rcNt; + } + LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n")); + rcNt = STATUS_DEVICE_NOT_CONNECTED; + vgdrvNtDeleteDevExtFundament(pDevExt); + } + + IoDeleteSymbolicLink(&DosName); + } + else + LogFunc(("IoCreateSymbolicLink failed with rcNt=%#x!\n", rcNt)); + IoDeleteDevice(pDeviceObject); + } + else + LogFunc(("IoCreateDevice failed with rcNt=%#x!\n", rcNt)); + + LogFunc(("Returning with rcNt=%#x\n", rcNt)); + return rcNt; +} + + +/** + * Irp completion routine for PnP Irps we send. + * + * @returns NT status code. + * @param pDevObj Device object. + * @param pIrp Request packet. + * @param pEvent Semaphore. + */ +static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent) +{ + RT_NOREF2(pDevObj, pIrp); + KeSetEvent(pEvent, 0, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +/** + * Helper to send a PnP IRP and wait until it's done. + * + * @returns NT status code. + * @param pDevObj Device object. + * @param pIrp Request packet. + * @param fStrict When set, returns an error if the IRP gives an error. + */ +static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict) +{ + KEVENT Event; + + KeInitializeEvent(&Event, SynchronizationEvent, FALSE); + + IoCopyCurrentIrpStackLocationToNext(pIrp); + IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE); + + NTSTATUS rcNt = IoCallDriver(pDevObj, pIrp); + if (rcNt == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + rcNt = pIrp->IoStatus.Status; + } + + if ( !fStrict + && (rcNt == STATUS_NOT_SUPPORTED || rcNt == STATUS_INVALID_DEVICE_REQUEST)) + { + rcNt = STATUS_SUCCESS; + } + + Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning %#x\n", rcNt)); + return rcNt; +} + + +/** + * Deletes the device hardware resources. + * + * Used during removal, stopping and legacy module unloading. + * + * @param pDevExt The device extension. + */ +static void vgdrvNtDeleteDeviceResources(PVBOXGUESTDEVEXTWIN pDevExt) +{ + if (pDevExt->pInterruptObject) + { + IoDisconnectInterrupt(pDevExt->pInterruptObject); + pDevExt->pInterruptObject = NULL; + } + pDevExt->pPowerStateRequest = NULL; /* Will be deleted by the following call. */ + if (pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES) + VGDrvCommonDeleteDevExtResources(&pDevExt->Core); + vgdrvNtUnmapVMMDevMemory(pDevExt); +} + + +/** + * Deletes the device extension fundament and unlinks the device + * + * Used during removal and legacy module unloading. Must have called + * vgdrvNtDeleteDeviceResources. + * + * @param pDevObj Device object. + * @param pDevExt The device extension. + */ +static void vgdrvNtDeleteDeviceFundamentAndUnlink(PDEVICE_OBJECT pDevObj, PVBOXGUESTDEVEXTWIN pDevExt) +{ + /* + * Delete the remainder of the device extension. + */ + vgdrvNtDeleteDevExtFundament(pDevExt); + + /* + * Delete the DOS symlink to the device and finally the device itself. + */ + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + IoDeleteSymbolicLink(&DosName); + + Log(("vgdrvNtDeleteDeviceFundamentAndUnlink: Deleting device ...\n")); + IoDeleteDevice(pDevObj); +} + + +/** + * Checks if the device is idle. + * @returns STATUS_SUCCESS if idle, STATUS_UNSUCCESSFUL if busy. + * @param pDevExt The device extension. + * @param pszQueryNm The query name. + */ +static NTSTATUS vgdrvNtCheckIdle(PVBOXGUESTDEVEXTWIN pDevExt, const char *pszQueryNm) +{ + uint32_t cSessions = pDevExt->Core.cSessions; + if (cSessions == 0) + return STATUS_SUCCESS; + LogRel(("vgdrvNtCheckIdle/%s: cSessions=%d\n", pszQueryNm, cSessions)); + return STATUS_UNSUCCESSFUL; +} + + +/** + * PnP Request handler. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + +#ifdef LOG_ENABLED + static char const * const s_apszFnctName[] = + { + "IRP_MN_START_DEVICE", + "IRP_MN_QUERY_REMOVE_DEVICE", + "IRP_MN_REMOVE_DEVICE", + "IRP_MN_CANCEL_REMOVE_DEVICE", + "IRP_MN_STOP_DEVICE", + "IRP_MN_QUERY_STOP_DEVICE", + "IRP_MN_CANCEL_STOP_DEVICE", + "IRP_MN_QUERY_DEVICE_RELATIONS", + "IRP_MN_QUERY_INTERFACE", + "IRP_MN_QUERY_CAPABILITIES", + "IRP_MN_QUERY_RESOURCES", + "IRP_MN_QUERY_RESOURCE_REQUIREMENTS", + "IRP_MN_QUERY_DEVICE_TEXT", + "IRP_MN_FILTER_RESOURCE_REQUIREMENTS", + "IRP_MN_0xE", + "IRP_MN_READ_CONFIG", + "IRP_MN_WRITE_CONFIG", + "IRP_MN_EJECT", + "IRP_MN_SET_LOCK", + "IRP_MN_QUERY_ID", + "IRP_MN_QUERY_PNP_DEVICE_STATE", + "IRP_MN_QUERY_BUS_INFORMATION", + "IRP_MN_DEVICE_USAGE_NOTIFICATION", + "IRP_MN_SURPRISE_REMOVAL", + }; + Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n", + pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown")); +#endif + + NTSTATUS rc = STATUS_SUCCESS; + uint8_t bMinorFunction = pStack->MinorFunction; + switch (bMinorFunction) + { + case IRP_MN_START_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n")); + + /* This must be handled first by the lower driver. */ + rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); + if ( NT_SUCCESS(rc) + && NT_SUCCESS(pIrp->IoStatus.Status)) + { + Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n", + pStack->Parameters.StartDevice.AllocatedResources)); + if (pStack->Parameters.StartDevice.AllocatedResources) + { + rc = vgdrvNtSetupDevice(pDevExt, pDevObj, pIrp, NULL, NULL); + if (NT_SUCCESS(rc)) + Log(("vgdrvNtNt5PlusPnP: START_DEVICE: success\n")); + else + Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNtSetupDevice failed: %#x\n", rc)); + } + else + { + Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n", + pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL)); + rc = STATUS_UNSUCCESSFUL; + } + } + else + Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNt5PlusPnPSendIrpSynchronously failed: %#x + %#x\n", + rc, pIrp->IoStatus.Status)); + + pIrp->IoStatus.Status = rc; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rc; + } + + + /* + * Sent before removing the device and/or driver. + */ + case IRP_MN_QUERY_REMOVE_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n")); + + RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect); +#ifdef VBOX_REBOOT_ON_UNINSTALL + Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n")); + rc = STATUS_UNSUCCESSFUL; +#endif + if (NT_SUCCESS(rc)) + rc = vgdrvNtCheckIdle(pDevExt, "QUERY_REMOVE_DEVICE"); + if (NT_SUCCESS(rc)) + { + pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGREMOVE; + RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect); + + /* This IRP passed down to lower driver. */ + pIrp->IoStatus.Status = STATUS_SUCCESS; + + IoSkipCurrentIrpStackLocation(pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + + /* We must not do anything the IRP after doing IoSkip & CallDriver + since the driver below us will complete (or already have completed) the IRP. + I.e. just return the status we got from IoCallDriver */ + } + else + { + RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect); + pIrp->IoStatus.Status = rc; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Returning with rc = 0x%x\n", rc)); + return rc; + } + + /* + * Cancels a pending remove, IRP_MN_QUERY_REMOVE_DEVICE. + * We only have to revert the state. + */ + case IRP_MN_CANCEL_REMOVE_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n")); + + /* This must be handled first by the lower driver. */ + rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); + if ( NT_SUCCESS(rc) + && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE) + { + /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */ + pDevExt->enmDevState = pDevExt->enmPrevDevState; + } + + /* Complete the IRP. */ + pIrp->IoStatus.Status = rc; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rc; + } + + /* + * We do nothing here actually, esp. since this request is not expected for VBoxGuest. + * The cleanup will be done in IRP_MN_REMOVE_DEVICE, which follows this call. + */ + case IRP_MN_SURPRISE_REMOVAL: + { + Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n")); + pDevExt->enmDevState = VGDRVNTDEVSTATE_SURPRISEREMOVED; + LogRel(("VBoxGuest: unexpected device removal\n")); + + /* Pass to the lower driver. */ + pIrp->IoStatus.Status = STATUS_SUCCESS; + + IoSkipCurrentIrpStackLocation(pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + + /* Do not complete the IRP. */ + return rc; + } + + /* + * Device and/or driver removal. Destroy everything. + */ + case IRP_MN_REMOVE_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n")); + pDevExt->enmDevState = VGDRVNTDEVSTATE_REMOVED; + + /* + * Disconnect interrupts and delete all hardware resources. + * Note! This may already have been done if we're STOPPED already, if that's a possibility. + */ + vgdrvNtDeleteDeviceResources(pDevExt); + + /* + * We need to send the remove down the stack before we detach, but we don't need + * to wait for the completion of this operation (nor register a completion routine). + */ + pIrp->IoStatus.Status = STATUS_SUCCESS; + + IoSkipCurrentIrpStackLocation(pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + + IoDetachDevice(pDevExt->pNextLowerDriver); + Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n")); + + /* + * Delete the remainder of the device extension data, unlink it from the namespace and delete it. + */ + vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt); + + pDevObj = NULL; /* invalid */ + pDevExt = NULL; /* invalid */ + + Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n")); + return rc; /* Propagating rc from IoCallDriver. */ + } + + + /* + * Sent before stopping the device/driver to check whether it is okay to do so. + */ + case IRP_MN_QUERY_STOP_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n")); + RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect); + rc = vgdrvNtCheckIdle(pDevExt, "QUERY_STOP_DEVICE"); + if (NT_SUCCESS(rc)) + { + pDevExt->enmPrevDevState = pDevExt->enmDevState; + pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGSTOP; + RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect); + + /* This IRP passed down to lower driver. */ + pIrp->IoStatus.Status = STATUS_SUCCESS; + + IoSkipCurrentIrpStackLocation(pIrp); + + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + + /* we must not do anything with the IRP after doing IoSkip & CallDriver since the + driver below us will complete (or already have completed) the IRP. I.e. just + return the status we got from IoCallDriver. */ + } + else + { + RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect); + pIrp->IoStatus.Status = rc; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Returning with rc = 0x%x\n", rc)); + return rc; + } + + /* + * Cancels a pending remove, IRP_MN_QUERY_STOP_DEVICE. + * We only have to revert the state. + */ + case IRP_MN_CANCEL_STOP_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n")); + + /* This must be handled first by the lower driver. */ + rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); + if ( NT_SUCCESS(rc) + && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP) + { + /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */ + pDevExt->enmDevState = pDevExt->enmPrevDevState; + } + + /* Complete the IRP. */ + pIrp->IoStatus.Status = rc; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rc; + } + + /* + * Stop the device. + */ + case IRP_MN_STOP_DEVICE: + { + Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n")); + pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED; + + /* + * Release the hardware resources. + */ + vgdrvNtDeleteDeviceResources(pDevExt); + + /* + * Pass the request to the lower driver. + */ + pIrp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation(pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + return rc; + } + + default: + { + IoSkipCurrentIrpStackLocation(pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("vgdrvNtNt5PlusPnP: Unknown request %#x: Lower driver replied: %x\n", bMinorFunction, rc)); + return rc; + } + } +} + + +/** + * Handle the power completion event. + * + * @returns NT status code. + * @param pDevObj Targetted device object. + * @param pIrp IO request packet. + * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower. + */ +static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext) +{ +#ifdef VBOX_STRICT + RT_NOREF1(pDevObj); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext; + PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Assert(pDevExt); + + if (pIrpSp) + { + Assert(pIrpSp->MajorFunction == IRP_MJ_POWER); + if (NT_SUCCESS(pIrp->IoStatus.Status)) + { + switch (pIrpSp->MinorFunction) + { + case IRP_MN_SET_POWER: + switch (pIrpSp->Parameters.Power.Type) + { + case DevicePowerState: + switch (pIrpSp->Parameters.Power.State.DeviceState) + { + case PowerDeviceD0: + break; + default: /* Shut up MSC */ + break; + } + break; + default: /* Shut up MSC */ + break; + } + break; + } + } + } +#else + RT_NOREF3(pDevObj, pIrp, pContext); +#endif + + return STATUS_SUCCESS; +} + + +/** + * Handle the Power requests. + * + * @returns NT status code + * @param pDevObj device object + * @param pIrp IRP + */ +static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type; + POWER_STATE PowerState = pStack->Parameters.Power.State; + POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType; + + Log(("vgdrvNtNt5PlusPower:\n")); + + switch (pStack->MinorFunction) + { + case IRP_MN_SET_POWER: + { + Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType)); + switch (enmPowerType) + { + case SystemPowerState: + { + Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n", + enmPowerAction, PowerState.SystemState, PowerState.DeviceState)); + + switch (enmPowerAction) + { + case PowerActionSleep: + + /* System now is in a working state. */ + if (PowerState.SystemState == PowerSystemWorking) + { + if ( pDevExt + && pDevExt->enmLastSystemPowerAction == PowerActionHibernate) + { + Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n")); + int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core, + vgdrvNtVersionToOSType(g_enmVGDrvNtVer)); + if (RT_FAILURE(rc)) + Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc)); + } + } + break; + + case PowerActionShutdownReset: + { + Log(("vgdrvNtNt5PlusPower: Power action reset!\n")); + + /* Tell the VMM that we no longer support mouse pointer integration. */ + VMMDevReqMouseStatus *pReq = NULL; + int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus), + VMMDevReq_SetMouseStatus); + if (RT_SUCCESS(vrc)) + { + pReq->mouseFeatures = 0; + pReq->pointerXPos = 0; + pReq->pointerYPos = 0; + + vrc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(vrc)) + { + Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc)); + } + + VbglR0GRFree(&pReq->header); + } + + /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this + * power action and would assert/crash when we already cleaned up all the stuff! */ + break; + } + + case PowerActionShutdown: + case PowerActionShutdownOff: + { + Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n")); + if (PowerState.SystemState >= PowerSystemShutdown) + { + Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n")); + + VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest; + int vrc = VERR_NOT_IMPLEMENTED; + if (pReq) + { + pReq->header.requestType = VMMDevReq_SetPowerStatus; + pReq->powerState = VMMDevPowerState_PowerOff; + + vrc = VbglR0GRPerform(&pReq->header); + } + if (RT_FAILURE(vrc)) + Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc)); + + /* No need to do cleanup here; at this point we should've been + * turned off by VMMDev already! */ + } + break; + } + + case PowerActionHibernate: + Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n")); + break; + + case PowerActionWarmEject: + Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n")); + break; + + default: + Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction)); + break; + } + + /* + * Save the current system power action for later use. + * This becomes handy when we return from hibernation for example. + */ + if (pDevExt) + pDevExt->enmLastSystemPowerAction = enmPowerAction; + + break; + } + default: + break; + } + break; + } + default: + break; + } + + /* + * Whether we are completing or relaying this power IRP, + * we must call PoStartNextPowerIrp. + */ + g_pfnPoStartNextPowerIrp(pIrp); + + /* + * Send the IRP down the driver stack, using PoCallDriver + * (not IoCallDriver, as for non-power irps). + */ + IoCopyCurrentIrpStackLocationToNext(pIrp); + IoSetCompletionRoutine(pIrp, + vgdrvNtNt5PlusPowerComplete, + (PVOID)pDevExt, + TRUE, + TRUE, + TRUE); + return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp); +} + + +/** + * IRP_MJ_SYSTEM_CONTROL handler. + * + * @returns NT status code + * @param pDevObj Device object. + * @param pIrp IRP. + */ +static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + + LogFlowFuncEnter(); + + /* Always pass it on to the next driver. */ + IoSkipCurrentIrpStackLocation(pIrp); + + return IoCallDriver(pDevExt->pNextLowerDriver, pIrp); +} + + +/** + * Unload the driver. + * + * @param pDrvObj Driver object. + */ +static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj) +{ + LogFlowFuncEnter(); + +#ifdef TARGET_NT4 + /* + * We need to destroy the device object here on NT4 and earlier. + */ + PDEVICE_OBJECT pDevObj = pDrvObj->DeviceObject; + if (pDevObj) + { + if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4) + { + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + AssertPtr(pDevExt); + AssertMsg(pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES, + ("uInitState=%#x\n", pDevExt->Core.uInitState)); + + vgdrvNtDeleteDeviceResources(pDevExt); + vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt); + } + } +#else /* !TARGET_NT4 */ + /* + * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE + * where we already did the cleanup, so don't do anything here (yet). + */ + RT_NOREF1(pDrvObj); +#endif /* !TARGET_NT4 */ + + VGDrvCommonDestroyLoggers(); + RTR0Term(); + + /* + * Finally deregister the bugcheck callback. Do it late to catch trouble in RTR0Term. + */ + if (g_fBugCheckCallbackRegistered) + { + g_pfnKeDeregisterBugCheckCallback(&g_BugCheckCallbackRec); + g_fBugCheckCallbackRegistered = false; + } +} + + +/** + * For simplifying request completion into a simple return statement, extended + * version. + * + * @returns rcNt + * @param rcNt The status code. + * @param uInfo Extra info value. + * @param pIrp The IRP. + */ +DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp) +{ + pIrp->IoStatus.Status = rcNt; + pIrp->IoStatus.Information = uInfo; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return rcNt; +} + + +/** + * For simplifying request completion into a simple return statement. + * + * @returns rcNt + * @param rcNt The status code. + * @param pIrp The IRP. + */ +DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp) +{ + return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp); +} + + +/** + * Checks if NT authority rev 1 SID (SECURITY_NT_AUTHORITY). + * + * @returns true / false. + * @param pSid The SID to check. + */ +DECLINLINE(bool) vgdrvNtIsSidNtAuth(struct _SID const *pSid) +{ + return pSid != NULL + && pSid->Revision == 1 + && pSid->IdentifierAuthority.Value[5] == 5 + && pSid->IdentifierAuthority.Value[4] == 0 + && pSid->IdentifierAuthority.Value[3] == 0 + && pSid->IdentifierAuthority.Value[2] == 0 + && pSid->IdentifierAuthority.Value[1] == 0 + && pSid->IdentifierAuthority.Value[0] == 0; +} + + +/** + * Matches SID with local system user (S-1-5-18 / SECURITY_LOCAL_SYSTEM_RID). + */ +DECLINLINE(bool) vgdrvNtIsSidLocalSystemUser(SID const *pSid) +{ + return vgdrvNtIsSidNtAuth(pSid) + && pSid->SubAuthorityCount == 1 + && pSid->SubAuthority[0] == SECURITY_LOCAL_SYSTEM_RID; +} + + +/** + * Matches SID with NT system admin user (S-1-5-*-500 / DOMAIN_USER_RID_ADMIN). + */ +DECLINLINE(bool) vgdrvNtIsSidAdminUser(SID const *pSid) +{ + /** @todo restrict to SECURITY_NT_NON_UNIQUE? */ + return vgdrvNtIsSidNtAuth(pSid) + && pSid->SubAuthorityCount >= 2 + && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES + && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_ADMIN; +} + + +/** + * Matches SID with NT system guest user (S-1-5-*-501 / DOMAIN_USER_RID_GUEST). + */ +DECLINLINE(bool) vgdrvNtIsSidGuestUser(SID const *pSid) +{ + /** @todo restrict to SECURITY_NT_NON_UNIQUE? */ + return vgdrvNtIsSidNtAuth(pSid) + && pSid->SubAuthorityCount >= 2 + && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES + && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_GUEST; +} + + +/** + * Matches SID with NT system admins group (S-1-5-32-544, S-1-5-*-512). + */ +DECLINLINE(bool) vgdrvNtIsSidAdminsGroup(SID const *pSid) +{ + return vgdrvNtIsSidNtAuth(pSid) + && ( ( pSid->SubAuthorityCount == 2 + && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID + && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_ADMINS) +#if 0 + /** @todo restrict to SECURITY_NT_NON_UNIQUE? */ + || ( pSid->SubAuthorityCount >= 2 + && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES + && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_ADMINS) +#endif + ); +} + + +/** + * Matches SID with NT system users group (S-1-5-32-545, S-1-5-32-547, S-1-5-*-512). + */ +DECLINLINE(bool) vgdrvNtIsSidUsersGroup(SID const *pSid) +{ + return vgdrvNtIsSidNtAuth(pSid) + && ( ( pSid->SubAuthorityCount == 2 + && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID + && ( pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_USERS + || pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_POWER_USERS) ) +#if 0 + /** @todo restrict to SECURITY_NT_NON_UNIQUE? */ + || ( pSid->SubAuthorityCount >= 2 + && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES + && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_USERS) +#endif + ); +} + + +/** + * Matches SID with NT system guests group (S-1-5-32-546, S-1-5-*-512). + */ +DECLINLINE(bool) vgdrvNtIsSidGuestsGroup(SID const *pSid) +{ + return vgdrvNtIsSidNtAuth(pSid) + && ( ( pSid->SubAuthorityCount == 2 + && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID + && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_GUESTS) +#if 0 + /** @todo restrict to SECURITY_NT_NON_UNIQUE? */ + || ( pSid->SubAuthorityCount >= 2 + && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES + && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_GUESTS) +#endif + ); +} + + +/** + * Checks if local authority rev 1 SID (SECURITY_LOCAL_SID_AUTHORITY). + * + * @returns true / false. + * @param pSid The SID to check. + */ +DECLINLINE(bool) vgdrvNtIsSidLocalAuth(struct _SID const *pSid) +{ + return pSid != NULL + && pSid->Revision == 1 + && pSid->IdentifierAuthority.Value[5] == 2 + && pSid->IdentifierAuthority.Value[4] == 0 + && pSid->IdentifierAuthority.Value[3] == 0 + && pSid->IdentifierAuthority.Value[2] == 0 + && pSid->IdentifierAuthority.Value[1] == 0 + && pSid->IdentifierAuthority.Value[0] == 0; +} + + +/** + * Matches SID with console logon group (S-1-2-1 / SECURITY_LOCAL_LOGON_RID). + */ +DECLINLINE(bool) vgdrvNtIsSidConsoleLogonGroup(SID const *pSid) +{ + return vgdrvNtIsSidLocalAuth(pSid) + && pSid->SubAuthorityCount == 1 + && pSid->SubAuthority[0] == SECURITY_LOCAL_LOGON_RID; +} + + +/** + * Checks if mandatory label authority rev 1 SID (SECURITY_MANDATORY_LABEL_AUTHORITY). + * + * @returns true / false. + * @param pSid The SID to check. + */ +DECLINLINE(bool) vgdrvNtIsSidMandatoryLabelAuth(struct _SID const *pSid) +{ + return pSid != NULL + && pSid->Revision == 1 + && pSid->IdentifierAuthority.Value[5] == 16 + && pSid->IdentifierAuthority.Value[4] == 0 + && pSid->IdentifierAuthority.Value[3] == 0 + && pSid->IdentifierAuthority.Value[2] == 0 + && pSid->IdentifierAuthority.Value[1] == 0 + && pSid->IdentifierAuthority.Value[0] == 0; +} + + +#ifdef LOG_ENABLED +/** Format an SID for logging. */ +static const char *vgdrvNtFormatSid(char *pszBuf, size_t cbBuf, struct _SID const *pSid) +{ + uint64_t uAuth = RT_MAKE_U64_FROM_U8(pSid->IdentifierAuthority.Value[5], pSid->IdentifierAuthority.Value[4], + pSid->IdentifierAuthority.Value[3], pSid->IdentifierAuthority.Value[2], + pSid->IdentifierAuthority.Value[1], pSid->IdentifierAuthority.Value[0], + 0, 0); + ssize_t offCur = RTStrPrintf2(pszBuf, cbBuf, "S-%u-%RU64", pSid->Revision, uAuth); + ULONG const *puSubAuth = &pSid->SubAuthority[0]; + unsigned cSubAuths = pSid->SubAuthorityCount; + while (cSubAuths > 0 && (size_t)offCur < cbBuf) + { + ssize_t cchThis = RTStrPrintf2(&pszBuf[offCur], cbBuf - (size_t)offCur, "-%u", *puSubAuth); + if (cchThis > 0) + { + offCur += cchThis; + puSubAuth++; + cSubAuths--; + } + else + { + Assert(cbBuf >= 5); + pszBuf[cbBuf - 4] = '.'; + pszBuf[cbBuf - 3] = '.'; + pszBuf[cbBuf - 2] = '.'; + pszBuf[cbBuf - 1] = '\0'; + break; + } + } + return pszBuf; +} +#endif + + +/** + * Calculate requestor flags for the current process. + * + * ASSUMES vgdrvNtCreate is executed in the context of the process and thread + * doing the NtOpenFile call. + * + * @returns VMMDEV_REQUESTOR_XXX + */ +static uint32_t vgdrvNtCalcRequestorFlags(void) +{ + uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE + | VMMDEV_REQUESTOR_USR_NOT_GIVEN + | VMMDEV_REQUESTOR_CON_DONT_KNOW + | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN + | VMMDEV_REQUESTOR_NO_USER_DEVICE; + HANDLE hToken = NULL; + NTSTATUS rcNt = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken); + if (NT_SUCCESS(rcNt)) + { + union + { + TOKEN_USER CurUser; + TOKEN_GROUPS CurGroups; + uint8_t abPadding[256]; + } Buf; +#ifdef LOG_ENABLED + char szSid[200]; +#endif + + /* + * Get the user SID and see if it's a standard one. + */ + RT_ZERO(Buf.CurUser); + ULONG cbReturned = 0; + rcNt = ZwQueryInformationToken(hToken, TokenUser, &Buf.CurUser, sizeof(Buf), &cbReturned); + if (NT_SUCCESS(rcNt)) + { + struct _SID const *pSid = (struct _SID const *)Buf.CurUser.User.Sid; + Log5(("vgdrvNtCalcRequestorFlags: TokenUser: %#010x %s\n", + Buf.CurUser.User.Attributes, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid))); + + if (vgdrvNtIsSidLocalSystemUser(pSid)) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_SYSTEM; + else if (vgdrvNtIsSidAdminUser(pSid)) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_ROOT; + else if (vgdrvNtIsSidGuestUser(pSid)) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST; + } + else + LogRel(("vgdrvNtCalcRequestorFlags: TokenUser query failed: %#x\n", rcNt)); + + /* + * Get the groups. + */ + TOKEN_GROUPS *pCurGroupsFree = NULL; + TOKEN_GROUPS *pCurGroups = &Buf.CurGroups; + uint32_t cbCurGroups = sizeof(Buf); + cbReturned = 0; + RT_ZERO(Buf); + rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned); + if (rcNt == STATUS_BUFFER_TOO_SMALL) + { + uint32_t cTries = 8; + do + { + RTMemTmpFree(pCurGroupsFree); + if (cbCurGroups < cbReturned) + cbCurGroups = RT_ALIGN_32(cbCurGroups + 32, 64); + else + cbCurGroups += 64; + pCurGroupsFree = pCurGroups = (TOKEN_GROUPS *)RTMemTmpAllocZ(cbCurGroups); + if (pCurGroupsFree) + rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned); + else + rcNt = STATUS_NO_MEMORY; + } while (rcNt == STATUS_BUFFER_TOO_SMALL && cTries-- > 0); + } + if (NT_SUCCESS(rcNt)) + { + bool fGuestsMember = false; + bool fUsersMember = false; + if (g_enmVGDrvNtVer >= VGDRVNTVER_WIN7) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_NO; + + for (uint32_t iGrp = 0; iGrp < pCurGroups->GroupCount; iGrp++) + { + uint32_t const fAttribs = pCurGroups->Groups[iGrp].Attributes; + struct _SID const *pSid = (struct _SID const *)pCurGroups->Groups[iGrp].Sid; + Log5(("vgdrvNtCalcRequestorFlags: TokenGroups[%u]: %#10x %s\n", + iGrp, fAttribs, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid))); + + if ( (fAttribs & SE_GROUP_INTEGRITY_ENABLED) + && vgdrvNtIsSidMandatoryLabelAuth(pSid) + && pSid->SubAuthorityCount == 1 + && (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN) + { + fRequestor &= ~VMMDEV_REQUESTOR_TRUST_MASK; + if (pSid->SubAuthority[0] < SECURITY_MANDATORY_LOW_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_UNTRUSTED; + else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_LOW; + else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_PLUS_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM; + else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_HIGH_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM_PLUS; + else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_SYSTEM_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_HIGH; + else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_PROTECTED_PROCESS_RID) + fRequestor |= VMMDEV_REQUESTOR_TRUST_SYSTEM; + else + fRequestor |= VMMDEV_REQUESTOR_TRUST_PROTECTED; + Log5(("vgdrvNtCalcRequestorFlags: mandatory label %u: => %#x\n", pSid->SubAuthority[0], fRequestor)); + } + else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY)) + == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY) + && vgdrvNtIsSidConsoleLogonGroup(pSid)) + { + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_YES; + Log5(("vgdrvNtCalcRequestorFlags: console: => %#x\n", fRequestor)); + } + else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY)) + == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY) + && vgdrvNtIsSidNtAuth(pSid)) + { + if (vgdrvNtIsSidAdminsGroup(pSid)) + { + fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL; + Log5(("vgdrvNtCalcRequestorFlags: admins group: => %#x\n", fRequestor)); + } + else if (vgdrvNtIsSidUsersGroup(pSid)) + { + Log5(("vgdrvNtCalcRequestorFlags: users group\n")); + fUsersMember = true; + } + else if (vgdrvNtIsSidGuestsGroup(pSid)) + { + Log5(("vgdrvNtCalcRequestorFlags: guests group\n")); + fGuestsMember = true; + } + } + } + if ((fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_NOT_GIVEN) + { + if (fUsersMember) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST; + else if (fGuestsMember) + fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST; + } + } + else + LogRel(("vgdrvNtCalcRequestorFlags: TokenGroups query failed: %#x\n", rcNt)); + + RTMemTmpFree(pCurGroupsFree); + ZwClose(hToken); + } + else + LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt)); + + Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor)); + return fRequestor; +} + + +/** + * Create (i.e. Open) file entry point. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode)); + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack->FileObject; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + + Assert(pFileObj->FsContext == NULL); + + /* + * We are not remotely similar to a directory... + */ + NTSTATUS rcNt; + if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)) + { + /* + * Check the device state. We enter the critsect in shared mode to + * prevent race with PnP system requests checking whether we're idle. + */ + RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect); + VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState; + if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL) + { + /* + * Create a client session. + */ + int rc; + PVBOXGUESTSESSION pSession; + if (pIrp->RequestorMode == KernelMode) + rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession); + else + rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession); + RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect); + if (RT_SUCCESS(rc)) + { + pFileObj->FsContext = pSession; + Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n", + pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor)); + + return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp); + } + + /* Note. the IoStatus is completely ignored on error. */ + Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc)); + if (rc == VERR_NO_MEMORY) + rcNt = STATUS_NO_MEMORY; + else + rcNt = STATUS_UNSUCCESSFUL; + } + else + { + RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect); + LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState)); + rcNt = STATUS_DEVICE_NOT_READY; + } + } + else + { + LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n")); + rcNt = STATUS_NOT_A_DIRECTORY; + } + return vgdrvNtCompleteRequest(rcNt, pIrp); +} + + +/** + * Close file entry point. + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack->FileObject; + + LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext)); + +#ifdef VBOX_WITH_HGCM + /* Close both, R0 and R3 sessions. */ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext; + if (pSession) + VGDrvCommonCloseSession(&pDevExt->Core, pSession); +#endif + + 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 NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL; + + if (!RT_VALID_PTR(pSession)) + return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp); + +#if 0 /* No fast I/O controls defined yet. */ + /* + * Deal with the 2-3 high-speed IOCtl that takes their arguments from + * the session and iCmd, and does not return anything. + */ + if (pSession->fUnrestricted) + { + ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode; + if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN + || ulCmd == SUP_IOCTL_FAST_DO_NOP) + { + int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession); + + /* Complete the I/O request. */ + supdrvSessionRelease(pSession); + return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp); + } + } +#endif + + return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack); +} + + +/** + * Device I/O Control entry point. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pIrp Request packet. + * @param pStack The request stack pointer. + */ +static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + PIRP pIrp, PIO_STACK_LOCATION pStack) +{ + NTSTATUS rcNt; + uint32_t cbOut = 0; + int rc = 0; + Log2(("vgdrvNtDeviceControlSlow(%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)); + +#if 0 /*def 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. */ + PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer; + if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) + && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn + && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut) + { + /* Zero extra output bytes to make sure we don't leak anything. */ + if (pHdr->cbIn < pHdr->cbOut) + RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn); + + /* + * Do the job. + */ + rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr, + RT_MAX(pHdr->cbIn, pHdr->cbOut)); + if (RT_SUCCESS(rc)) + { + rcNt = STATUS_SUCCESS; + cbOut = pHdr->cbOut; + if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength) + { + cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength; + LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n", + pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode)); + } + + /* If IDC successful disconnect request, we must set the context pointer to NULL. */ + if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT + && RT_SUCCESS(pHdr->rc)) + pStack->FileObject->FsContext = NULL; + } + else if (rc == VERR_NOT_SUPPORTED) + rcNt = STATUS_NOT_SUPPORTED; + else + rcNt = STATUS_INVALID_PARAMETER; + Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc)); + } + else + { + Log(("vgdrvNtDeviceControlSlow: 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 + { + Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n", + pStack->Parameters.DeviceIoControl.IoControlCode)); + rcNt = STATUS_NOT_SUPPORTED; + } + } +#if 0 /*def RT_ARCH_AMD64*/ + else + { + Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n")); + rcNt = STATUS_NOT_SUPPORTED; + } +#endif + + return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp); +} + + +/** + * Internal Device I/O Control entry point (for IDC). + * + * @param pDevObj Device object. + * @param pIrp Request packet. + */ +static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + /* Currently no special code here. */ + return vgdrvNtDeviceControl(pDevObj, pIrp); +} + + +/** + * IRP_MJ_SHUTDOWN handler. + * + * @returns NT status code + * @param pDevObj Device object. + * @param pIrp IRP. + */ +static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + LogFlowFuncEnter(); + + VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest; + if (pReq) + { + pReq->header.requestType = VMMDevReq_SetPowerStatus; + pReq->powerState = VMMDevPowerState_PowerOff; + + int rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc)); + } + + /* just in case, since we shouldn't normally get here. */ + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + + +/** + * Stub function for functions we don't implemented. + * + * @returns STATUS_NOT_SUPPORTED + * @param pDevObj Device object. + * @param pIrp IRP. + */ +static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + RT_NOREF1(pDevObj); + LogFlowFuncEnter(); + + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_NOT_SUPPORTED; +} + + +/** + * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE). + * + * This adds a log entry on the host, in case Hyper-V isn't active or the guest + * is too old for reporting it itself via the crash MSRs. + * + * @param pvBuffer Not used. + * @param cbBuffer Not used. + */ +static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer) +{ + if (g_pauKiBugCheckData) + { + RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0], + g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]); + + VMMDevReqNtBugCheck *pReq = NULL; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck); + if (RT_SUCCESS(rc)) + { + pReq->uBugCheck = g_pauKiBugCheckData[0]; + pReq->auParameters[0] = g_pauKiBugCheckData[1]; + pReq->auParameters[1] = g_pauKiBugCheckData[2]; + pReq->auParameters[2] = g_pauKiBugCheckData[3]; + pReq->auParameters[3] = g_pauKiBugCheckData[4]; + VbglR0GRPerform(&pReq->header); + VbglR0GRFree(&pReq->header); + } + } + else + { + RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n"); + + VMMDevRequestHeader *pReqHdr = NULL; + int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck); + if (RT_SUCCESS(rc)) + { + VbglR0GRPerform(pReqHdr); + VbglR0GRFree(pReqHdr); + } + } + + RT_NOREF(pvBuffer, cbBuffer); +} + + +/** + * Sets the mouse notification callback. + * + * @returns VBox status code. + * @param pDevExt Pointer to the device extension. + * @param pNotify Pointer to the mouse notify struct. + */ +int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify) +{ + PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt; + /* we need a lock here to avoid concurrency with the set event functionality */ + KIRQL OldIrql; + KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql); + pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify; + pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser; + KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql); + return VINF_SUCCESS; +} + + +/** + * DPC handler. + * + * @param pDPC DPC descriptor. + * @param pDevObj Device object. + * @param pIrp Interrupt request packet. + * @param pContext Context specific pointer. + */ +static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext) +{ + RT_NOREF3(pDPC, pIrp, pContext); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + Log3Func(("pDevExt=0x%p\n", pDevExt)); + + /* Test & reset the counter. */ + if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0)) + { + /* we need a lock here to avoid concurrency with the set event ioctl handler thread, + * i.e. to prevent the event from destroyed while we're using it */ + Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); + KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock); + + if (pDevExt->Core.pfnMouseNotifyCallback) + pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg); + + KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock); + } + + /* Process the wake-up list we were asked by the scheduling a DPC + * in vgdrvNtIsrHandler(). */ + VGDrvCommonWaitDoWakeUps(&pDevExt->Core); +} + + +/** + * ISR handler. + * + * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE). + * @param pInterrupt Interrupt that was triggered. + * @param pServiceContext Context specific pointer. + */ +static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext) +{ + RT_NOREF1(pInterrupt); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext; + if (pDevExt == NULL) + return FALSE; + + /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/ + + /* Enter the common ISR routine and do the actual work. */ + BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core); + + /* If we need to wake up some events we do that in a DPC to make + * sure we're called at the right IRQL. */ + if (fIRQTaken) + { + Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt)); + if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq) + || !RTListIsEmpty(&pDevExt->Core.WakeUpList)) + { + Log3Func(("Requesting DPC...\n")); + IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/); + } + } + return fIRQTaken; +} + + +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + NOREF(pDevExt); + /* nothing to do here - i.e. since we can not KeSetEvent from ISR level, + * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event + * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */ +} + + +/** + * Hook for handling OS specfic options from the host. + * + * @returns true if handled, false if not. + * @param pDevExt The device extension. + * @param pszName The option name. + * @param pszValue The option value. + */ +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue); + return false; +} + + +/** + * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key. + */ +static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType, + PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx) +{ + Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue)); + + /* + * Filter out general service config values. + */ + if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0 + || RTUtf16ICmpAscii(pwszValueName, "Start") == 0 + || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0 + || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0 + || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0 + || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0 + || RTUtf16ICmpAscii(pwszValueName, "Group") == 0 + || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0 + || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0 + ) + { + return STATUS_SUCCESS; + } + + /* + * Convert the value name. + */ + size_t cch = RTUtf16CalcUtf8Len(pwszValueName); + if (cch < 64 && cch > 0) + { + char szValueName[72]; + char *pszTmp = szValueName; + int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL); + if (RT_SUCCESS(rc)) + { + /* + * Convert the value. + */ + char szValue[72]; + char *pszFree = NULL; + char *pszValue = NULL; + szValue[0] = '\0'; + switch (uValueType) + { + case REG_SZ: + case REG_EXPAND_SZ: + rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch); + if (RT_SUCCESS(rc) && cch < _1K) + { + if (cch < sizeof(szValue)) + { + pszValue = szValue; + rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL); + } + else + { + rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL); + if (RT_SUCCESS(rc)) + pszFree = pszValue; + } + if (RT_FAILURE(rc)) + { + LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n", + pwszValueName, rc)); + pszValue = NULL; + } + } + else if (RT_SUCCESS(rc)) + LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n", + pwszValueName, cbValue, uValueType)); + else + LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n", + pwszValueName, cbValue, uValueType)); + break; + + case REG_DWORD: + if (cbValue == sizeof(uint32_t)) + { + RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0); + pszValue = szValue; + } + else + LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue)); + break; + + case REG_QWORD: + if (cbValue == sizeof(uint64_t)) + { + RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0); + pszValue = szValue; + } + else + LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue)); + break; + + default: + LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType)); + break; + } + if (pszValue) + { + /* + * Process it. + */ + PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser; + VGDrvCommonProcessOption(pDevExt, szValueName, pszValue); + if (pszFree) + RTStrFree(pszFree); + } + } + } + else if (cch > 0) + LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName)); + else + LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName)); + NOREF(pvEntryCtx); + return STATUS_SUCCESS; +} + + +/** + * Reads configuration from the registry and guest properties. + * + * We ignore failures and instead preserve existing configuration values. + * + * Thie routine will block. + * + * @param pDevExt The device extension. + */ +static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt) +{ + /* + * First the registry. + * + * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to + * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3 + * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0. + */ + RTL_QUERY_REGISTRY_TABLE aQuery[2]; + RT_ZERO(aQuery); + aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback; + aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; + aQuery[0].Name = NULL; + aQuery[0].EntryContext = NULL; + aQuery[0].DefaultType = REG_NONE; + NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/); + if (!NT_SUCCESS(rcNt)) + LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt)); + + /* + * Read configuration from the host. + */ + VGDrvCommonProcessOptionsFromHost(&pDevExt->Core); +} + +#ifdef VBOX_STRICT + +/** + * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits. + */ +static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask) +{ + AssertPtrReturn(pu32Bits, 0); + LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask)); + uint32_t u32Result = 0; + uint32_t u32WorkingMask = u32Mask; + int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask); + + while (iBitOffset > 0) + { + bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1); + if (fSet) + u32Result |= 1 << (iBitOffset - 1); + u32WorkingMask &= ~(1 << (iBitOffset - 1)); + iBitOffset = ASMBitFirstSetU32 (u32WorkingMask); + } + LogFlowFunc(("Returning %#x\n", u32Result)); + return u32Result; +} + + +static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp) +{ + ULONG u32Bits2 = u32Bits; + uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask); + if ( u32Result != u32Exp + || (u32Bits2 & u32Mask) + || (u32Bits2 & u32Result) + || ((u32Bits2 | u32Result) != u32Bits) + ) + AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n", + u32Mask, u32Bits, u32Bits2, u32Result)); +} + + +static void vgdrvNtDoTests(void) +{ + vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0); + vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0); + vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0); + vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1); + vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10); + vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22); +} + +#endif /* VBOX_STRICT */ + +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER + +/* + * DPC latency checker. + */ + +/** + * One DPC latency sample. + */ +typedef struct DPCSAMPLE +{ + LARGE_INTEGER PerfDelta; + LARGE_INTEGER PerfCounter; + LARGE_INTEGER PerfFrequency; + uint64_t u64TSC; +} DPCSAMPLE; +AssertCompileSize(DPCSAMPLE, 4*8); + +/** + * The DPC latency measurement workset. + */ +typedef struct DPCDATA +{ + KDPC Dpc; + KTIMER Timer; + KSPIN_LOCK SpinLock; + + ULONG ulTimerRes; + + bool volatile fFinished; + + /** The timer interval (relative). */ + LARGE_INTEGER DueTime; + + LARGE_INTEGER PerfCounterPrev; + + /** Align the sample array on a 64 byte boundrary just for the off chance + * that we'll get cache line aligned memory backing this structure. */ + uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7]; + + int cSamples; + DPCSAMPLE aSamples[8192]; +} DPCDATA; + +AssertCompileMemberAlignment(DPCDATA, aSamples, 64); + +/** + * DPC callback routine for the DPC latency measurement code. + * + * @param pDpc The DPC, not used. + * @param pvDeferredContext Pointer to the DPCDATA. + * @param SystemArgument1 System use, ignored. + * @param SystemArgument2 System use, ignored. + */ +static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) +{ + DPCDATA *pData = (DPCDATA *)pvDeferredContext; + RT_NOREF(pDpc, SystemArgument1, SystemArgument2); + + KeAcquireSpinLockAtDpcLevel(&pData->SpinLock); + + if (pData->cSamples >= RT_ELEMENTS(pData->aSamples)) + pData->fFinished = true; + else + { + DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++]; + + pSample->u64TSC = ASMReadTSC(); + pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency); + pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart; + + pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart; + + KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc); + } + + KeReleaseSpinLockFromDpcLevel(&pData->SpinLock); +} + + +/** + * Handles the DPC latency checker request. + * + * @returns VBox status code. + */ +int VGDrvNtIOCtl_DpcLatencyChecker(void) +{ + /* + * Allocate a block of non paged memory for samples and related data. + */ + DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA)); + if (!pData) + { + RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n"); + return VERR_NO_MEMORY; + } + + /* + * Initialize the data. + */ + KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData); + KeInitializeTimer(&pData->Timer); + KeInitializeSpinLock(&pData->SpinLock); + + pData->fFinished = false; + pData->cSamples = 0; + pData->PerfCounterPrev.QuadPart = 0; + + pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1); + pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10; + + /* + * Start the DPC measurements and wait for a full set. + */ + KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc); + + while (!pData->fFinished) + { + LARGE_INTEGER Interval; + Interval.QuadPart = -100 * 1000 * 10; + KeDelayExecutionThread(KernelMode, TRUE, &Interval); + } + + ExSetTimerResolution(0, 0); + + /* + * Log everything to the host. + */ + RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes); + for (int i = 0; i < pData->cSamples; i++) + { + DPCSAMPLE *pSample = &pData->aSamples[i]; + + RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n", + i, + pSample->PerfDelta.QuadPart, + pSample->PerfCounter.QuadPart, + pSample->PerfFrequency.QuadPart, + pSample->u64TSC); + } + + RTMemFree(pData); + return VINF_SUCCESS; +} + +#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp new file mode 100644 index 00000000..bcc67c68 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp @@ -0,0 +1,4516 @@ +/* $Id: VBoxGuest.cpp $ */ +/** @file + * VBoxGuest - Guest Additions Driver, Common Code. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/** @page pg_vbdrv VBoxGuest + * + * VBoxGuest is the device driver for VMMDev. + * + * The device driver is shipped as part of the guest additions. It has roots in + * the host VMM support driver (usually known as VBoxDrv), so fixes in platform + * specific code may apply to both drivers. + * + * The common code lives in VBoxGuest.cpp and is compiled both as C++ and C. + * The VBoxGuest.cpp source file shall not contain platform specific code, + * though it must occationally do a few \#ifdef RT_OS_XXX tests to cater for + * platform differences. Though, in those cases, it is common that more than + * one platform needs special handling. + * + * On most platforms the device driver should create two device nodes, one for + * full (unrestricted) access to the feature set, and one which only provides a + * restrict set of functions. These are generally referred to as 'vboxguest' + * and 'vboxuser' respectively. Currently, this two device approach is only + * implemented on Linux! + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DEFAULT +#include "VBoxGuestInternal.h" +#include <VBox/VMMDev.h> /* for VMMDEV_RAM_SIZE */ +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/HostServices/GuestPropertySvc.h> +#include <iprt/ctype.h> +#include <iprt/mem.h> +#include <iprt/time.h> +#include <iprt/memobj.h> +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/string.h> +#include <iprt/process.h> +#include <iprt/assert.h> +#include <iprt/param.h> +#include <iprt/timer.h> +#ifdef VBOX_WITH_HGCM +# include <iprt/thread.h> +#endif +#include "version-generated.h" +#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) +# include "revision-generated.h" +#endif +#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN) +# include <iprt/rand.h> +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBOXGUEST_ACQUIRE_STYLE_EVENTS (VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST) + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +#ifdef VBOX_WITH_HGCM +static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdrNonVolatile, void *pvUser, uint32_t u32User); +#endif +static int vgdrvIoCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); +static void vgdrvBitUsageTrackerClear(PVBOXGUESTBITUSAGETRACER pTracker); +static uint32_t vgdrvGetAllowedEventMaskForSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); +static int vgdrvResetEventFilterOnHost(PVBOXGUESTDEVEXT pDevExt, uint32_t fFixedEvents); +static int vgdrvResetMouseStatusOnHost(PVBOXGUESTDEVEXT pDevExt); +static int vgdrvResetCapabilitiesOnHost(PVBOXGUESTDEVEXT pDevExt); +static int vgdrvSetSessionEventFilter(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination); +static int vgdrvSetSessionMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination); +static int vgdrvSetSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNoMask, + uint32_t *pfSessionCaps, uint32_t *pfGlobalCaps, bool fSessionTermination); +static int vgdrvAcquireSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, uint32_t fFlags, bool fSessionTermination); +static int vgdrvDispatchEventsLocked(PVBOXGUESTDEVEXT pDevExt, uint32_t fEvents); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static const uint32_t g_cbChangeMemBalloonReq = RT_UOFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]); + +#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 Solaris. + */ +struct CLANG11WEIRDNESS { PFNRT pfn; } g_apfnVBoxGuestIPRTDeps[] = +{ + /* VirtioNet */ + { (PFNRT)RTRandBytes }, + /* RTSemMutex* */ + { (PFNRT)RTSemMutexCreate }, + { (PFNRT)RTSemMutexDestroy }, + { (PFNRT)RTSemMutexRequest }, + { (PFNRT)RTSemMutexRequestNoResume }, + { (PFNRT)RTSemMutexRequestDebug }, + { (PFNRT)RTSemMutexRequestNoResumeDebug }, + { (PFNRT)RTSemMutexRelease }, + { (PFNRT)RTSemMutexIsOwned }, + { NULL } +}; +#endif /* RT_OS_DARWIN || RT_OS_SOLARIS */ + + +/** + * Reserves memory in which the VMM can relocate any guest mappings + * that are floating around. + * + * This operation is a little bit tricky since the VMM might not accept + * just any address because of address clashes between the three contexts + * it operates in, so use a small stack to perform this operation. + * + * @returns VBox status code (ignored). + * @param pDevExt The device extension. + */ +static int vgdrvInitFixateGuestMappings(PVBOXGUESTDEVEXT pDevExt) +{ + /* + * Query the required space. + */ + VMMDevReqHypervisorInfo *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_GetHypervisorInfo); + if (RT_FAILURE(rc)) + return rc; + pReq->hypervisorStart = 0; + pReq->hypervisorSize = 0; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) /* this shouldn't happen! */ + { + VbglR0GRFree(&pReq->header); + return rc; + } + + /* + * The VMM will report back if there is nothing it wants to map, like for + * instance in VT-x and AMD-V mode. + */ + if (pReq->hypervisorSize == 0) + Log(("vgdrvInitFixateGuestMappings: nothing to do\n")); + else + { + /* + * We have to try several times since the host can be picky + * about certain addresses. + */ + RTR0MEMOBJ hFictive = NIL_RTR0MEMOBJ; + uint32_t cbHypervisor = pReq->hypervisorSize; + RTR0MEMOBJ ahTries[5]; + uint32_t iTry; + bool fBitched = false; + Log(("vgdrvInitFixateGuestMappings: cbHypervisor=%#x\n", cbHypervisor)); + for (iTry = 0; iTry < RT_ELEMENTS(ahTries); iTry++) + { + /* + * Reserve space, or if that isn't supported, create a object for + * some fictive physical memory and map that in to kernel space. + * + * To make the code a bit uglier, most systems cannot help with + * 4MB alignment, so we have to deal with that in addition to + * having two ways of getting the memory. + */ + uint32_t uAlignment = _4M; + RTR0MEMOBJ hObj; + rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M), uAlignment); + if (rc == VERR_NOT_SUPPORTED) + { + uAlignment = PAGE_SIZE; + rc = RTR0MemObjReserveKernel(&hObj, (void *)-1, RT_ALIGN_32(cbHypervisor, _4M) + _4M, uAlignment); + } + /* + * If both RTR0MemObjReserveKernel calls above failed because either not supported or + * not implemented at all at the current platform, try to map the memory object into the + * virtual kernel space. + */ + if (rc == VERR_NOT_SUPPORTED) + { + if (hFictive == NIL_RTR0MEMOBJ) + { + rc = RTR0MemObjEnterPhys(&hObj, VBOXGUEST_HYPERVISOR_PHYSICAL_START, cbHypervisor + _4M, RTMEM_CACHE_POLICY_DONT_CARE); + if (RT_FAILURE(rc)) + break; + hFictive = hObj; + } + uAlignment = _4M; + rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + if (rc == VERR_NOT_SUPPORTED) + { + uAlignment = PAGE_SIZE; + rc = RTR0MemObjMapKernel(&hObj, hFictive, (void *)-1, uAlignment, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + } + } + if (RT_FAILURE(rc)) + { + LogRel(("VBoxGuest: Failed to reserve memory for the hypervisor: rc=%Rrc (cbHypervisor=%#x uAlignment=%#x iTry=%u)\n", + rc, cbHypervisor, uAlignment, iTry)); + fBitched = true; + break; + } + + /* + * Try set it. + */ + pReq->header.requestType = VMMDevReq_SetHypervisorInfo; + pReq->header.rc = VERR_INTERNAL_ERROR; + pReq->hypervisorSize = cbHypervisor; + pReq->hypervisorStart = (RTGCPTR32)(uintptr_t)RTR0MemObjAddress(hObj); + if ( uAlignment == PAGE_SIZE + && pReq->hypervisorStart & (_4M - 1)) + pReq->hypervisorStart = RT_ALIGN_32(pReq->hypervisorStart, _4M); + AssertMsg(RT_ALIGN_32(pReq->hypervisorStart, _4M) == pReq->hypervisorStart, ("%#x\n", pReq->hypervisorStart)); + + rc = VbglR0GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + { + pDevExt->hGuestMappings = hFictive != NIL_RTR0MEMOBJ ? hFictive : hObj; + Log(("VBoxGuest: %p LB %#x; uAlignment=%#x iTry=%u hGuestMappings=%p (%s)\n", + RTR0MemObjAddress(pDevExt->hGuestMappings), + RTR0MemObjSize(pDevExt->hGuestMappings), + uAlignment, iTry, pDevExt->hGuestMappings, hFictive != NIL_RTR0PTR ? "fictive" : "reservation")); + break; + } + ahTries[iTry] = hObj; + } + + /* + * Cleanup failed attempts. + */ + while (iTry-- > 0) + RTR0MemObjFree(ahTries[iTry], false /* fFreeMappings */); + if ( RT_FAILURE(rc) + && hFictive != NIL_RTR0PTR) + RTR0MemObjFree(hFictive, false /* fFreeMappings */); + if (RT_FAILURE(rc) && !fBitched) + LogRel(("VBoxGuest: Warning: failed to reserve %#d of memory for guest mappings.\n", cbHypervisor)); + } + VbglR0GRFree(&pReq->header); + + /* + * We ignore failed attempts for now. + */ + return VINF_SUCCESS; +} + + +/** + * Undo what vgdrvInitFixateGuestMappings did. + * + * @param pDevExt The device extension. + */ +static void vgdrvTermUnfixGuestMappings(PVBOXGUESTDEVEXT pDevExt) +{ + if (pDevExt->hGuestMappings != NIL_RTR0PTR) + { + /* + * Tell the host that we're going to free the memory we reserved for + * it, the free it up. (Leak the memory if anything goes wrong here.) + */ + VMMDevReqHypervisorInfo *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevReqHypervisorInfo), VMMDevReq_SetHypervisorInfo); + if (RT_SUCCESS(rc)) + { + pReq->hypervisorStart = 0; + pReq->hypervisorSize = 0; + rc = VbglR0GRPerform(&pReq->header); + VbglR0GRFree(&pReq->header); + } + if (RT_SUCCESS(rc)) + { + rc = RTR0MemObjFree(pDevExt->hGuestMappings, true /* fFreeMappings */); + AssertRC(rc); + } + else + LogRel(("vgdrvTermUnfixGuestMappings: Failed to unfix the guest mappings! rc=%Rrc\n", rc)); + + pDevExt->hGuestMappings = NIL_RTR0MEMOBJ; + } +} + + + +/** + * Report the guest information to the host. + * + * @returns IPRT status code. + * @param enmOSType The OS type to report. + */ +static int vgdrvReportGuestInfo(VBOXOSTYPE enmOSType) +{ + /* + * Allocate and fill in the two guest info reports. + */ + VMMDevReportGuestInfo2 *pReqInfo2 = NULL; + VMMDevReportGuestInfo *pReqInfo1 = NULL; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqInfo2, sizeof (VMMDevReportGuestInfo2), VMMDevReq_ReportGuestInfo2); + Log(("vgdrvReportGuestInfo: VbglR0GRAlloc VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + pReqInfo2->guestInfo.additionsMajor = VBOX_VERSION_MAJOR; + pReqInfo2->guestInfo.additionsMinor = VBOX_VERSION_MINOR; + pReqInfo2->guestInfo.additionsBuild = VBOX_VERSION_BUILD; + pReqInfo2->guestInfo.additionsRevision = VBOX_SVN_REV; + pReqInfo2->guestInfo.additionsFeatures = VBOXGSTINFO2_F_REQUESTOR_INFO; + RTStrCopy(pReqInfo2->guestInfo.szName, sizeof(pReqInfo2->guestInfo.szName), VBOX_VERSION_STRING); + + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqInfo1, sizeof (VMMDevReportGuestInfo), VMMDevReq_ReportGuestInfo); + Log(("vgdrvReportGuestInfo: VbglR0GRAlloc VMMDevReportGuestInfo completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + pReqInfo1->guestInfo.interfaceVersion = VMMDEV_VERSION; + pReqInfo1->guestInfo.osType = enmOSType; + + /* + * There are two protocols here: + * 1. Info2 + Info1. Supported by >=3.2.51. + * 2. Info1 and optionally Info2. The old protocol. + * + * We try protocol 1 first. It will fail with VERR_NOT_SUPPORTED + * if not supported by the VMMDev (message ordering requirement). + */ + rc = VbglR0GRPerform(&pReqInfo2->header); + Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + rc = VbglR0GRPerform(&pReqInfo1->header); + Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo completed with rc=%Rrc\n", rc)); + } + else if ( rc == VERR_NOT_SUPPORTED + || rc == VERR_NOT_IMPLEMENTED) + { + rc = VbglR0GRPerform(&pReqInfo1->header); + Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + rc = VbglR0GRPerform(&pReqInfo2->header); + Log(("vgdrvReportGuestInfo: VbglR0GRPerform VMMDevReportGuestInfo2 completed with rc=%Rrc\n", rc)); + if (rc == VERR_NOT_IMPLEMENTED) + rc = VINF_SUCCESS; + } + } + VbglR0GRFree(&pReqInfo1->header); + } + VbglR0GRFree(&pReqInfo2->header); + } + + return rc; +} + + +/** + * Report the guest driver status to the host. + * + * @returns IPRT status code. + * @param fActive Flag whether the driver is now active or not. + */ +static int vgdrvReportDriverStatus(bool fActive) +{ + /* + * Report guest status of the VBox driver to the host. + */ + VMMDevReportGuestStatus *pReq2 = NULL; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq2, sizeof(*pReq2), VMMDevReq_ReportGuestStatus); + Log(("vgdrvReportDriverStatus: VbglR0GRAlloc VMMDevReportGuestStatus completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + pReq2->guestStatus.facility = VBoxGuestFacilityType_VBoxGuestDriver; + pReq2->guestStatus.status = fActive ? + VBoxGuestFacilityStatus_Active + : VBoxGuestFacilityStatus_Inactive; + pReq2->guestStatus.flags = 0; + rc = VbglR0GRPerform(&pReq2->header); + Log(("vgdrvReportDriverStatus: VbglR0GRPerform VMMDevReportGuestStatus completed with fActive=%d, rc=%Rrc\n", + fActive ? 1 : 0, rc)); + if (rc == VERR_NOT_IMPLEMENTED) /* Compatibility with older hosts. */ + rc = VINF_SUCCESS; + VbglR0GRFree(&pReq2->header); + } + + return rc; +} + + +/** @name Memory Ballooning + * @{ + */ + +/** + * Inflate the balloon by one chunk represented by an R0 memory object. + * + * The caller owns the balloon mutex. + * + * @returns IPRT status code. + * @param pMemObj Pointer to the R0 memory object. + * @param pReq The pre-allocated request for performing the VMMDev call. + */ +static int vgdrvBalloonInflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq) +{ + uint32_t iPage; + int rc; + + for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++) + { + RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage); + pReq->aPhysPage[iPage] = phys; + } + + pReq->fInflate = true; + pReq->header.size = g_cbChangeMemBalloonReq; + pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; + + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + LogRel(("vgdrvBalloonInflate: VbglR0GRPerform failed. rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Deflate the balloon by one chunk - info the host and free the memory object. + * + * The caller owns the balloon mutex. + * + * @returns IPRT status code. + * @param pMemObj Pointer to the R0 memory object. + * The memory object will be freed afterwards. + * @param pReq The pre-allocated request for performing the VMMDev call. + */ +static int vgdrvBalloonDeflate(PRTR0MEMOBJ pMemObj, VMMDevChangeMemBalloon *pReq) +{ + uint32_t iPage; + int rc; + + for (iPage = 0; iPage < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; iPage++) + { + RTHCPHYS phys = RTR0MemObjGetPagePhysAddr(*pMemObj, iPage); + pReq->aPhysPage[iPage] = phys; + } + + pReq->fInflate = false; + pReq->header.size = g_cbChangeMemBalloonReq; + pReq->cPages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; + + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + { + LogRel(("vgdrvBalloonDeflate: VbglR0GRPerform failed. rc=%Rrc\n", rc)); + return rc; + } + + rc = RTR0MemObjFree(*pMemObj, true); + if (RT_FAILURE(rc)) + { + LogRel(("vgdrvBalloonDeflate: RTR0MemObjFree(%p,true) -> %Rrc; this is *BAD*!\n", *pMemObj, rc)); + return rc; + } + + *pMemObj = NIL_RTR0MEMOBJ; + return VINF_SUCCESS; +} + + +/** + * Inflate/deflate the memory balloon and notify the host. + * + * This is a worker used by vgdrvIoCtl_CheckMemoryBalloon - it takes the mutex. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param cBalloonChunks The new size of the balloon in chunks of 1MB. + * @param pfHandleInR3 Where to return the handle-in-ring3 indicator + * (VINF_SUCCESS if set). + */ +static int vgdrvSetBalloonSizeKernel(PVBOXGUESTDEVEXT pDevExt, uint32_t cBalloonChunks, bool *pfHandleInR3) +{ + int rc = VINF_SUCCESS; + + if (pDevExt->MemBalloon.fUseKernelAPI) + { + VMMDevChangeMemBalloon *pReq; + uint32_t i; + + if (cBalloonChunks > pDevExt->MemBalloon.cMaxChunks) + { + LogRel(("vgdrvSetBalloonSizeKernel: illegal balloon size %u (max=%u)\n", + cBalloonChunks, pDevExt->MemBalloon.cMaxChunks)); + return VERR_INVALID_PARAMETER; + } + + if (cBalloonChunks == pDevExt->MemBalloon.cMaxChunks) + return VINF_SUCCESS; /* nothing to do */ + + if ( cBalloonChunks > pDevExt->MemBalloon.cChunks + && !pDevExt->MemBalloon.paMemObj) + { + pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAllocZ(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks); + if (!pDevExt->MemBalloon.paMemObj) + { + LogRel(("vgdrvSetBalloonSizeKernel: no memory for paMemObj!\n")); + return VERR_NO_MEMORY; + } + } + + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon); + if (RT_FAILURE(rc)) + return rc; + + if (cBalloonChunks > pDevExt->MemBalloon.cChunks) + { + /* inflate */ + for (i = pDevExt->MemBalloon.cChunks; i < cBalloonChunks; i++) + { + rc = RTR0MemObjAllocPhysNC(&pDevExt->MemBalloon.paMemObj[i], + VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, NIL_RTHCPHYS); + if (RT_FAILURE(rc)) + { + if (rc == VERR_NOT_SUPPORTED) + { + /* not supported -- fall back to the R3-allocated memory. */ + rc = VINF_SUCCESS; + pDevExt->MemBalloon.fUseKernelAPI = false; + Assert(pDevExt->MemBalloon.cChunks == 0); + Log(("VBoxGuestSetBalloonSizeKernel: PhysNC allocs not supported, falling back to R3 allocs.\n")); + } + /* else if (rc == VERR_NO_MEMORY || rc == VERR_NO_PHYS_MEMORY): + * cannot allocate more memory => don't try further, just stop here */ + /* else: XXX what else can fail? VERR_MEMOBJ_INIT_FAILED for instance. just stop. */ + break; + } + + rc = vgdrvBalloonInflate(&pDevExt->MemBalloon.paMemObj[i], pReq); + if (RT_FAILURE(rc)) + { + Log(("vboxGuestSetBalloonSize(inflate): failed, rc=%Rrc!\n", rc)); + RTR0MemObjFree(pDevExt->MemBalloon.paMemObj[i], true); + pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ; + break; + } + pDevExt->MemBalloon.cChunks++; + } + } + else + { + /* deflate */ + for (i = pDevExt->MemBalloon.cChunks; i-- > cBalloonChunks;) + { + rc = vgdrvBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq); + if (RT_FAILURE(rc)) + { + Log(("vboxGuestSetBalloonSize(deflate): failed, rc=%Rrc!\n", rc)); + break; + } + pDevExt->MemBalloon.cChunks--; + } + } + + VbglR0GRFree(&pReq->header); + } + + /* + * Set the handle-in-ring3 indicator. When set Ring-3 will have to work + * the balloon changes via the other API. + */ + *pfHandleInR3 = pDevExt->MemBalloon.fUseKernelAPI ? false : true; + + return rc; +} + + +/** + * Inflate/deflate the balloon by one chunk. + * + * Worker for vgdrvIoCtl_ChangeMemoryBalloon - it takes the mutex. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param pvChunk The address of the chunk to add to / remove from the + * balloon. (user space address) + * @param fInflate Inflate if true, deflate if false. + */ +static int vgdrvSetBalloonSizeFromUser(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, RTR3PTR pvChunk, bool fInflate) +{ + VMMDevChangeMemBalloon *pReq; + PRTR0MEMOBJ pMemObj = NULL; + int rc = VINF_SUCCESS; + uint32_t i; + RT_NOREF1(pSession); + + if (fInflate) + { + if ( pDevExt->MemBalloon.cChunks > pDevExt->MemBalloon.cMaxChunks - 1 + || pDevExt->MemBalloon.cMaxChunks == 0 /* If called without first querying. */) + { + LogRel(("vgdrvSetBalloonSizeFromUser: cannot inflate balloon, already have %u chunks (max=%u)\n", + pDevExt->MemBalloon.cChunks, pDevExt->MemBalloon.cMaxChunks)); + return VERR_INVALID_PARAMETER; + } + + if (!pDevExt->MemBalloon.paMemObj) + { + pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAlloc(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks); + if (!pDevExt->MemBalloon.paMemObj) + { + LogRel(("vgdrvSetBalloonSizeFromUser: no memory for paMemObj!\n")); + return VERR_NO_MEMORY; + } + for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++) + pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ; + } + } + else + { + if (pDevExt->MemBalloon.cChunks == 0) + { + AssertMsgFailed(("vgdrvSetBalloonSizeFromUser: cannot decrease balloon, already at size 0\n")); + return VERR_INVALID_PARAMETER; + } + } + + /* + * Enumerate all memory objects and check if the object is already registered. + */ + for (i = 0; i < pDevExt->MemBalloon.cMaxChunks; i++) + { + if ( fInflate + && !pMemObj + && pDevExt->MemBalloon.paMemObj[i] == NIL_RTR0MEMOBJ) + pMemObj = &pDevExt->MemBalloon.paMemObj[i]; /* found free object pointer */ + if (RTR0MemObjAddressR3(pDevExt->MemBalloon.paMemObj[i]) == pvChunk) + { + if (fInflate) + return VERR_ALREADY_EXISTS; /* don't provide the same memory twice */ + pMemObj = &pDevExt->MemBalloon.paMemObj[i]; + break; + } + } + if (!pMemObj) + { + if (fInflate) + { + /* no free object pointer found -- should not happen */ + return VERR_NO_MEMORY; + } + + /* cannot free this memory as it wasn't provided before */ + return VERR_NOT_FOUND; + } + + /* + * Try inflate / default the balloon as requested. + */ + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon); + if (RT_FAILURE(rc)) + return rc; + pReq->header.fRequestor = pSession->fRequestor; + + if (fInflate) + { + rc = RTR0MemObjLockUser(pMemObj, pvChunk, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, + RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS); + if (RT_SUCCESS(rc)) + { + rc = vgdrvBalloonInflate(pMemObj, pReq); + if (RT_SUCCESS(rc)) + pDevExt->MemBalloon.cChunks++; + else + { + Log(("vgdrvSetBalloonSizeFromUser(inflate): failed, rc=%Rrc!\n", rc)); + RTR0MemObjFree(*pMemObj, true); + *pMemObj = NIL_RTR0MEMOBJ; + } + } + } + else + { + rc = vgdrvBalloonDeflate(pMemObj, pReq); + if (RT_SUCCESS(rc)) + pDevExt->MemBalloon.cChunks--; + else + Log(("vgdrvSetBalloonSizeFromUser(deflate): failed, rc=%Rrc!\n", rc)); + } + + VbglR0GRFree(&pReq->header); + return rc; +} + + +/** + * Cleanup the memory balloon of a session. + * + * Will request the balloon mutex, so it must be valid and the caller must not + * own it already. + * + * @param pDevExt The device extension. + * @param pSession The session. Can be NULL at unload. + */ +static void vgdrvCloseMemBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx); + if ( pDevExt->MemBalloon.pOwner == pSession + || pSession == NULL /*unload*/) + { + if (pDevExt->MemBalloon.paMemObj) + { + VMMDevChangeMemBalloon *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, g_cbChangeMemBalloonReq, VMMDevReq_ChangeMemBalloon); + if (RT_SUCCESS(rc)) + { + /* fRequestor is kernel here, as we're cleaning up. */ + + uint32_t i; + for (i = pDevExt->MemBalloon.cChunks; i-- > 0;) + { + rc = vgdrvBalloonDeflate(&pDevExt->MemBalloon.paMemObj[i], pReq); + if (RT_FAILURE(rc)) + { + LogRel(("vgdrvCloseMemBalloon: Deflate failed with rc=%Rrc. Will leak %u chunks.\n", + rc, pDevExt->MemBalloon.cChunks)); + break; + } + pDevExt->MemBalloon.paMemObj[i] = NIL_RTR0MEMOBJ; + pDevExt->MemBalloon.cChunks--; + } + VbglR0GRFree(&pReq->header); + } + else + LogRel(("vgdrvCloseMemBalloon: Failed to allocate VMMDev request buffer (rc=%Rrc). Will leak %u chunks.\n", + rc, pDevExt->MemBalloon.cChunks)); + RTMemFree(pDevExt->MemBalloon.paMemObj); + pDevExt->MemBalloon.paMemObj = NULL; + } + + pDevExt->MemBalloon.pOwner = NULL; + } + RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx); +} + +/** @} */ + + + +/** @name Heartbeat + * @{ + */ + +/** + * Sends heartbeat to host. + * + * @returns VBox status code. + */ +static int vgdrvHeartbeatSend(PVBOXGUESTDEVEXT pDevExt) +{ + int rc; + if (pDevExt->pReqGuestHeartbeat) + { + rc = VbglR0GRPerform(pDevExt->pReqGuestHeartbeat); + Log3(("vgdrvHeartbeatSend: VbglR0GRPerform vgdrvHeartbeatSend completed with rc=%Rrc\n", rc)); + } + else + rc = VERR_INVALID_STATE; + return rc; +} + + +/** + * Callback for heartbeat timer. + */ +static DECLCALLBACK(void) vgdrvHeartbeatTimerHandler(PRTTIMER hTimer, void *pvUser, uint64_t iTick) +{ + PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser; + int rc; + AssertReturnVoid(pDevExt); + + rc = vgdrvHeartbeatSend(pDevExt); + if (RT_FAILURE(rc)) + Log(("HB Timer: vgdrvHeartbeatSend failed: rc=%Rrc\n", rc)); + + NOREF(hTimer); NOREF(iTick); +} + + +/** + * Configure the host to check guest's heartbeat + * and get heartbeat interval from the host. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param fEnabled Set true to enable guest heartbeat checks on host. + */ +static int vgdrvHeartbeatHostConfigure(PVBOXGUESTDEVEXT pDevExt, bool fEnabled) +{ + VMMDevReqHeartbeat *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_HeartbeatConfigure); + Log(("vgdrvHeartbeatHostConfigure: VbglR0GRAlloc vgdrvHeartbeatHostConfigure completed with rc=%Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + pReq->fEnabled = fEnabled; + pReq->cNsInterval = 0; + rc = VbglR0GRPerform(&pReq->header); + Log(("vgdrvHeartbeatHostConfigure: VbglR0GRPerform vgdrvHeartbeatHostConfigure completed with rc=%Rrc\n", rc)); + pDevExt->cNsHeartbeatInterval = pReq->cNsInterval; + VbglR0GRFree(&pReq->header); + } + return rc; +} + + +/** + * Initializes the heartbeat timer. + * + * This feature may be disabled by the host. + * + * @returns VBox status (ignored). + * @param pDevExt The device extension. + */ +static int vgdrvHeartbeatInit(PVBOXGUESTDEVEXT pDevExt) +{ + /* + * Make sure that heartbeat checking is disabled. + */ + int rc = vgdrvHeartbeatHostConfigure(pDevExt, false); + if (RT_SUCCESS(rc)) + { + rc = vgdrvHeartbeatHostConfigure(pDevExt, true); + if (RT_SUCCESS(rc)) + { + /* + * Preallocate the request to use it from the timer callback because: + * 1) on Windows VbglR0GRAlloc must be called at IRQL <= APC_LEVEL + * and the timer callback runs at DISPATCH_LEVEL; + * 2) avoid repeated allocations. + */ + rc = VbglR0GRAlloc(&pDevExt->pReqGuestHeartbeat, sizeof(*pDevExt->pReqGuestHeartbeat), VMMDevReq_GuestHeartbeat); + if (RT_SUCCESS(rc)) + { + LogRel(("vgdrvHeartbeatInit: Setting up heartbeat to trigger every %RU64 milliseconds\n", + pDevExt->cNsHeartbeatInterval / RT_NS_1MS)); + rc = RTTimerCreateEx(&pDevExt->pHeartbeatTimer, pDevExt->cNsHeartbeatInterval, 0 /*fFlags*/, + (PFNRTTIMER)vgdrvHeartbeatTimerHandler, pDevExt); + if (RT_SUCCESS(rc)) + { + rc = RTTimerStart(pDevExt->pHeartbeatTimer, 0); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + LogRel(("vgdrvHeartbeatInit: Heartbeat timer failed to start, rc=%Rrc\n", rc)); + } + else + LogRel(("vgdrvHeartbeatInit: Failed to create heartbeat timer: %Rrc\n", rc)); + + VbglR0GRFree(pDevExt->pReqGuestHeartbeat); + pDevExt->pReqGuestHeartbeat = NULL; + } + else + LogRel(("vgdrvHeartbeatInit: VbglR0GRAlloc(VMMDevReq_GuestHeartbeat): %Rrc\n", rc)); + + LogRel(("vgdrvHeartbeatInit: Failed to set up the timer, guest heartbeat is disabled\n")); + vgdrvHeartbeatHostConfigure(pDevExt, false); + } + else + LogRel(("vgdrvHeartbeatInit: Failed to configure host for heartbeat checking: rc=%Rrc\n", rc)); + } + return rc; +} + +/** @} */ + + +/** + * Helper to reinit the VMMDev communication after hibernation. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param enmOSType The OS type. + * + * @todo Call this on all platforms, not just windows. + */ +int VGDrvCommonReinitDevExtAfterHibernation(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType) +{ + int rc = vgdrvReportGuestInfo(enmOSType); + if (RT_SUCCESS(rc)) + { + rc = vgdrvReportDriverStatus(true /* Driver is active */); + if (RT_FAILURE(rc)) + Log(("VGDrvCommonReinitDevExtAfterHibernation: could not report guest driver status, rc=%Rrc\n", rc)); + } + else + Log(("VGDrvCommonReinitDevExtAfterHibernation: could not report guest information to host, rc=%Rrc\n", rc)); + LogFlow(("VGDrvCommonReinitDevExtAfterHibernation: returned with rc=%Rrc\n", rc)); + RT_NOREF1(pDevExt); + return rc; +} + + +/** + * Initializes the release logger (debug is implicit), if configured. + * + * @returns IPRT status code. + */ +int VGDrvCommonInitLoggers(void) +{ +#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER + /* + * Create the release log. + */ + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + int rc = RTLogCreate(&pRelLogger, 0 /*fFlags*/, "all", "VBOXGUEST_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, + RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL); + if (RT_SUCCESS(rc)) + RTLogRelSetDefaultInstance(pRelLogger); + /** @todo Add native hook for getting logger config parameters and setting + * them. On linux we should use the module parameter stuff... */ + return rc; +#else + return VINF_SUCCESS; +#endif +} + + +/** + * Destroys the loggers. + */ +void VGDrvCommonDestroyLoggers(void) +{ +#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif +} + + +/** + * Initialize the device extension fundament. + * + * There are no device resources at this point, VGDrvCommonInitDevExtResources + * should be called when they are available. + * + * @returns VBox status code. + * @param pDevExt The device extension to init. + */ +int VGDrvCommonInitDevExtFundament(PVBOXGUESTDEVEXT pDevExt) +{ + int rc; + AssertMsg( pDevExt->uInitState != VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT + && pDevExt->uInitState != VBOXGUESTDEVEXT_INIT_STATE_RESOURCES, ("uInitState=%#x\n", pDevExt->uInitState)); + + /* + * Initialize the data. + */ + pDevExt->IOPortBase = UINT16_MAX; + pDevExt->pVMMDevMemory = NULL; + pDevExt->hGuestMappings = NIL_RTR0MEMOBJ; + pDevExt->EventSpinlock = NIL_RTSPINLOCK; + pDevExt->fHostFeatures = 0; + pDevExt->pIrqAckEvents = NULL; + pDevExt->PhysIrqAckEvents = NIL_RTCCPHYS; + RTListInit(&pDevExt->WaitList); +#ifdef VBOX_WITH_HGCM + RTListInit(&pDevExt->HGCMWaitList); +#endif +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + RTListInit(&pDevExt->WakeUpList); +#endif + RTListInit(&pDevExt->WokenUpList); + RTListInit(&pDevExt->FreeList); + RTListInit(&pDevExt->SessionList); + pDevExt->cSessions = 0; + pDevExt->fLoggingEnabled = false; + pDevExt->f32PendingEvents = 0; + pDevExt->u32MousePosChangedSeq = 0; + pDevExt->SessionSpinlock = NIL_RTSPINLOCK; + pDevExt->MemBalloon.hMtx = NIL_RTSEMFASTMUTEX; + pDevExt->MemBalloon.cChunks = 0; + pDevExt->MemBalloon.cMaxChunks = 0; + pDevExt->MemBalloon.fUseKernelAPI = true; + pDevExt->MemBalloon.paMemObj = NULL; + pDevExt->MemBalloon.pOwner = NULL; + pDevExt->pfnMouseNotifyCallback = NULL; + pDevExt->pvMouseNotifyCallbackArg = NULL; + pDevExt->pReqGuestHeartbeat = NULL; + + pDevExt->fFixedEvents = 0; + vgdrvBitUsageTrackerClear(&pDevExt->EventFilterTracker); + pDevExt->fEventFilterHost = UINT32_MAX; /* forces a report */ + + vgdrvBitUsageTrackerClear(&pDevExt->MouseStatusTracker); + pDevExt->fMouseStatusHost = UINT32_MAX; /* forces a report */ + + pDevExt->fAcquireModeGuestCaps = 0; + pDevExt->fSetModeGuestCaps = 0; + pDevExt->fAcquiredGuestCaps = 0; + vgdrvBitUsageTrackerClear(&pDevExt->SetGuestCapsTracker); + pDevExt->fGuestCapsHost = UINT32_MAX; /* forces a report */ + + /* + * Create the wait and session spinlocks as well as the ballooning mutex. + */ + rc = RTSpinlockCreate(&pDevExt->EventSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestEvent"); + if (RT_SUCCESS(rc)) + { + rc = RTSpinlockCreate(&pDevExt->SessionSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestSession"); + if (RT_SUCCESS(rc)) + { + rc = RTSemFastMutexCreate(&pDevExt->MemBalloon.hMtx); + if (RT_SUCCESS(rc)) + { + pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT; + return VINF_SUCCESS; + } + + LogRel(("VGDrvCommonInitDevExt: failed to create mutex, rc=%Rrc!\n", rc)); + RTSpinlockDestroy(pDevExt->SessionSpinlock); + } + else + LogRel(("VGDrvCommonInitDevExt: failed to create spinlock, rc=%Rrc!\n", rc)); + RTSpinlockDestroy(pDevExt->EventSpinlock); + } + else + LogRel(("VGDrvCommonInitDevExt: failed to create spinlock, rc=%Rrc!\n", rc)); + + pDevExt->uInitState = 0; + return rc; +} + + +/** + * Counter to VGDrvCommonInitDevExtFundament. + * + * @param pDevExt The device extension. + */ +void VGDrvCommonDeleteDevExtFundament(PVBOXGUESTDEVEXT pDevExt) +{ + int rc2; + AssertMsgReturnVoid(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT, ("uInitState=%#x\n", pDevExt->uInitState)); + pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_DELETED; + + rc2 = RTSemFastMutexDestroy(pDevExt->MemBalloon.hMtx); AssertRC(rc2); + rc2 = RTSpinlockDestroy(pDevExt->EventSpinlock); AssertRC(rc2); + rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2); +} + + +/** + * Initializes the VBoxGuest device extension resource parts. + * + * The native code locates the VMMDev on the PCI bus and retrieve the MMIO and + * I/O port ranges, this function will take care of mapping the MMIO memory (if + * present). Upon successful return the native code should set up the interrupt + * handler. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. Allocated by the native code. + * @param IOPortBase The base of the I/O port range. + * @param pvMMIOBase The base of the MMIO memory mapping. + * This is optional, pass NULL if not present. + * @param cbMMIO The size of the MMIO memory mapping. + * This is optional, pass 0 if not present. + * @param enmOSType The guest OS type to report to the VMMDev. + * @param fFixedEvents Events that will be enabled upon init and no client + * will ever be allowed to mask. + */ +int VGDrvCommonInitDevExtResources(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, + void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents) +{ + int rc; + AssertMsgReturn(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT, ("uInitState=%#x\n", pDevExt->uInitState), + VERR_INVALID_STATE); + + /* + * If there is an MMIO region validate the version and size. + */ + if (pvMMIOBase) + { + VMMDevMemory *pVMMDev = (VMMDevMemory *)pvMMIOBase; + Assert(cbMMIO); + if ( pVMMDev->u32Version == VMMDEV_MEMORY_VERSION + && pVMMDev->u32Size >= 32 + && pVMMDev->u32Size <= cbMMIO) + { + pDevExt->pVMMDevMemory = pVMMDev; + Log(("VGDrvCommonInitDevExtResources: VMMDevMemory: mapping=%p size=%#RX32 (%#RX32) version=%#RX32\n", + pVMMDev, pVMMDev->u32Size, cbMMIO, pVMMDev->u32Version)); + } + else /* try live without it. */ + LogRel(("VGDrvCommonInitDevExtResources: Bogus VMMDev memory; u32Version=%RX32 (expected %RX32) u32Size=%RX32 (expected <= %RX32)\n", + pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size, cbMMIO)); + } + + /* + * Initialize the guest library and report the guest info back to VMMDev, + * set the interrupt control filter mask, and fixate the guest mappings + * made by the VMM. + */ + pDevExt->IOPortBase = IOPortBase; + rc = VbglR0InitPrimary(pDevExt->IOPortBase, (VMMDevMemory *)pDevExt->pVMMDevMemory, &pDevExt->fHostFeatures); + if (RT_SUCCESS(rc)) + { + VMMDevRequestHeader *pAckReq = NULL; + rc = VbglR0GRAlloc(&pAckReq, sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents); + if (RT_SUCCESS(rc)) + { + pDevExt->PhysIrqAckEvents = VbglR0PhysHeapGetPhysAddr(pAckReq); + Assert(pDevExt->PhysIrqAckEvents != 0); + ASMCompilerBarrier(); /* linux + solaris already have IRQs hooked up at this point, so take care. */ + pDevExt->pIrqAckEvents = (VMMDevEvents *)pAckReq; + + rc = vgdrvReportGuestInfo(enmOSType); + if (RT_SUCCESS(rc)) + { + /* + * Set the fixed event and make sure the host doesn't have any lingering + * the guest capabilities or mouse status bits set. + */ +#ifdef VBOX_WITH_HGCM + fFixedEvents |= VMMDEV_EVENT_HGCM; +#endif + pDevExt->fFixedEvents = fFixedEvents; + rc = vgdrvResetEventFilterOnHost(pDevExt, fFixedEvents); + if (RT_SUCCESS(rc)) + { + rc = vgdrvResetCapabilitiesOnHost(pDevExt); + if (RT_SUCCESS(rc)) + { + rc = vgdrvResetMouseStatusOnHost(pDevExt); + if (RT_SUCCESS(rc)) + { + /* + * Initialize stuff which may fail without requiring the driver init to fail. + */ + vgdrvInitFixateGuestMappings(pDevExt); + vgdrvHeartbeatInit(pDevExt); + + /* + * Done! + */ + rc = vgdrvReportDriverStatus(true /* Driver is active */); + if (RT_FAILURE(rc)) + LogRel(("VGDrvCommonInitDevExtResources: VBoxReportGuestDriverStatus failed, rc=%Rrc\n", rc)); + + pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_RESOURCES; + LogFlowFunc(("VGDrvCommonInitDevExtResources: returns success\n")); + return VINF_SUCCESS; + } + LogRel(("VGDrvCommonInitDevExtResources: failed to clear mouse status: rc=%Rrc\n", rc)); + } + else + LogRel(("VGDrvCommonInitDevExtResources: failed to clear guest capabilities: rc=%Rrc\n", rc)); + } + else + LogRel(("VGDrvCommonInitDevExtResources: failed to set fixed event filter: rc=%Rrc\n", rc)); + pDevExt->fFixedEvents = 0; + } + else + LogRel(("VGDrvCommonInitDevExtResources: vgdrvReportGuestInfo failed: rc=%Rrc\n", rc)); + VbglR0GRFree((VMMDevRequestHeader *)pDevExt->pIrqAckEvents); + } + else + LogRel(("VGDrvCommonInitDevExtResources: VbglR0GRAlloc failed: rc=%Rrc\n", rc)); + + VbglR0TerminatePrimary(); + } + else + LogRel(("VGDrvCommonInitDevExtResources: VbglR0InitPrimary failed: rc=%Rrc\n", rc)); + pDevExt->IOPortBase = UINT16_MAX; + return rc; +} + + +/** + * Deletes all the items in a wait chain. + * @param pList The head of the chain. + */ +static void vgdrvDeleteWaitList(PRTLISTNODE pList) +{ + while (!RTListIsEmpty(pList)) + { + int rc2; + PVBOXGUESTWAIT pWait = RTListGetFirst(pList, VBOXGUESTWAIT, ListNode); + RTListNodeRemove(&pWait->ListNode); + + rc2 = RTSemEventMultiDestroy(pWait->Event); AssertRC(rc2); + pWait->Event = NIL_RTSEMEVENTMULTI; + pWait->pSession = NULL; + RTMemFree(pWait); + } +} + + +/** + * Counter to VGDrvCommonInitDevExtResources. + * + * @param pDevExt The device extension. + */ +void VGDrvCommonDeleteDevExtResources(PVBOXGUESTDEVEXT pDevExt) +{ + Log(("VGDrvCommonDeleteDevExtResources:\n")); + AssertMsgReturnVoid(pDevExt->uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES, ("uInitState=%#x\n", pDevExt->uInitState)); + pDevExt->uInitState = VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT; + + /* + * Stop and destroy HB timer and disable host heartbeat checking. + */ + if (pDevExt->pHeartbeatTimer) + { + RTTimerDestroy(pDevExt->pHeartbeatTimer); + vgdrvHeartbeatHostConfigure(pDevExt, false); + } + + VbglR0GRFree(pDevExt->pReqGuestHeartbeat); + pDevExt->pReqGuestHeartbeat = NULL; + + /* + * Clean up the bits that involves the host first. + */ + vgdrvTermUnfixGuestMappings(pDevExt); + if (!RTListIsEmpty(&pDevExt->SessionList)) + { + LogRelFunc(("session list not empty!\n")); + RTListInit(&pDevExt->SessionList); + } + + /* + * Update the host flags (mouse status etc) not to reflect this session. + */ + pDevExt->fFixedEvents = 0; + vgdrvResetEventFilterOnHost(pDevExt, 0 /*fFixedEvents*/); + vgdrvResetCapabilitiesOnHost(pDevExt); + vgdrvResetMouseStatusOnHost(pDevExt); + + vgdrvCloseMemBalloon(pDevExt, (PVBOXGUESTSESSION)NULL); + + /* + * No more IRQs. + */ + pDevExt->pIrqAckEvents = NULL; /* Will be freed by VbglR0TerminatePrimary. */ + ASMAtomicWriteU32(&pDevExt->fHostFeatures, 0); + + /* + * Cleanup all the other resources. + */ + vgdrvDeleteWaitList(&pDevExt->WaitList); +#ifdef VBOX_WITH_HGCM + vgdrvDeleteWaitList(&pDevExt->HGCMWaitList); +#endif +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + vgdrvDeleteWaitList(&pDevExt->WakeUpList); +#endif + vgdrvDeleteWaitList(&pDevExt->WokenUpList); + vgdrvDeleteWaitList(&pDevExt->FreeList); + + VbglR0TerminatePrimary(); + + + pDevExt->pVMMDevMemory = NULL; + pDevExt->IOPortBase = 0; +} + + +/** + * Initializes the VBoxGuest device extension when the device driver is loaded. + * + * The native code locates the VMMDev on the PCI bus and retrieve the MMIO and + * I/O port ranges, this function will take care of mapping the MMIO memory (if + * present). Upon successful return the native code should set up the interrupt + * handler. + * + * Instead of calling this method, the host specific code choose to perform a + * more granular initialization using: + * 1. VGDrvCommonInitLoggers + * 2. VGDrvCommonInitDevExtFundament + * 3. VGDrvCommonInitDevExtResources + * + * @returns VBox status code. + * + * @param pDevExt The device extension. Allocated by the native code. + * @param IOPortBase The base of the I/O port range. + * @param pvMMIOBase The base of the MMIO memory mapping. + * This is optional, pass NULL if not present. + * @param cbMMIO The size of the MMIO memory mapping. + * This is optional, pass 0 if not present. + * @param enmOSType The guest OS type to report to the VMMDev. + * @param fFixedEvents Events that will be enabled upon init and no client + * will ever be allowed to mask. + */ +int VGDrvCommonInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, + void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents) +{ + int rc; + VGDrvCommonInitLoggers(); + + rc = VGDrvCommonInitDevExtFundament(pDevExt); + if (RT_SUCCESS(rc)) + { + rc = VGDrvCommonInitDevExtResources(pDevExt, IOPortBase, pvMMIOBase, cbMMIO, enmOSType, fFixedEvents); + if (RT_SUCCESS(rc)) + return rc; + + VGDrvCommonDeleteDevExtFundament(pDevExt); + } + VGDrvCommonDestroyLoggers(); + return rc; /* (failed) */ +} + + +/** + * Checks if the given option can be taken to not mean 'false'. + * + * @returns true or false accordingly. + * @param pszValue The value to consider. + */ +bool VBDrvCommonIsOptionValueTrue(const char *pszValue) +{ + if (pszValue) + { + char ch; + while ( (ch = *pszValue) != '\0' + && RT_C_IS_SPACE(ch)) + pszValue++; + + return ch != '\0' + && ch != 'n' /* no */ + && ch != 'N' /* NO */ + && ch != 'd' /* disabled */ + && ch != 'f' /* false*/ + && ch != 'F' /* FALSE */ + && ch != 'D' /* DISABLED */ + && ( (ch != 'o' && ch != 'O') /* off, OFF, Off */ + || (pszValue[1] != 'f' && pszValue[1] != 'F') ) + && (ch != '0' || pszValue[1] != '\0') /* '0' */ + ; + } + return false; +} + + +/** + * Processes a option. + * + * This will let the OS specific code have a go at it too. + * + * @param pDevExt The device extension. + * @param pszName The option name, sans prefix. + * @param pszValue The option value. + */ +void VGDrvCommonProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue) +{ + Log(("VGDrvCommonProcessOption: pszName='%s' pszValue='%s'\n", pszName, pszValue)); + + if ( RTStrICmpAscii(pszName, "r3_log_to_host") == 0 + || RTStrICmpAscii(pszName, "LoggingEnabled") == 0 /*legacy*/ ) + pDevExt->fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue); + else if ( RTStrNICmpAscii(pszName, RT_STR_TUPLE("log")) == 0 + || RTStrNICmpAscii(pszName, RT_STR_TUPLE("dbg_log")) == 0) + { + bool const fDbgRel = *pszName == 'd' || *pszName == 'D'; + const char *pszSubName = &pszName[fDbgRel ? 4 + 3 : 3]; + if ( !*pszSubName + || RTStrICmpAscii(pszSubName, "_flags") == 0 + || RTStrICmpAscii(pszSubName, "_dest") == 0) + { + PRTLOGGER pLogger = !fDbgRel ? RTLogRelGetDefaultInstance() : RTLogDefaultInstance(); + if (pLogger) + { + if (!*pszSubName) + RTLogGroupSettings(pLogger, pszValue); + else if (RTStrICmpAscii(pszSubName, "_flags")) + RTLogFlags(pLogger, pszValue); + else + RTLogDestinations(pLogger, pszValue); + } + } + else if (!VGDrvNativeProcessOption(pDevExt, pszName, pszValue)) + LogRel(("VBoxGuest: Ignoring unknown option '%s' (value '%s')\n", pszName, pszValue)); + } + else if (!VGDrvNativeProcessOption(pDevExt, pszName, pszValue)) + LogRel(("VBoxGuest: Ignoring unknown option '%s' (value '%s')\n", pszName, pszValue)); +} + + +/** + * Read driver configuration from the host. + * + * This involves connecting to the guest properties service, which means that + * interrupts needs to work and that the calling thread must be able to block. + * + * @param pDevExt The device extension. + */ +void VGDrvCommonProcessOptionsFromHost(PVBOXGUESTDEVEXT pDevExt) +{ + /* + * Create a kernel session without our selves, then connect to the HGCM service. + */ + PVBOXGUESTSESSION pSession; + int rc = VGDrvCommonCreateKernelSession(pDevExt, &pSession); + if (RT_SUCCESS(rc)) + { + union + { + VBGLIOCHGCMCONNECT Connect; + VBGLIOCHGCMDISCONNECT Disconnect; + GuestPropMsgEnumProperties EnumMsg; + } uBuf; + + RT_ZERO(uBuf.Connect); + VBGLREQHDR_INIT(&uBuf.Connect.Hdr, HGCM_CONNECT); + uBuf.Connect.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + RTStrCopy(uBuf.Connect.u.In.Loc.u.host.achName, sizeof(uBuf.Connect.u.In.Loc.u.host.achName), + "VBoxGuestPropSvc"); /** @todo Add a define to the header for the name. */ + rc = VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_CONNECT, pDevExt, pSession, &uBuf.Connect.Hdr, sizeof(uBuf.Connect)); + if (RT_SUCCESS(rc)) + { + static const char g_szzPattern[] = "/VirtualBox/GuestAdd/VBoxGuest/*\0"; + uint32_t const idClient = uBuf.Connect.u.Out.idClient; + char *pszzStrings = NULL; + uint32_t cbStrings; + + /* + * Enumerate all the relevant properties. We try with a 1KB buffer, but + * will double it until we get what we want or go beyond 16KB. + */ + for (cbStrings = _1K; cbStrings <= _16K; cbStrings *= 2) + { + pszzStrings = (char *)RTMemAllocZ(cbStrings); + if (pszzStrings) + { + VBGL_HGCM_HDR_INIT(&uBuf.EnumMsg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3); + + uBuf.EnumMsg.patterns.type = VMMDevHGCMParmType_LinAddr; + uBuf.EnumMsg.patterns.u.Pointer.size = sizeof(g_szzPattern); + uBuf.EnumMsg.patterns.u.Pointer.u.linearAddr = (uintptr_t)g_szzPattern; + + uBuf.EnumMsg.strings.type = VMMDevHGCMParmType_LinAddr; + uBuf.EnumMsg.strings.u.Pointer.size = cbStrings; + uBuf.EnumMsg.strings.u.Pointer.u.linearAddr = (uintptr_t)pszzStrings; + + uBuf.EnumMsg.size.type = VMMDevHGCMParmType_32bit; + uBuf.EnumMsg.size.u.value32 = 0; + + rc = VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_CALL(sizeof(uBuf.EnumMsg)), pDevExt, pSession, + &uBuf.EnumMsg.hdr.Hdr, sizeof(uBuf.EnumMsg)); + if (RT_SUCCESS(rc)) + { + if ( uBuf.EnumMsg.size.type == VMMDevHGCMParmType_32bit + && uBuf.EnumMsg.size.u.value32 <= cbStrings + && uBuf.EnumMsg.size.u.value32 > 0) + cbStrings = uBuf.EnumMsg.size.u.value32; + Log(("VGDrvCommonReadConfigurationFromHost: GUEST_PROP_FN_ENUM_PROPS -> %#x bytes (cbStrings=%#x)\n", + uBuf.EnumMsg.size.u.value32, cbStrings)); + break; + } + + RTMemFree(pszzStrings); + pszzStrings = NULL; + } + else + { + LogRel(("VGDrvCommonReadConfigurationFromHost: failed to allocate %#x bytes\n", cbStrings)); + break; + } + } + + /* + * Disconnect and destroy the session. + */ + VBGLREQHDR_INIT(&uBuf.Disconnect.Hdr, HGCM_DISCONNECT); + uBuf.Disconnect.u.In.idClient = idClient; + VGDrvCommonIoCtl(VBGL_IOCTL_HGCM_DISCONNECT, pDevExt, pSession, &uBuf.Disconnect.Hdr, sizeof(uBuf.Disconnect)); + + VGDrvCommonCloseSession(pDevExt, pSession); + + /* + * Process the properties if we got any. + * + * The string buffer contains packed strings in groups of four - name, value, + * timestamp (as a decimal string) and flags. It is terminated by four empty + * strings. Layout: + * Name\0Value\0Timestamp\0Flags\0 + */ + if (pszzStrings) + { + uint32_t off; + for (off = 0; off < cbStrings; off++) + { + /* + * Parse the four fields, checking that it's all plain ASCII w/o any control characters. + */ + const char *apszFields[4] = { NULL, NULL, NULL, NULL }; + bool fValidFields = true; + unsigned iField; + for (iField = 0; iField < RT_ELEMENTS(apszFields); iField++) + { + apszFields[0] = &pszzStrings[off]; + while (off < cbStrings) + { + char ch = pszzStrings[off++]; + if ((unsigned)ch < 0x20U || (unsigned)ch > 0x7fU) + { + if (!ch) + break; + if (fValidFields) + Log(("VGDrvCommonReadConfigurationFromHost: Invalid char %#x at %#x (field %u)\n", + ch, off - 1, iField)); + fValidFields = false; + } + } + } + if ( off <= cbStrings + && fValidFields + && *apszFields[0] != '\0') + { + /* + * Validate and convert the flags to integer, then process the option. + */ + uint32_t fFlags = 0; + rc = GuestPropValidateFlags(apszFields[3], &fFlags); + if (RT_SUCCESS(rc)) + { + if (fFlags & GUEST_PROP_F_RDONLYGUEST) + { + apszFields[0] += sizeof(g_szzPattern) - 2; + VGDrvCommonProcessOption(pDevExt, apszFields[0], apszFields[1]); + } + else + LogRel(("VBoxGuest: Ignoring '%s' as it does not have RDONLYGUEST set\n", apszFields[0])); + } + else + LogRel(("VBoxGuest: Invalid flags '%s' for '%s': %Rrc\n", apszFields[2], apszFields[0], rc)); + } + else if (off < cbStrings) + { + LogRel(("VBoxGuest: Malformed guest properties enum result!\n")); + Log(("VBoxGuest: off=%#x cbStrings=%#x\n%.*Rhxd\n", off, cbStrings, cbStrings, pszzStrings)); + break; + } + else if (!fValidFields) + LogRel(("VBoxGuest: Ignoring %.*Rhxs as it has invalid characters in one or more fields\n", + (int)strlen(apszFields[0]), apszFields[0])); + else + break; + } + + RTMemFree(pszzStrings); + } + else + LogRel(("VGDrvCommonReadConfigurationFromHost: failed to enumerate '%s': %Rrc\n", g_szzPattern, rc)); + + } + else + LogRel(("VGDrvCommonReadConfigurationFromHost: failed to connect: %Rrc\n", rc)); + } + else + LogRel(("VGDrvCommonReadConfigurationFromHost: failed to connect: %Rrc\n", rc)); +} + + +/** + * Destroys the VBoxGuest device extension. + * + * The native code should call this before the driver is unloaded, + * but don't call this on shutdown. + * + * @param pDevExt The device extension. + */ +void VGDrvCommonDeleteDevExt(PVBOXGUESTDEVEXT pDevExt) +{ + Log(("VGDrvCommonDeleteDevExt:\n")); + Log(("VBoxGuest: The additions driver is terminating.\n")); + VGDrvCommonDeleteDevExtResources(pDevExt); + VGDrvCommonDeleteDevExtFundament(pDevExt); + VGDrvCommonDestroyLoggers(); +} + + +/** + * Creates a VBoxGuest user session. + * + * The native code calls this when a ring-3 client opens the device. + * Use VGDrvCommonCreateKernelSession when a ring-0 client connects. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param fRequestor VMMDEV_REQUESTOR_XXX. + * @param ppSession Where to store the session on success. + */ +int VGDrvCommonCreateUserSession(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession)); + if (RT_UNLIKELY(!pSession)) + { + LogRel(("VGDrvCommonCreateUserSession: no memory!\n")); + return VERR_NO_MEMORY; + } + + pSession->Process = RTProcSelf(); + pSession->R0Process = RTR0ProcHandleSelf(); + pSession->pDevExt = pDevExt; + pSession->fRequestor = fRequestor; + pSession->fUserSession = RT_BOOL(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE); + RTSpinlockAcquire(pDevExt->SessionSpinlock); + RTListAppend(&pDevExt->SessionList, &pSession->ListNode); + pDevExt->cSessions++; + RTSpinlockRelease(pDevExt->SessionSpinlock); + + *ppSession = pSession; + LogFlow(("VGDrvCommonCreateUserSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n", + pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */ + return VINF_SUCCESS; +} + + +/** + * Creates a VBoxGuest kernel session. + * + * The native code calls this when a ring-0 client connects to the device. + * Use VGDrvCommonCreateUserSession when a ring-3 client opens the device. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param ppSession Where to store the session on success. + */ +int VGDrvCommonCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)RTMemAllocZ(sizeof(*pSession)); + if (RT_UNLIKELY(!pSession)) + { + LogRel(("VGDrvCommonCreateKernelSession: no memory!\n")); + return VERR_NO_MEMORY; + } + + pSession->Process = NIL_RTPROCESS; + pSession->R0Process = NIL_RTR0PROCESS; + pSession->pDevExt = pDevExt; + pSession->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV_OTHER + | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + RTSpinlockAcquire(pDevExt->SessionSpinlock); + RTListAppend(&pDevExt->SessionList, &pSession->ListNode); + pDevExt->cSessions++; + RTSpinlockRelease(pDevExt->SessionSpinlock); + + *ppSession = pSession; + LogFlow(("VGDrvCommonCreateKernelSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n", + pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */ + return VINF_SUCCESS; +} + + +/** + * Closes a VBoxGuest session. + * + * @param pDevExt The device extension. + * @param pSession The session to close (and free). + */ +void VGDrvCommonCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ +#ifdef VBOX_WITH_HGCM + unsigned i; +#endif + LogFlow(("VGDrvCommonCloseSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n", + pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */ + + RTSpinlockAcquire(pDevExt->SessionSpinlock); + RTListNodeRemove(&pSession->ListNode); + pDevExt->cSessions--; + RTSpinlockRelease(pDevExt->SessionSpinlock); + vgdrvAcquireSessionCapabilities(pDevExt, pSession, 0, UINT32_MAX, VBGL_IOC_AGC_FLAGS_DEFAULT, true /*fSessionTermination*/); + vgdrvSetSessionCapabilities(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/, + NULL /*pfSessionCaps*/, NULL /*pfGlobalCaps*/, true /*fSessionTermination*/); + vgdrvSetSessionEventFilter(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/, true /*fSessionTermination*/); + vgdrvSetSessionMouseStatus(pDevExt, pSession, 0 /*fOrMask*/, UINT32_MAX /*fNotMask*/, true /*fSessionTermination*/); + + vgdrvIoCtl_CancelAllWaitEvents(pDevExt, pSession); + +#ifdef VBOX_WITH_HGCM + for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) + if (pSession->aHGCMClientIds[i]) + { + uint32_t idClient = pSession->aHGCMClientIds[i]; + pSession->aHGCMClientIds[i] = 0; + Log(("VGDrvCommonCloseSession: disconnecting client id %#RX32\n", idClient)); + VbglR0HGCMInternalDisconnect(idClient, VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV, + vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT); + } +#endif + + pSession->pDevExt = NULL; + pSession->Process = NIL_RTPROCESS; + pSession->R0Process = NIL_RTR0PROCESS; + vgdrvCloseMemBalloon(pDevExt, pSession); + RTMemFree(pSession); +} + + +/** + * Allocates a wait-for-event entry. + * + * @returns The wait-for-event entry. + * @param pDevExt The device extension. + * @param pSession The session that's allocating this. Can be NULL. + */ +static PVBOXGUESTWAIT vgdrvWaitAlloc(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + /* + * Allocate it one way or the other. + */ + PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode); + if (pWait) + { + RTSpinlockAcquire(pDevExt->EventSpinlock); + + pWait = RTListGetFirst(&pDevExt->FreeList, VBOXGUESTWAIT, ListNode); + if (pWait) + RTListNodeRemove(&pWait->ListNode); + + RTSpinlockRelease(pDevExt->EventSpinlock); + } + if (!pWait) + { + int rc; + + pWait = (PVBOXGUESTWAIT)RTMemAlloc(sizeof(*pWait)); + if (!pWait) + { + LogRelMax(32, ("vgdrvWaitAlloc: out-of-memory!\n")); + return NULL; + } + + rc = RTSemEventMultiCreate(&pWait->Event); + if (RT_FAILURE(rc)) + { + LogRelMax(32, ("vgdrvWaitAlloc: RTSemEventMultiCreate failed with rc=%Rrc!\n", rc)); + RTMemFree(pWait); + return NULL; + } + + pWait->ListNode.pNext = NULL; + pWait->ListNode.pPrev = NULL; + } + + /* + * Zero members just as an precaution. + */ + pWait->fReqEvents = 0; + pWait->fResEvents = 0; +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + pWait->fPendingWakeUp = false; + pWait->fFreeMe = false; +#endif + pWait->pSession = pSession; +#ifdef VBOX_WITH_HGCM + pWait->pHGCMReq = NULL; +#endif + RTSemEventMultiReset(pWait->Event); + return pWait; +} + + +/** + * Frees the wait-for-event entry. + * + * The caller must own the wait spinlock ! + * The entry must be in a list! + * + * @param pDevExt The device extension. + * @param pWait The wait-for-event entry to free. + */ +static void vgdrvWaitFreeLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait) +{ + pWait->fReqEvents = 0; + pWait->fResEvents = 0; +#ifdef VBOX_WITH_HGCM + pWait->pHGCMReq = NULL; +#endif +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + Assert(!pWait->fFreeMe); + if (pWait->fPendingWakeUp) + pWait->fFreeMe = true; + else +#endif + { + RTListNodeRemove(&pWait->ListNode); + RTListAppend(&pDevExt->FreeList, &pWait->ListNode); + } +} + + +/** + * Frees the wait-for-event entry. + * + * @param pDevExt The device extension. + * @param pWait The wait-for-event entry to free. + */ +static void vgdrvWaitFreeUnlocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTWAIT pWait) +{ + RTSpinlockAcquire(pDevExt->EventSpinlock); + vgdrvWaitFreeLocked(pDevExt, pWait); + RTSpinlockRelease(pDevExt->EventSpinlock); +} + + +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP +/** + * Processes the wake-up list. + * + * All entries in the wake-up list gets signalled and moved to the woken-up + * list. + * At least on Windows this function can be invoked concurrently from + * different VCPUs. So, be thread-safe. + * + * @param pDevExt The device extension. + */ +void VGDrvCommonWaitDoWakeUps(PVBOXGUESTDEVEXT pDevExt) +{ + if (!RTListIsEmpty(&pDevExt->WakeUpList)) + { + RTSpinlockAcquire(pDevExt->EventSpinlock); + for (;;) + { + int rc; + PVBOXGUESTWAIT pWait = RTListGetFirst(&pDevExt->WakeUpList, VBOXGUESTWAIT, ListNode); + if (!pWait) + break; + /* Prevent other threads from accessing pWait when spinlock is released. */ + RTListNodeRemove(&pWait->ListNode); + + pWait->fPendingWakeUp = true; + RTSpinlockRelease(pDevExt->EventSpinlock); + + rc = RTSemEventMultiSignal(pWait->Event); + AssertRC(rc); + + RTSpinlockAcquire(pDevExt->EventSpinlock); + Assert(pWait->ListNode.pNext == NULL && pWait->ListNode.pPrev == NULL); + RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode); + pWait->fPendingWakeUp = false; + if (RT_LIKELY(!pWait->fFreeMe)) + { /* likely */ } + else + { + pWait->fFreeMe = false; + vgdrvWaitFreeLocked(pDevExt, pWait); + } + } + RTSpinlockRelease(pDevExt->EventSpinlock); + } +} +#endif /* VBOXGUEST_USE_DEFERRED_WAKE_UP */ + + +/** + * Implements the fast (no input or output) type of IOCtls. + * + * This is currently just a placeholder stub inherited from the support driver code. + * + * @returns VBox status code. + * @param iFunction The IOCtl function number. + * @param pDevExt The device extension. + * @param pSession The session. + */ +int VGDrvCommonIoCtlFast(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + LogFlow(("VGDrvCommonIoCtlFast: iFunction=%#x pDevExt=%p pSession=%p\n", iFunction, pDevExt, pSession)); + + NOREF(iFunction); + NOREF(pDevExt); + NOREF(pSession); + return VERR_NOT_SUPPORTED; +} + + +/** + * Gets the driver I/O control interface version, maybe adjusting it for + * backwards compatibility. + * + * The adjusting is currently not implemented as we only have one major I/O + * control interface version out there to support. This is something we will + * implement as needed. + * + * returns IPRT status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param pReq The request info. + */ +static int vgdrvIoCtl_DriverVersionInfo(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCDRIVERVERSIONINFO pReq) +{ + int rc; + LogFlow(("VBGL_IOCTL_DRIVER_VERSION_INFO: uReqVersion=%#x uMinVersion=%#x uReserved1=%#x uReserved2=%#x\n", + pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, pReq->u.In.uReserved1, pReq->u.In.uReserved2)); + RT_NOREF2(pDevExt, pSession); + + /* + * Input validation. + */ + if ( pReq->u.In.uMinVersion <= pReq->u.In.uReqVersion + && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(pReq->u.In.uReqVersion)) + { + /* + * Match the version. + * The current logic is very simple, match the major interface version. + */ + if ( pReq->u.In.uMinVersion <= VBGL_IOC_VERSION + && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(VBGL_IOC_VERSION)) + rc = VINF_SUCCESS; + else + { + LogRel(("VBGL_IOCTL_DRIVER_VERSION_INFO: Version mismatch. Requested: %#x Min: %#x Current: %#x\n", + pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, VBGL_IOC_VERSION)); + rc = VERR_VERSION_MISMATCH; + } + } + else + { + LogRel(("VBGL_IOCTL_DRIVER_VERSION_INFO: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n", + pReq->u.In.uMinVersion, pReq->u.In.uReqVersion)); + rc = VERR_INVALID_PARAMETER; + } + + pReq->u.Out.uSessionVersion = RT_SUCCESS(rc) ? VBGL_IOC_VERSION : UINT32_MAX; + pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION; + pReq->u.Out.uDriverRevision = VBOX_SVN_REV; + pReq->u.Out.uReserved1 = 0; + pReq->u.Out.uReserved2 = 0; + return rc; +} + + +/** + * Similar to vgdrvIoCtl_DriverVersionInfo, except its for IDC. + * + * returns IPRT status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param pReq The request info. + */ +static int vgdrvIoCtl_IdcConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCIDCCONNECT pReq) +{ + int rc; + LogFlow(("VBGL_IOCTL_IDC_CONNECT: u32MagicCookie=%#x uReqVersion=%#x uMinVersion=%#x uReserved=%#x\n", + pReq->u.In.u32MagicCookie, pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, pReq->u.In.uReserved)); + Assert(pSession != NULL); + RT_NOREF(pDevExt); + + /* + * Input validation. + */ + if (pReq->u.In.u32MagicCookie == VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE) + { + if ( pReq->u.In.uMinVersion <= pReq->u.In.uReqVersion + && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(pReq->u.In.uReqVersion)) + { + /* + * Match the version. + * The current logic is very simple, match the major interface version. + */ + if ( pReq->u.In.uMinVersion <= VBGL_IOC_VERSION + && RT_HI_U16(pReq->u.In.uMinVersion) == RT_HI_U16(VBGL_IOC_VERSION)) + { + pReq->u.Out.pvSession = pSession; + pReq->u.Out.uSessionVersion = VBGL_IOC_VERSION; + pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION; + pReq->u.Out.uDriverRevision = VBOX_SVN_REV; + pReq->u.Out.uReserved1 = 0; + pReq->u.Out.pvReserved2 = NULL; + return VINF_SUCCESS; + + } + LogRel(("VBGL_IOCTL_IDC_CONNECT: Version mismatch. Requested: %#x Min: %#x Current: %#x\n", + pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, VBGL_IOC_VERSION)); + rc = VERR_VERSION_MISMATCH; + } + else + { + LogRel(("VBGL_IOCTL_IDC_CONNECT: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n", + pReq->u.In.uMinVersion, pReq->u.In.uReqVersion)); + rc = VERR_INVALID_PARAMETER; + } + + pReq->u.Out.pvSession = NULL; + pReq->u.Out.uSessionVersion = UINT32_MAX; + pReq->u.Out.uDriverVersion = VBGL_IOC_VERSION; + pReq->u.Out.uDriverRevision = VBOX_SVN_REV; + pReq->u.Out.uReserved1 = 0; + pReq->u.Out.pvReserved2 = NULL; + } + else + { + LogRel(("VBGL_IOCTL_IDC_CONNECT: u32MagicCookie=%#x expected %#x!\n", + pReq->u.In.u32MagicCookie, VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE)); + rc = VERR_INVALID_PARAMETER; + } + return rc; +} + + +/** + * Counterpart to vgdrvIoCtl_IdcConnect, destroys the session. + * + * returns IPRT status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param pReq The request info. + */ +static int vgdrvIoCtl_IdcDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCIDCDISCONNECT pReq) +{ + LogFlow(("VBGL_IOCTL_IDC_DISCONNECT: pvSession=%p vs pSession=%p\n", pReq->u.In.pvSession, pSession)); + RT_NOREF(pDevExt); + Assert(pSession != NULL); + + if (pReq->u.In.pvSession == pSession) + { + VGDrvCommonCloseSession(pDevExt, pSession); + return VINF_SUCCESS; + } + LogRel(("VBGL_IOCTL_IDC_DISCONNECT: In.pvSession=%p is not equal to pSession=%p!\n", pReq->u.In.pvSession, pSession)); + return VERR_INVALID_PARAMETER; +} + + +/** + * Return the VMM device I/O info. + * + * returns IPRT status code. + * @param pDevExt The device extension. + * @param pInfo The request info. + * @note Ring-0 only, caller checked. + */ +static int vgdrvIoCtl_GetVMMDevIoInfo(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCGETVMMDEVIOINFO pInfo) +{ + LogFlow(("VBGL_IOCTL_GET_VMMDEV_IO_INFO\n")); + + pInfo->u.Out.IoPort = pDevExt->IOPortBase; + pInfo->u.Out.pvVmmDevMapping = pDevExt->pVMMDevMemory; + pInfo->u.Out.auPadding[0] = 0; +#if HC_ARCH_BITS != 32 + pInfo->u.Out.auPadding[1] = 0; + pInfo->u.Out.auPadding[2] = 0; +#endif + return VINF_SUCCESS; +} + + +/** + * Set the callback for the kernel mouse handler. + * + * returns IPRT status code. + * @param pDevExt The device extension. + * @param pNotify The new callback information. + */ +static int vgdrvIoCtl_SetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify) +{ + LogFlow(("VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK: pfnNotify=%p pvUser=%p\n", pNotify->u.In.pfnNotify, pNotify->u.In.pvUser)); + +#ifdef VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT + VGDrvNativeSetMouseNotifyCallback(pDevExt, pNotify); +#else + RTSpinlockAcquire(pDevExt->EventSpinlock); + pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify; + pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser; + RTSpinlockRelease(pDevExt->EventSpinlock); +#endif + return VINF_SUCCESS; +} + + +/** + * Worker vgdrvIoCtl_WaitEvent. + * + * The caller enters the spinlock, we leave it. + * + * @returns VINF_SUCCESS if we've left the spinlock and can return immediately. + */ +DECLINLINE(int) vbdgCheckWaitEventCondition(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + PVBGLIOCWAITFOREVENTS pInfo, int iEvent, const uint32_t fReqEvents) +{ + uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents; + if (fMatches & VBOXGUEST_ACQUIRE_STYLE_EVENTS) + fMatches &= vgdrvGetAllowedEventMaskForSession(pDevExt, pSession); + if (fMatches || pSession->fPendingCancelWaitEvents) + { + ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches); + RTSpinlockRelease(pDevExt->EventSpinlock); + + pInfo->u.Out.fEvents = fMatches; + if (fReqEvents & ~((uint32_t)1 << iEvent)) + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x\n", pInfo->u.Out.fEvents)); + else + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x/%d\n", pInfo->u.Out.fEvents, iEvent)); + pSession->fPendingCancelWaitEvents = false; + return VINF_SUCCESS; + } + + RTSpinlockRelease(pDevExt->EventSpinlock); + return VERR_TIMEOUT; +} + + +static int vgdrvIoCtl_WaitForEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + PVBGLIOCWAITFOREVENTS pInfo, bool fInterruptible) +{ + uint32_t const cMsTimeout = pInfo->u.In.cMsTimeOut; + const uint32_t fReqEvents = pInfo->u.In.fEvents; + uint32_t fResEvents; + int iEvent; + PVBOXGUESTWAIT pWait; + int rc; + + pInfo->u.Out.fEvents = 0; /* Note! This overwrites pInfo->u.In.* fields! */ + + /* + * Copy and verify the input mask. + */ + iEvent = ASMBitFirstSetU32(fReqEvents) - 1; + if (RT_UNLIKELY(iEvent < 0)) + { + LogRel(("VBOXGUEST_IOCTL_WAITEVENT: Invalid input mask %#x!!\n", fReqEvents)); + return VERR_INVALID_PARAMETER; + } + + /* + * Check the condition up front, before doing the wait-for-event allocations. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + rc = vbdgCheckWaitEventCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents); + if (rc == VINF_SUCCESS) + return rc; + + if (!cMsTimeout) + { + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_TIMEOUT\n")); + return VERR_TIMEOUT; + } + + pWait = vgdrvWaitAlloc(pDevExt, pSession); + if (!pWait) + return VERR_NO_MEMORY; + pWait->fReqEvents = fReqEvents; + + /* + * We've got the wait entry now, re-enter the spinlock and check for the condition. + * If the wait condition is met, return. + * Otherwise enter into the list and go to sleep waiting for the ISR to signal us. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + RTListAppend(&pDevExt->WaitList, &pWait->ListNode); + rc = vbdgCheckWaitEventCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents); + if (rc == VINF_SUCCESS) + { + vgdrvWaitFreeUnlocked(pDevExt, pWait); + return rc; + } + + if (fInterruptible) + rc = RTSemEventMultiWaitNoResume(pWait->Event, cMsTimeout == UINT32_MAX ? RT_INDEFINITE_WAIT : cMsTimeout); + else + rc = RTSemEventMultiWait(pWait->Event, cMsTimeout == UINT32_MAX ? RT_INDEFINITE_WAIT : cMsTimeout); + + /* + * There is one special case here and that's when the semaphore is + * destroyed upon device driver unload. This shouldn't happen of course, + * but in case it does, just get out of here ASAP. + */ + if (rc == VERR_SEM_DESTROYED) + return rc; + + /* + * Unlink the wait item and dispose of it. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + fResEvents = pWait->fResEvents; + vgdrvWaitFreeLocked(pDevExt, pWait); + RTSpinlockRelease(pDevExt->EventSpinlock); + + /* + * Now deal with the return code. + */ + if ( fResEvents + && fResEvents != UINT32_MAX) + { + pInfo->u.Out.fEvents = fResEvents; + if (fReqEvents & ~((uint32_t)1 << iEvent)) + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x\n", pInfo->u.Out.fEvents)); + else + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %#x/%d\n", pInfo->u.Out.fEvents, iEvent)); + rc = VINF_SUCCESS; + } + else if ( fResEvents == UINT32_MAX + || rc == VERR_INTERRUPTED) + { + rc = VERR_INTERRUPTED; + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_INTERRUPTED\n")); + } + else if (rc == VERR_TIMEOUT) + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns VERR_TIMEOUT (2)\n")); + else + { + if (RT_SUCCESS(rc)) + { + LogRelMax(32, ("VBOXGUEST_IOCTL_WAITEVENT: returns %Rrc but no events!\n", rc)); + rc = VERR_INTERNAL_ERROR; + } + LogFlow(("VBOXGUEST_IOCTL_WAITEVENT: returns %Rrc\n", rc)); + } + + return rc; +} + + +/** @todo the semantics of this IoCtl have been tightened, so that no calls to + * VBOXGUEST_IOCTL_WAITEVENT are allowed in a session after it has been + * called. Change the code to make calls to VBOXGUEST_IOCTL_WAITEVENT made + * after that to return VERR_INTERRUPTED or something appropriate. */ +static int vgdrvIoCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + PVBOXGUESTWAIT pWait; + PVBOXGUESTWAIT pSafe; + int rc = 0; + /* Was as least one WAITEVENT in process for this session? If not we + * set a flag that the next call should be interrupted immediately. This + * is needed so that a user thread can reliably interrupt another one in a + * WAITEVENT loop. */ + bool fCancelledOne = false; + + LogFlow(("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS\n")); + + /* + * Walk the event list and wake up anyone with a matching session. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode) + { + if (pWait->pSession == pSession) + { + fCancelledOne = true; + pWait->fResEvents = UINT32_MAX; + RTListNodeRemove(&pWait->ListNode); +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode); +#else + rc |= RTSemEventMultiSignal(pWait->Event); + RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode); +#endif + } + } + if (!fCancelledOne) + pSession->fPendingCancelWaitEvents = true; + RTSpinlockRelease(pDevExt->EventSpinlock); + Assert(rc == 0); + NOREF(rc); + +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + VGDrvCommonWaitDoWakeUps(pDevExt); +#endif + + return VINF_SUCCESS; +} + + +/** + * Checks if the VMM request is allowed in the context of the given session. + * + * @returns VINF_SUCCESS or VERR_PERMISSION_DENIED. + * @param pDevExt The device extension. + * @param pSession The calling session. + * @param enmType The request type. + * @param pReqHdr The request. + */ +static int vgdrvCheckIfVmmReqIsAllowed(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VMMDevRequestType enmType, + VMMDevRequestHeader const *pReqHdr) +{ + /* + * Categorize the request being made. + */ + /** @todo This need quite some more work! */ + enum + { + kLevel_Invalid, kLevel_NoOne, kLevel_OnlyVBoxGuest, kLevel_OnlyKernel, kLevel_TrustedUsers, kLevel_AllUsers + } enmRequired; + RT_NOREF1(pDevExt); + + switch (enmType) + { + /* + * Deny access to anything we don't know or provide specialized I/O controls for. + */ +#ifdef VBOX_WITH_HGCM + case VMMDevReq_HGCMConnect: + case VMMDevReq_HGCMDisconnect: +# ifdef VBOX_WITH_64_BITS_GUESTS + case VMMDevReq_HGCMCall64: +# endif + case VMMDevReq_HGCMCall32: + case VMMDevReq_HGCMCancel: + case VMMDevReq_HGCMCancel2: +#endif /* VBOX_WITH_HGCM */ + case VMMDevReq_SetGuestCapabilities: + default: + enmRequired = kLevel_NoOne; + break; + + /* + * There are a few things only this driver can do (and it doesn't use + * the VMMRequst I/O control route anyway, but whatever). + */ + case VMMDevReq_ReportGuestInfo: + case VMMDevReq_ReportGuestInfo2: + case VMMDevReq_GetHypervisorInfo: + case VMMDevReq_SetHypervisorInfo: + case VMMDevReq_RegisterPatchMemory: + case VMMDevReq_DeregisterPatchMemory: + case VMMDevReq_GetMemBalloonChangeRequest: + case VMMDevReq_ChangeMemBalloon: + enmRequired = kLevel_OnlyVBoxGuest; + break; + + /* + * Trusted users apps only. + */ + case VMMDevReq_QueryCredentials: + case VMMDevReq_ReportCredentialsJudgement: + case VMMDevReq_RegisterSharedModule: + case VMMDevReq_UnregisterSharedModule: + case VMMDevReq_WriteCoreDump: + case VMMDevReq_GetCpuHotPlugRequest: + case VMMDevReq_SetCpuHotPlugStatus: + case VMMDevReq_CheckSharedModules: + case VMMDevReq_GetPageSharingStatus: + case VMMDevReq_DebugIsPageShared: + case VMMDevReq_ReportGuestStats: + case VMMDevReq_ReportGuestUserState: + case VMMDevReq_GetStatisticsChangeRequest: + enmRequired = kLevel_TrustedUsers; + break; + + /* + * Anyone. + */ + case VMMDevReq_GetMouseStatus: + case VMMDevReq_SetMouseStatus: + case VMMDevReq_SetPointerShape: + case VMMDevReq_GetHostVersion: + case VMMDevReq_Idle: + case VMMDevReq_GetHostTime: + case VMMDevReq_SetPowerStatus: + case VMMDevReq_AcknowledgeEvents: + case VMMDevReq_CtlGuestFilterMask: + case VMMDevReq_ReportGuestStatus: + case VMMDevReq_GetDisplayChangeRequest: + case VMMDevReq_VideoModeSupported: + case VMMDevReq_GetHeightReduction: + case VMMDevReq_GetDisplayChangeRequest2: + case VMMDevReq_VideoModeSupported2: + case VMMDevReq_VideoAccelEnable: + case VMMDevReq_VideoAccelFlush: + case VMMDevReq_VideoSetVisibleRegion: + case VMMDevReq_VideoUpdateMonitorPositions: + case VMMDevReq_GetDisplayChangeRequestEx: + case VMMDevReq_GetDisplayChangeRequestMulti: + case VMMDevReq_GetSeamlessChangeRequest: + case VMMDevReq_GetVRDPChangeRequest: + case VMMDevReq_LogString: + case VMMDevReq_GetSessionId: + enmRequired = kLevel_AllUsers; + break; + + /* + * Depends on the request parameters... + */ + /** @todo this have to be changed into an I/O control and the facilities + * tracked in the session so they can automatically be failed when the + * session terminates without reporting the new status. + * + * The information presented by IGuest is not reliable without this! */ + case VMMDevReq_ReportGuestCapabilities: + switch (((VMMDevReportGuestStatus const *)pReqHdr)->guestStatus.facility) + { + case VBoxGuestFacilityType_All: + case VBoxGuestFacilityType_VBoxGuestDriver: + enmRequired = kLevel_OnlyVBoxGuest; + break; + case VBoxGuestFacilityType_VBoxService: + enmRequired = kLevel_TrustedUsers; + break; + case VBoxGuestFacilityType_VBoxTrayClient: + case VBoxGuestFacilityType_Seamless: + case VBoxGuestFacilityType_Graphics: + default: + enmRequired = kLevel_AllUsers; + break; + } + break; + } + + /* + * Check against the session. + */ + switch (enmRequired) + { + default: + case kLevel_NoOne: + break; + case kLevel_OnlyVBoxGuest: + case kLevel_OnlyKernel: + if (pSession->R0Process == NIL_RTR0PROCESS) + return VINF_SUCCESS; + break; + case kLevel_TrustedUsers: + if (pSession->fUserSession) + break; + RT_FALL_THRU(); + case kLevel_AllUsers: + return VINF_SUCCESS; + } + + return VERR_PERMISSION_DENIED; +} + +static int vgdrvIoCtl_VMMDevRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + VMMDevRequestHeader *pReqHdr, size_t cbData) +{ + int rc; + VMMDevRequestHeader *pReqCopy; + + /* + * Validate the header and request size. + */ + const VMMDevRequestType enmType = pReqHdr->requestType; + const uint32_t cbReq = pReqHdr->size; + const uint32_t cbMinSize = (uint32_t)vmmdevGetRequestSize(enmType); + + LogFlow(("VBOXGUEST_IOCTL_VMMREQUEST: type %d\n", pReqHdr->requestType)); + + if (cbReq < cbMinSize) + { + LogRel(("VBOXGUEST_IOCTL_VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n", + cbReq, cbMinSize, enmType)); + return VERR_INVALID_PARAMETER; + } + if (cbReq > cbData) + { + LogRel(("VBOXGUEST_IOCTL_VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n", + cbData, cbReq, enmType)); + return VERR_INVALID_PARAMETER; + } + rc = VbglGR0Verify(pReqHdr, cbData); + if (RT_FAILURE(rc)) + { + Log(("VBOXGUEST_IOCTL_VMMREQUEST: invalid header: size %#x, expected >= %#x (hdr); type=%#x; rc=%Rrc!!\n", + cbData, cbReq, enmType, rc)); + return rc; + } + + rc = vgdrvCheckIfVmmReqIsAllowed(pDevExt, pSession, enmType, pReqHdr); + if (RT_FAILURE(rc)) + { + Log(("VBOXGUEST_IOCTL_VMMREQUEST: Operation not allowed! type=%#x rc=%Rrc\n", enmType, rc)); + return rc; + } + + /* + * Make a copy of the request in the physical memory heap so + * the VBoxGuestLibrary can more easily deal with the request. + * (This is really a waste of time since the OS or the OS specific + * code has already buffered or locked the input/output buffer, but + * it does makes things a bit simpler wrt to phys address.) + */ + rc = VbglR0GRAlloc(&pReqCopy, cbReq, enmType); + if (RT_FAILURE(rc)) + { + Log(("VBOXGUEST_IOCTL_VMMREQUEST: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n", + cbReq, cbReq, rc)); + return rc; + } + memcpy(pReqCopy, pReqHdr, cbReq); + Assert(pReqCopy->reserved1 == cbReq); + pReqCopy->reserved1 = 0; /* VGDrvCommonIoCtl or caller sets cbOut, so clear it. */ + pReqCopy->fRequestor = pSession->fRequestor; + + if (enmType == VMMDevReq_GetMouseStatus) /* clear poll condition. */ + pSession->u32MousePosChangedSeq = ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq); + + rc = VbglR0GRPerform(pReqCopy); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(pReqCopy->rc)) + { + Assert(rc != VINF_HGCM_ASYNC_EXECUTE); + Assert(pReqCopy->rc != VINF_HGCM_ASYNC_EXECUTE); + + memcpy(pReqHdr, pReqCopy, cbReq); + pReqHdr->reserved1 = cbReq; /* preserve cbOut */ + } + else if (RT_FAILURE(rc)) + Log(("VBOXGUEST_IOCTL_VMMREQUEST: VbglR0GRPerform - rc=%Rrc!\n", rc)); + else + { + Log(("VBOXGUEST_IOCTL_VMMREQUEST: request execution failed; VMMDev rc=%Rrc!\n", pReqCopy->rc)); + rc = pReqCopy->rc; + } + + VbglR0GRFree(pReqCopy); + return rc; +} + + +#ifdef VBOX_WITH_HGCM + +AssertCompile(RT_INDEFINITE_WAIT == (uint32_t)RT_INDEFINITE_WAIT); /* assumed by code below */ + +/** Worker for vgdrvHgcmAsyncWaitCallback*. */ +static int vgdrvHgcmAsyncWaitCallbackWorker(VMMDevHGCMRequestHeader volatile *pHdr, PVBOXGUESTDEVEXT pDevExt, + bool fInterruptible, uint32_t cMillies) +{ + int rc; + + /* + * Check to see if the condition was met by the time we got here. + * + * We create a simple poll loop here for dealing with out-of-memory + * conditions since the caller isn't necessarily able to deal with + * us returning too early. + */ + PVBOXGUESTWAIT pWait; + for (;;) + { + RTSpinlockAcquire(pDevExt->EventSpinlock); + if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0) + { + RTSpinlockRelease(pDevExt->EventSpinlock); + return VINF_SUCCESS; + } + RTSpinlockRelease(pDevExt->EventSpinlock); + + pWait = vgdrvWaitAlloc(pDevExt, NULL); + if (pWait) + break; + if (fInterruptible) + return VERR_INTERRUPTED; + RTThreadSleep(1); + } + pWait->fReqEvents = VMMDEV_EVENT_HGCM; + pWait->pHGCMReq = pHdr; + + /* + * Re-enter the spinlock and re-check for the condition. + * If the condition is met, return. + * Otherwise link us into the HGCM wait list and go to sleep. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + RTListAppend(&pDevExt->HGCMWaitList, &pWait->ListNode); + if ((pHdr->fu32Flags & VBOX_HGCM_REQ_DONE) != 0) + { + vgdrvWaitFreeLocked(pDevExt, pWait); + RTSpinlockRelease(pDevExt->EventSpinlock); + return VINF_SUCCESS; + } + RTSpinlockRelease(pDevExt->EventSpinlock); + + if (fInterruptible) + rc = RTSemEventMultiWaitNoResume(pWait->Event, cMillies); + else + rc = RTSemEventMultiWait(pWait->Event, cMillies); + if (rc == VERR_SEM_DESTROYED) + return rc; + + /* + * Unlink, free and return. + */ + if ( RT_FAILURE(rc) + && rc != VERR_TIMEOUT + && ( !fInterruptible + || rc != VERR_INTERRUPTED)) + LogRel(("vgdrvHgcmAsyncWaitCallback: wait failed! %Rrc\n", rc)); + + vgdrvWaitFreeUnlocked(pDevExt, pWait); + return rc; +} + + +/** + * This is a callback for dealing with async waits. + * + * It operates in a manner similar to vgdrvIoCtl_WaitEvent. + */ +static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallback(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User) +{ + PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser; + LogFlow(("vgdrvHgcmAsyncWaitCallback: requestType=%d\n", pHdr->header.requestType)); + return vgdrvHgcmAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr, pDevExt, + false /* fInterruptible */, u32User /* cMillies */); +} + + +/** + * This is a callback for dealing with async waits with a timeout. + * + * It operates in a manner similar to vgdrvIoCtl_WaitEvent. + */ +static DECLCALLBACK(int) vgdrvHgcmAsyncWaitCallbackInterruptible(VMMDevHGCMRequestHeader *pHdr, void *pvUser, uint32_t u32User) +{ + PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser; + LogFlow(("vgdrvHgcmAsyncWaitCallbackInterruptible: requestType=%d\n", pHdr->header.requestType)); + return vgdrvHgcmAsyncWaitCallbackWorker((VMMDevHGCMRequestHeader volatile *)pHdr, pDevExt, + true /* fInterruptible */, u32User /* cMillies */); +} + + +static int vgdrvIoCtl_HGCMConnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCONNECT pInfo) +{ + int rc; + HGCMCLIENTID idClient = 0; + + /* + * The VbglHGCMConnect call will invoke the callback if the HGCM + * call is performed in an ASYNC fashion. The function is not able + * to deal with cancelled requests. + */ + Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: %.128s\n", + pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost || pInfo->u.In.Loc.type == VMMDevHGCMLoc_LocalHost_Existing + ? pInfo->u.In.Loc.u.host.achName : "<not local host>")); + + rc = VbglR0HGCMInternalConnect(&pInfo->u.In.Loc, pSession->fRequestor, &idClient, + vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT); + Log(("VBOXGUEST_IOCTL_HGCM_CONNECT: idClient=%RX32 (rc=%Rrc)\n", idClient, rc)); + if (RT_SUCCESS(rc)) + { + /* + * Append the client id to the client id table. + * If the table has somehow become filled up, we'll disconnect the session. + */ + unsigned i; + RTSpinlockAcquire(pDevExt->SessionSpinlock); + for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) + if (!pSession->aHGCMClientIds[i]) + { + pSession->aHGCMClientIds[i] = idClient; + break; + } + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (i >= RT_ELEMENTS(pSession->aHGCMClientIds)) + { + LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CONNECT: too many HGCMConnect calls for one session!\n")); + VbglR0HGCMInternalDisconnect(idClient, pSession->fRequestor, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT); + + pInfo->u.Out.idClient = 0; + return VERR_TOO_MANY_OPEN_FILES; + } + } + pInfo->u.Out.idClient = idClient; + return rc; +} + + +static int vgdrvIoCtl_HGCMDisconnect(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMDISCONNECT pInfo) +{ + /* + * Validate the client id and invalidate its entry while we're in the call. + */ + int rc; + const uint32_t idClient = pInfo->u.In.idClient; + unsigned i; + RTSpinlockAcquire(pDevExt->SessionSpinlock); + for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) + if (pSession->aHGCMClientIds[i] == idClient) + { + pSession->aHGCMClientIds[i] = UINT32_MAX; + break; + } + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (i >= RT_ELEMENTS(pSession->aHGCMClientIds)) + { + LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_DISCONNECT: idClient=%RX32\n", idClient)); + return VERR_INVALID_HANDLE; + } + + /* + * The VbglHGCMConnect call will invoke the callback if the HGCM + * call is performed in an ASYNC fashion. The function is not able + * to deal with cancelled requests. + */ + Log(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: idClient=%RX32\n", idClient)); + rc = VbglR0HGCMInternalDisconnect(idClient, pSession->fRequestor, vgdrvHgcmAsyncWaitCallback, pDevExt, RT_INDEFINITE_WAIT); + LogFlow(("VBOXGUEST_IOCTL_HGCM_DISCONNECT: rc=%Rrc\n", rc)); + + /* Update the client id array according to the result. */ + RTSpinlockAcquire(pDevExt->SessionSpinlock); + if (pSession->aHGCMClientIds[i] == UINT32_MAX) + pSession->aHGCMClientIds[i] = RT_SUCCESS(rc) ? 0 : idClient; + RTSpinlockRelease(pDevExt->SessionSpinlock); + + return rc; +} + + +static int vgdrvIoCtl_HGCMCallInner(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCALL pInfo, + uint32_t cMillies, bool fInterruptible, bool f32bit, bool fUserData, + size_t cbExtra, size_t cbData) +{ + const uint32_t u32ClientId = pInfo->u32ClientID; + uint32_t fFlags; + size_t cbActual; + unsigned i; + int rc; + + /* + * Some more validations. + */ + if (RT_LIKELY(pInfo->cParms <= VMMDEV_MAX_HGCM_PARMS)) /* (Just make sure it doesn't overflow the next check.) */ + { /* likely */} + else + { + LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cParm=%RX32 is not sane\n", pInfo->cParms)); + return VERR_INVALID_PARAMETER; + } + + cbActual = cbExtra + sizeof(*pInfo); +#ifdef RT_ARCH_AMD64 + if (f32bit) + cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter32); + else +#endif + cbActual += pInfo->cParms * sizeof(HGCMFunctionParameter); + if (RT_LIKELY(cbData >= cbActual)) + { /* likely */} + else + { + LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cbData=%#zx (%zu) required size is %#zx (%zu)\n", + cbData, cbData, cbActual, cbActual)); + return VERR_INVALID_PARAMETER; + } + pInfo->Hdr.cbOut = (uint32_t)cbActual; + + /* + * Validate the client id. + */ + RTSpinlockAcquire(pDevExt->SessionSpinlock); + for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) + if (pSession->aHGCMClientIds[i] == u32ClientId) + break; + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (RT_LIKELY(i < RT_ELEMENTS(pSession->aHGCMClientIds))) + { /* likely */} + else + { + LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CALL: Invalid handle. u32Client=%RX32\n", u32ClientId)); + return VERR_INVALID_HANDLE; + } + + /* + * The VbglHGCMCall call will invoke the callback if the HGCM + * call is performed in an ASYNC fashion. This function can + * deal with cancelled requests, so we let user more requests + * be interruptible (should add a flag for this later I guess). + */ + LogFlow(("VBOXGUEST_IOCTL_HGCM_CALL: u32Client=%RX32\n", pInfo->u32ClientID)); + fFlags = !fUserData && pSession->R0Process == NIL_RTR0PROCESS ? VBGLR0_HGCMCALL_F_KERNEL : VBGLR0_HGCMCALL_F_USER; + uint32_t cbInfo = (uint32_t)(cbData - cbExtra); +#ifdef RT_ARCH_AMD64 + if (f32bit) + { + if (fInterruptible) + rc = VbglR0HGCMInternalCall32(pInfo, cbInfo, fFlags, pSession->fRequestor, + vgdrvHgcmAsyncWaitCallbackInterruptible, pDevExt, cMillies); + else + rc = VbglR0HGCMInternalCall32(pInfo, cbInfo, fFlags, pSession->fRequestor, + vgdrvHgcmAsyncWaitCallback, pDevExt, cMillies); + } + else +#endif + { + if (fInterruptible) + rc = VbglR0HGCMInternalCall(pInfo, cbInfo, fFlags, pSession->fRequestor, + vgdrvHgcmAsyncWaitCallbackInterruptible, pDevExt, cMillies); + else + rc = VbglR0HGCMInternalCall(pInfo, cbInfo, fFlags, pSession->fRequestor, + vgdrvHgcmAsyncWaitCallback, pDevExt, cMillies); + } + if (RT_SUCCESS(rc)) + { + rc = pInfo->Hdr.rc; + LogFlow(("VBOXGUEST_IOCTL_HGCM_CALL: result=%Rrc\n", rc)); + } + else + { + if ( rc != VERR_INTERRUPTED + && rc != VERR_TIMEOUT) + LogRelMax(32, ("VBOXGUEST_IOCTL_HGCM_CALL: %s Failed. rc=%Rrc (Hdr.rc=%Rrc).\n", f32bit ? "32" : "64", rc, pInfo->Hdr.rc)); + else + Log(("VBOXGUEST_IOCTL_HGCM_CALL: %s Failed. rc=%Rrc (Hdr.rc=%Rrc).\n", f32bit ? "32" : "64", rc, pInfo->Hdr.rc)); + } + return rc; +} + + +static int vgdrvIoCtl_HGCMCallWrapper(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCHGCMCALL pInfo, + bool f32bit, bool fUserData, size_t cbData) +{ + return vgdrvIoCtl_HGCMCallInner(pDevExt, pSession, pInfo, pInfo->cMsTimeout, + pInfo->fInterruptible || pSession->R0Process != NIL_RTR0PROCESS, + f32bit, fUserData, 0 /*cbExtra*/, cbData); +} + + +/** + * Handles a fast HGCM call from another driver. + * + * The driver has provided a fully assembled HGCM call request and all we need + * to do is send it to the host and do the wait processing. + * + * @returns VBox status code of the request submission part. + * @param pDevExt The device extension. + * @param pCallReq The call request. + */ +static int vgdrvIoCtl_HGCMFastCall(PVBOXGUESTDEVEXT pDevExt, VBGLIOCIDCHGCMFASTCALL volatile *pCallReq) +{ + VMMDevHGCMCall volatile *pHgcmCall = (VMMDevHGCMCall volatile *)(pCallReq + 1); + int rc; + + /* + * Check out the physical address. + */ + Assert((pCallReq->GCPhysReq & PAGE_OFFSET_MASK) == ((uintptr_t)pHgcmCall & PAGE_OFFSET_MASK)); + + AssertReturn(!pCallReq->fInterruptible, VERR_NOT_IMPLEMENTED); + + /* + * Submit the request. + */ + Log(("vgdrvIoCtl_HGCMFastCall -> host\n")); + ASMOutU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST, (uint32_t)pCallReq->GCPhysReq); + + /* Make the compiler aware that the host has changed memory. */ + ASMCompilerBarrier(); + + rc = pHgcmCall->header.header.rc; + Log(("vgdrvIoCtl_HGCMFastCall -> %Rrc (header rc=%Rrc)\n", rc, pHgcmCall->header.result)); + + /* + * The host is likely to engage in asynchronous execution of HGCM, unless it fails. + */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + rc = vgdrvHgcmAsyncWaitCallbackWorker(&pHgcmCall->header, pDevExt, false /* fInterruptible */, RT_INDEFINITE_WAIT); + if (pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + { + Assert(!(pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED)); + rc = VINF_SUCCESS; + } + else + { + /* + * Timeout and interrupt scenarios are messy and requires + * cancelation, so implement later. + */ + AssertReleaseMsgFailed(("rc=%Rrc\n", rc)); + } + } + else + Assert((pHgcmCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) || RT_FAILURE_NP(rc)); + + Log(("vgdrvIoCtl_HGCMFastCall: rc=%Rrc result=%Rrc fu32Flags=%#x\n", rc, pHgcmCall->header.result, pHgcmCall->header.fu32Flags)); + return rc; + +} + +#endif /* VBOX_WITH_HGCM */ + +/** + * Handle VBGL_IOCTL_CHECK_BALLOON from R3. + * + * Ask the host for the size of the balloon and try to set it accordingly. If + * this approach fails because it's not supported, return with fHandleInR3 set + * and let the user land supply memory we can lock via the other ioctl. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pInfo The output buffer. + */ +static int vgdrvIoCtl_CheckMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHECKBALLOON pInfo) +{ + VMMDevGetMemBalloonChangeRequest *pReq; + int rc; + + LogFlow(("VBGL_IOCTL_CHECK_BALLOON:\n")); + rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx); + AssertRCReturn(rc, rc); + + /* + * The first user trying to query/change the balloon becomes the + * owner and owns it until the session is closed (vgdrvCloseMemBalloon). + */ + if ( pDevExt->MemBalloon.pOwner != pSession + && pDevExt->MemBalloon.pOwner == NULL) + pDevExt->MemBalloon.pOwner = pSession; + + if (pDevExt->MemBalloon.pOwner == pSession) + { + /* + * This is a response to that event. Setting this bit means that + * we request the value from the host and change the guest memory + * balloon according to this value. + */ + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(VMMDevGetMemBalloonChangeRequest), VMMDevReq_GetMemBalloonChangeRequest); + if (RT_SUCCESS(rc)) + { + pReq->header.fRequestor = pSession->fRequestor; + pReq->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST; + rc = VbglR0GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + { + Assert(pDevExt->MemBalloon.cMaxChunks == pReq->cPhysMemChunks || pDevExt->MemBalloon.cMaxChunks == 0); + pDevExt->MemBalloon.cMaxChunks = pReq->cPhysMemChunks; + + pInfo->u.Out.cBalloonChunks = pReq->cBalloonChunks; + pInfo->u.Out.fHandleInR3 = false; + pInfo->u.Out.afPadding[0] = false; + pInfo->u.Out.afPadding[1] = false; + pInfo->u.Out.afPadding[2] = false; + + rc = vgdrvSetBalloonSizeKernel(pDevExt, pReq->cBalloonChunks, &pInfo->u.Out.fHandleInR3); + /* Ignore various out of memory failures. */ + if ( rc == VERR_NO_MEMORY + || rc == VERR_NO_PHYS_MEMORY + || rc == VERR_NO_CONT_MEMORY) + rc = VINF_SUCCESS; + } + else + LogRel(("VBGL_IOCTL_CHECK_BALLOON: VbglR0GRPerform failed. rc=%Rrc\n", rc)); + VbglR0GRFree(&pReq->header); + } + } + else + rc = VERR_PERMISSION_DENIED; + + RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx); + LogFlow(("VBGL_IOCTL_CHECK_BALLOON returns %Rrc\n", rc)); + return rc; +} + + +/** + * Handle a request for changing the memory balloon. + * + * @returns VBox status code. + * + * @param pDevExt The device extention. + * @param pSession The session. + * @param pInfo The change request structure (input). + */ +static int vgdrvIoCtl_ChangeMemoryBalloon(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHANGEBALLOON pInfo) +{ + int rc; + LogFlow(("VBGL_IOCTL_CHANGE_BALLOON: fInflate=%RTbool u64ChunkAddr=%p\n", pInfo->u.In.fInflate, pInfo->u.In.pvChunk)); + if ( pInfo->u.In.abPadding[0] + || pInfo->u.In.abPadding[1] + || pInfo->u.In.abPadding[2] + || pInfo->u.In.abPadding[3] + || pInfo->u.In.abPadding[4] + || pInfo->u.In.abPadding[5] + || pInfo->u.In.abPadding[6] +#if ARCH_BITS == 32 + || pInfo->u.In.abPadding[7] + || pInfo->u.In.abPadding[8] + || pInfo->u.In.abPadding[9] +#endif + ) + { + Log(("VBGL_IOCTL_CHANGE_BALLOON: Padding isn't all zero: %.*Rhxs\n", sizeof(pInfo->u.In.abPadding), pInfo->u.In.abPadding)); + return VERR_INVALID_PARAMETER; + } + + rc = RTSemFastMutexRequest(pDevExt->MemBalloon.hMtx); + AssertRCReturn(rc, rc); + + if (!pDevExt->MemBalloon.fUseKernelAPI) + { + /* + * The first user trying to query/change the balloon becomes the + * owner and owns it until the session is closed (vgdrvCloseMemBalloon). + */ + if ( pDevExt->MemBalloon.pOwner != pSession + && pDevExt->MemBalloon.pOwner == NULL) + pDevExt->MemBalloon.pOwner = pSession; + + if (pDevExt->MemBalloon.pOwner == pSession) + rc = vgdrvSetBalloonSizeFromUser(pDevExt, pSession, pInfo->u.In.pvChunk, pInfo->u.In.fInflate != false); + else + rc = VERR_PERMISSION_DENIED; + } + else + rc = VERR_PERMISSION_DENIED; + + RTSemFastMutexRelease(pDevExt->MemBalloon.hMtx); + return rc; +} + + +/** + * Handle a request for writing a core dump of the guest on the host. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pInfo The output buffer. + */ +static int vgdrvIoCtl_WriteCoreDump(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCWRITECOREDUMP pInfo) +{ + VMMDevReqWriteCoreDump *pReq = NULL; + int rc; + LogFlow(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP\n")); + RT_NOREF1(pDevExt); + + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_WriteCoreDump); + if (RT_SUCCESS(rc)) + { + pReq->header.fRequestor = pSession->fRequestor; + pReq->fFlags = pInfo->u.In.fFlags; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + Log(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP: VbglR0GRPerform failed, rc=%Rrc!\n", rc)); + + VbglR0GRFree(&pReq->header); + } + else + Log(("VBOXGUEST_IOCTL_WRITE_CORE_DUMP: failed to allocate %u (%#x) bytes to cache the request. rc=%Rrc!!\n", + sizeof(*pReq), sizeof(*pReq), rc)); + return rc; +} + + +/** + * Guest backdoor logging. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pch The log message (need not be NULL terminated). + * @param cbData Size of the buffer. + * @param fUserSession Copy of VBOXGUESTSESSION::fUserSession for the + * call. True normal user, false root user. + */ +static int vgdrvIoCtl_Log(PVBOXGUESTDEVEXT pDevExt, const char *pch, size_t cbData, bool fUserSession) +{ + if (pDevExt->fLoggingEnabled) + RTLogBackdoorPrintf("%.*s", cbData, pch); + else if (!fUserSession) + LogRel(("%.*s", cbData, pch)); + else + Log(("%.*s", cbData, pch)); + return VINF_SUCCESS; +} + + +/** @name Guest Capabilities, Mouse Status and Event Filter + * @{ + */ + +/** + * Clears a bit usage tracker (init time). + * + * @param pTracker The tracker to clear. + */ +static void vgdrvBitUsageTrackerClear(PVBOXGUESTBITUSAGETRACER pTracker) +{ + uint32_t iBit; + AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t)); + + for (iBit = 0; iBit < 32; iBit++) + pTracker->acPerBitUsage[iBit] = 0; + pTracker->fMask = 0; +} + + +#ifdef VBOX_STRICT +/** + * Checks that pTracker->fMask is correct and that the usage values are within + * the valid range. + * + * @param pTracker The tracker. + * @param cMax Max valid usage value. + * @param pszWhat Identifies the tracker in assertions. + */ +static void vgdrvBitUsageTrackerCheckMask(PCVBOXGUESTBITUSAGETRACER pTracker, uint32_t cMax, const char *pszWhat) +{ + uint32_t fMask = 0; + uint32_t iBit; + AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t)); + + for (iBit = 0; iBit < 32; iBit++) + if (pTracker->acPerBitUsage[iBit]) + { + fMask |= RT_BIT_32(iBit); + AssertMsg(pTracker->acPerBitUsage[iBit] <= cMax, + ("%s: acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax)); + } + + AssertMsg(fMask == pTracker->fMask, ("%s: %#x vs %#x\n", pszWhat, fMask, pTracker->fMask)); +} +#endif + + +/** + * Applies a change to the bit usage tracker. + * + * + * @returns true if the mask changed, false if not. + * @param pTracker The bit usage tracker. + * @param fChanged The bits to change. + * @param fPrevious The previous value of the bits. + * @param cMax The max valid usage value for assertions. + * @param pszWhat Identifies the tracker in assertions. + */ +static bool vgdrvBitUsageTrackerChange(PVBOXGUESTBITUSAGETRACER pTracker, uint32_t fChanged, uint32_t fPrevious, + uint32_t cMax, const char *pszWhat) +{ + bool fGlobalChange = false; + AssertCompile(sizeof(pTracker->acPerBitUsage) == 32 * sizeof(uint32_t)); + + while (fChanged) + { + uint32_t const iBit = ASMBitFirstSetU32(fChanged) - 1; + uint32_t const fBitMask = RT_BIT_32(iBit); + Assert(iBit < 32); Assert(fBitMask & fChanged); + + if (fBitMask & fPrevious) + { + pTracker->acPerBitUsage[iBit] -= 1; + AssertMsg(pTracker->acPerBitUsage[iBit] <= cMax, + ("%s: acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax)); + if (pTracker->acPerBitUsage[iBit] == 0) + { + fGlobalChange = true; + pTracker->fMask &= ~fBitMask; + } + } + else + { + pTracker->acPerBitUsage[iBit] += 1; + AssertMsg(pTracker->acPerBitUsage[iBit] > 0 && pTracker->acPerBitUsage[iBit] <= cMax, + ("pTracker->acPerBitUsage[%u]=%#x cMax=%#x\n", pszWhat, iBit, pTracker->acPerBitUsage[iBit], cMax)); + if (pTracker->acPerBitUsage[iBit] == 1) + { + fGlobalChange = true; + pTracker->fMask |= fBitMask; + } + } + + fChanged &= ~fBitMask; + } + +#ifdef VBOX_STRICT + vgdrvBitUsageTrackerCheckMask(pTracker, cMax, pszWhat); +#endif + NOREF(pszWhat); NOREF(cMax); + return fGlobalChange; +} + + +/** + * Init and termination worker for resetting the (host) event filter on the host + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param fFixedEvents Fixed events (init time). + */ +static int vgdrvResetEventFilterOnHost(PVBOXGUESTDEVEXT pDevExt, uint32_t fFixedEvents) +{ + VMMDevCtlGuestFilterMask *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask); + if (RT_SUCCESS(rc)) + { + pReq->u32NotMask = UINT32_MAX & ~fFixedEvents; + pReq->u32OrMask = fFixedEvents; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + LogRelFunc(("failed with rc=%Rrc\n", rc)); + VbglR0GRFree(&pReq->header); + } + RT_NOREF1(pDevExt); + return rc; +} + + +/** + * Changes the event filter mask for the given session. + * + * This is called in response to VBGL_IOCTL_CHANGE_FILTER_MASK as well as to do + * session cleanup. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param fOrMask The events to add. + * @param fNotMask The events to remove. + * @param fSessionTermination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes the session spinlock. + */ +static int vgdrvSetSessionEventFilter(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination) +{ + VMMDevCtlGuestFilterMask *pReq; + uint32_t fChanged; + uint32_t fPrevious; + int rc; + + /* + * Preallocate a request buffer so we can do all in one go without leaving the spinlock. + */ + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask); + if (RT_SUCCESS(rc)) + { /* nothing */ } + else if (!fSessionTermination) + { + LogRel(("vgdrvSetSessionFilterMask: VbglR0GRAlloc failure: %Rrc\n", rc)); + return rc; + } + else + pReq = NULL; /* Ignore failure, we must do session cleanup. */ + + + RTSpinlockAcquire(pDevExt->SessionSpinlock); + + /* + * Apply the changes to the session mask. + */ + fPrevious = pSession->fEventFilter; + pSession->fEventFilter |= fOrMask; + pSession->fEventFilter &= ~fNotMask; + + /* + * If anything actually changed, update the global usage counters. + */ + fChanged = fPrevious ^ pSession->fEventFilter; + LogFlow(("vgdrvSetSessionEventFilter: Session->fEventFilter: %#x -> %#x (changed %#x)\n", + fPrevious, pSession->fEventFilter, fChanged)); + if (fChanged) + { + bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->EventFilterTracker, fChanged, fPrevious, + pDevExt->cSessions, "EventFilterTracker"); + + /* + * If there are global changes, update the event filter on the host. + */ + if (fGlobalChange || pDevExt->fEventFilterHost == UINT32_MAX) + { + Assert(pReq || fSessionTermination); + if (pReq) + { + pReq->u32OrMask = pDevExt->fFixedEvents | pDevExt->EventFilterTracker.fMask; + if (pReq->u32OrMask == pDevExt->fEventFilterHost) + rc = VINF_SUCCESS; + else + { + pDevExt->fEventFilterHost = pReq->u32OrMask; + pReq->u32NotMask = ~pReq->u32OrMask; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + { + /* + * Failed, roll back (unless it's session termination time). + */ + pDevExt->fEventFilterHost = UINT32_MAX; + if (!fSessionTermination) + { + vgdrvBitUsageTrackerChange(&pDevExt->EventFilterTracker, fChanged, pSession->fEventFilter, + pDevExt->cSessions, "EventFilterTracker"); + pSession->fEventFilter = fPrevious; + } + } + } + } + else + rc = VINF_SUCCESS; + } + } + + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (pReq) + VbglR0GRFree(&pReq->header); + return rc; +} + + +/** + * Handle VBGL_IOCTL_CHANGE_FILTER_MASK. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pInfo The request. + */ +static int vgdrvIoCtl_ChangeFilterMask(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCCHANGEFILTERMASK pInfo) +{ + LogFlow(("VBGL_IOCTL_CHANGE_FILTER_MASK: or=%#x not=%#x\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask)); + + if ((pInfo->u.In.fOrMask | pInfo->u.In.fNotMask) & ~VMMDEV_EVENT_VALID_EVENT_MASK) + { + Log(("VBGL_IOCTL_CHANGE_FILTER_MASK: or=%#x not=%#x: Invalid masks!\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask)); + return VERR_INVALID_PARAMETER; + } + + return vgdrvSetSessionEventFilter(pDevExt, pSession, pInfo->u.In.fOrMask, pInfo->u.In.fNotMask, false /*fSessionTermination*/); +} + + +/** + * Init and termination worker for set mouse feature status to zero on the host. + * + * @returns VBox status code. + * @param pDevExt The device extension. + */ +static int vgdrvResetMouseStatusOnHost(PVBOXGUESTDEVEXT pDevExt) +{ + VMMDevReqMouseStatus *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetMouseStatus); + if (RT_SUCCESS(rc)) + { + pReq->mouseFeatures = 0; + pReq->pointerXPos = 0; + pReq->pointerYPos = 0; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + LogRelFunc(("failed with rc=%Rrc\n", rc)); + VbglR0GRFree(&pReq->header); + } + RT_NOREF1(pDevExt); + return rc; +} + + +/** + * Changes the mouse status mask for the given session. + * + * This is called in response to VBOXGUEST_IOCTL_SET_MOUSE_STATUS as well as to + * do session cleanup. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param fOrMask The status flags to add. + * @param fNotMask The status flags to remove. + * @param fSessionTermination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes the session spinlock. + */ +static int vgdrvSetSessionMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, bool fSessionTermination) +{ + VMMDevReqMouseStatus *pReq; + uint32_t fChanged; + uint32_t fPrevious; + int rc; + + /* + * Preallocate a request buffer so we can do all in one go without leaving the spinlock. + */ + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetMouseStatus); + if (RT_SUCCESS(rc)) + { + if (!fSessionTermination) + pReq->header.fRequestor = pSession->fRequestor; + } + else if (!fSessionTermination) + { + LogRel(("vgdrvSetSessionMouseStatus: VbglR0GRAlloc failure: %Rrc\n", rc)); + return rc; + } + else + pReq = NULL; /* Ignore failure, we must do session cleanup. */ + + + RTSpinlockAcquire(pDevExt->SessionSpinlock); + + /* + * Apply the changes to the session mask. + */ + fPrevious = pSession->fMouseStatus; + pSession->fMouseStatus |= fOrMask; + pSession->fMouseStatus &= ~fNotMask; + + /* + * If anything actually changed, update the global usage counters. + */ + fChanged = fPrevious ^ pSession->fMouseStatus; + if (fChanged) + { + bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->MouseStatusTracker, fChanged, fPrevious, + pDevExt->cSessions, "MouseStatusTracker"); + + /* + * If there are global changes, update the event filter on the host. + */ + if (fGlobalChange || pDevExt->fMouseStatusHost == UINT32_MAX) + { + Assert(pReq || fSessionTermination); + if (pReq) + { + pReq->mouseFeatures = pDevExt->MouseStatusTracker.fMask; + if (pReq->mouseFeatures == pDevExt->fMouseStatusHost) + rc = VINF_SUCCESS; + else + { + pDevExt->fMouseStatusHost = pReq->mouseFeatures; + pReq->pointerXPos = 0; + pReq->pointerYPos = 0; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + { + /* + * Failed, roll back (unless it's session termination time). + */ + pDevExt->fMouseStatusHost = UINT32_MAX; + if (!fSessionTermination) + { + vgdrvBitUsageTrackerChange(&pDevExt->MouseStatusTracker, fChanged, pSession->fMouseStatus, + pDevExt->cSessions, "MouseStatusTracker"); + pSession->fMouseStatus = fPrevious; + } + } + } + } + else + rc = VINF_SUCCESS; + } + } + + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (pReq) + VbglR0GRFree(&pReq->header); + return rc; +} + + +/** + * Sets the mouse status features for this session and updates them globally. + * + * @returns VBox status code. + * + * @param pDevExt The device extention. + * @param pSession The session. + * @param fFeatures New bitmap of enabled features. + */ +static int vgdrvIoCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fFeatures) +{ + LogFlow(("VBGL_IOCTL_SET_MOUSE_STATUS: features=%#x\n", fFeatures)); + + if (fFeatures & ~VMMDEV_MOUSE_GUEST_MASK) + return VERR_INVALID_PARAMETER; + + return vgdrvSetSessionMouseStatus(pDevExt, pSession, fFeatures, ~fFeatures, false /*fSessionTermination*/); +} + + +/** + * Return the mask of VMM device events that this session is allowed to see (wrt + * to "acquire" mode guest capabilities). + * + * The events associated with guest capabilities in "acquire" mode will be + * restricted to sessions which has acquired the respective capabilities. + * If someone else tries to wait for acquired events, they won't be woken up + * when the event becomes pending. Should some other thread in the session + * acquire the capability while the corresponding event is pending, the waiting + * thread will woken up. + * + * @returns Mask of events valid for the given session. + * @param pDevExt The device extension. + * @param pSession The session. + * + * @remarks Needs only be called when dispatching events in the + * VBOXGUEST_ACQUIRE_STYLE_EVENTS mask. + */ +static uint32_t vgdrvGetAllowedEventMaskForSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + uint32_t fAcquireModeGuestCaps; + uint32_t fAcquiredGuestCaps; + uint32_t fAllowedEvents; + + /* + * Note! Reads pSession->fAcquiredGuestCaps and pDevExt->fAcquireModeGuestCaps + * WITHOUT holding VBOXGUESTDEVEXT::SessionSpinlock. + */ + fAcquireModeGuestCaps = ASMAtomicUoReadU32(&pDevExt->fAcquireModeGuestCaps); + if (fAcquireModeGuestCaps == 0) + return VMMDEV_EVENT_VALID_EVENT_MASK; + fAcquiredGuestCaps = ASMAtomicUoReadU32(&pSession->fAcquiredGuestCaps); + + /* + * Calculate which events to allow according to the cap config and caps + * acquired by the session. + */ + fAllowedEvents = VMMDEV_EVENT_VALID_EVENT_MASK; + if ( !(fAcquiredGuestCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS) + && (fAcquireModeGuestCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS)) + fAllowedEvents &= ~VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + + if ( !(fAcquiredGuestCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) + && (fAcquireModeGuestCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS)) + fAllowedEvents &= ~VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + + return fAllowedEvents; +} + + +/** + * Init and termination worker for set guest capabilities to zero on the host. + * + * @returns VBox status code. + * @param pDevExt The device extension. + */ +static int vgdrvResetCapabilitiesOnHost(PVBOXGUESTDEVEXT pDevExt) +{ + VMMDevReqGuestCapabilities2 *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities); + if (RT_SUCCESS(rc)) + { + pReq->u32NotMask = UINT32_MAX; + pReq->u32OrMask = 0; + rc = VbglR0GRPerform(&pReq->header); + + if (RT_FAILURE(rc)) + LogRelFunc(("failed with rc=%Rrc\n", rc)); + VbglR0GRFree(&pReq->header); + } + RT_NOREF1(pDevExt); + return rc; +} + + +/** + * Sets the guest capabilities to the host while holding the lock. + * + * This will ASSUME that we're the ones in charge of the mask, so + * we'll simply clear all bits we don't set. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pReq The request. + */ +static int vgdrvUpdateCapabilitiesOnHostWithReqAndLock(PVBOXGUESTDEVEXT pDevExt, VMMDevReqGuestCapabilities2 *pReq) +{ + int rc; + + pReq->u32OrMask = pDevExt->fAcquiredGuestCaps | pDevExt->SetGuestCapsTracker.fMask; + if (pReq->u32OrMask == pDevExt->fGuestCapsHost) + rc = VINF_SUCCESS; + else + { + pDevExt->fGuestCapsHost = pReq->u32OrMask; + pReq->u32NotMask = ~pReq->u32OrMask; + rc = VbglR0GRPerform(&pReq->header); + if (RT_FAILURE(rc)) + pDevExt->fGuestCapsHost = UINT32_MAX; + } + + return rc; +} + + +/** + * Switch a set of capabilities into "acquire" mode and (maybe) acquire them for + * the given session. + * + * This is called in response to VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE as well as + * to do session cleanup. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param fOrMask The capabilities to add . + * @param fNotMask The capabilities to remove. Ignored in + * VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE. + * @param fFlags Confusing operation modifier. + * VBOXGUESTCAPSACQUIRE_FLAGS_NONE means to both + * configure and acquire/release the capabilities. + * VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE + * means only configure capabilities in the + * @a fOrMask capabilities for "acquire" mode. + * @param fSessionTermination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes both the session and event spinlocks. + */ +static int vgdrvAcquireSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, uint32_t fFlags, + bool fSessionTermination) +{ + uint32_t fCurrentOwnedCaps; + uint32_t fSessionRemovedCaps; + uint32_t fSessionAddedCaps; + uint32_t fOtherConflictingCaps; + VMMDevReqGuestCapabilities2 *pReq = NULL; + int rc; + + + /* + * Validate and adjust input. + */ + if (fOrMask & ~( VMMDEV_GUEST_SUPPORTS_SEAMLESS + | VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING + | VMMDEV_GUEST_SUPPORTS_GRAPHICS ) ) + { + LogRel(("vgdrvAcquireSessionCapabilities: invalid fOrMask=%#x (pSession=%p fNotMask=%#x fFlags=%#x)\n", + fOrMask, pSession, fNotMask, fFlags)); + return VERR_INVALID_PARAMETER; + } + + if ((fFlags & ~VBGL_IOC_AGC_FLAGS_VALID_MASK) != 0) + { + LogRel(("vgdrvAcquireSessionCapabilities: invalid fFlags=%#x (pSession=%p fOrMask=%#x fNotMask=%#x)\n", + fFlags, pSession, fOrMask, fNotMask)); + return VERR_INVALID_PARAMETER; + } + Assert(!fOrMask || !fSessionTermination); + + /* The fNotMask no need to have all values valid, invalid ones will simply be ignored. */ + fNotMask &= ~fOrMask; + + /* + * Preallocate a update request if we're about to do more than just configure + * the capability mode. + */ + if (!(fFlags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE)) + { + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities); + if (RT_SUCCESS(rc)) + { + if (!fSessionTermination) + pReq->header.fRequestor = pSession->fRequestor; + } + else if (!fSessionTermination) + { + LogRel(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: VbglR0GRAlloc failure: %Rrc\n", + pSession, fOrMask, fNotMask, fFlags, rc)); + return rc; + } + else + pReq = NULL; /* Ignore failure, we must do session cleanup. */ + } + + /* + * Try switch the capabilities in the OR mask into "acquire" mode. + * + * Note! We currently ignore anyone which may already have "set" the capabilities + * in fOrMask. Perhaps not the best way to handle it, but it's simple... + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + + if (!(pDevExt->fSetModeGuestCaps & fOrMask)) + pDevExt->fAcquireModeGuestCaps |= fOrMask; + else + { + RTSpinlockRelease(pDevExt->EventSpinlock); + + if (pReq) + VbglR0GRFree(&pReq->header); + AssertMsgFailed(("Trying to change caps mode: %#x\n", fOrMask)); + LogRel(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: calling caps acquire for set caps\n", + pSession, fOrMask, fNotMask, fFlags)); + return VERR_INVALID_STATE; + } + + /* + * If we only wanted to switch the capabilities into "acquire" mode, we're done now. + */ + if (fFlags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE) + { + RTSpinlockRelease(pDevExt->EventSpinlock); + + Assert(!pReq); + Log(("vgdrvAcquireSessionCapabilities: pSession=%p fOrMask=%#x fNotMask=%#x fFlags=%#x: configured acquire caps: 0x%x\n", + pSession, fOrMask, fNotMask, fFlags)); + return VINF_SUCCESS; + } + Assert(pReq || fSessionTermination); + + /* + * Caller wants to acquire/release the capabilities too. + * + * Note! The mode change of the capabilities above won't be reverted on + * failure, this is intentional. + */ + fCurrentOwnedCaps = pSession->fAcquiredGuestCaps; + fSessionRemovedCaps = fCurrentOwnedCaps & fNotMask; + fSessionAddedCaps = fOrMask & ~fCurrentOwnedCaps; + fOtherConflictingCaps = pDevExt->fAcquiredGuestCaps & ~fCurrentOwnedCaps; + fOtherConflictingCaps &= fSessionAddedCaps; + + if (!fOtherConflictingCaps) + { + if (fSessionAddedCaps) + { + pSession->fAcquiredGuestCaps |= fSessionAddedCaps; + pDevExt->fAcquiredGuestCaps |= fSessionAddedCaps; + } + + if (fSessionRemovedCaps) + { + pSession->fAcquiredGuestCaps &= ~fSessionRemovedCaps; + pDevExt->fAcquiredGuestCaps &= ~fSessionRemovedCaps; + } + + /* + * If something changes (which is very likely), tell the host. + */ + if (fSessionAddedCaps || fSessionRemovedCaps || pDevExt->fGuestCapsHost == UINT32_MAX) + { + Assert(pReq || fSessionTermination); + if (pReq) + { + rc = vgdrvUpdateCapabilitiesOnHostWithReqAndLock(pDevExt, pReq); + if (RT_FAILURE(rc) && !fSessionTermination) + { + /* Failed, roll back. */ + if (fSessionAddedCaps) + { + pSession->fAcquiredGuestCaps &= ~fSessionAddedCaps; + pDevExt->fAcquiredGuestCaps &= ~fSessionAddedCaps; + } + if (fSessionRemovedCaps) + { + pSession->fAcquiredGuestCaps |= fSessionRemovedCaps; + pDevExt->fAcquiredGuestCaps |= fSessionRemovedCaps; + } + + RTSpinlockRelease(pDevExt->EventSpinlock); + LogRel(("vgdrvAcquireSessionCapabilities: vgdrvUpdateCapabilitiesOnHostWithReqAndLock failed: rc=%Rrc\n", rc)); + VbglR0GRFree(&pReq->header); + return rc; + } + } + } + } + else + { + RTSpinlockRelease(pDevExt->EventSpinlock); + + Log(("vgdrvAcquireSessionCapabilities: Caps %#x were busy\n", fOtherConflictingCaps)); + VbglR0GRFree(&pReq->header); + return VERR_RESOURCE_BUSY; + } + + RTSpinlockRelease(pDevExt->EventSpinlock); + if (pReq) + VbglR0GRFree(&pReq->header); + + /* + * If we added a capability, check if that means some other thread in our + * session should be unblocked because there are events pending. + * + * HACK ALERT! When the seamless support capability is added we generate a + * seamless change event so that the ring-3 client can sync with + * the seamless state. Although this introduces a spurious + * wakeups of the ring-3 client, it solves the problem of client + * state inconsistency in multiuser environment (on Windows). + */ + if (fSessionAddedCaps) + { + uint32_t fGenFakeEvents = 0; + if (fSessionAddedCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) + fGenFakeEvents |= VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + + RTSpinlockAcquire(pDevExt->EventSpinlock); + if (fGenFakeEvents || pDevExt->f32PendingEvents) + vgdrvDispatchEventsLocked(pDevExt, fGenFakeEvents); + RTSpinlockRelease(pDevExt->EventSpinlock); + +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + VGDrvCommonWaitDoWakeUps(pDevExt); +#endif + } + + return VINF_SUCCESS; +} + + +/** + * Handle VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pAcquire The request. + */ +static int vgdrvIoCtl_GuestCapsAcquire(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCACQUIREGUESTCAPS pAcquire) +{ + int rc; + LogFlow(("VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES: or=%#x not=%#x flags=%#x\n", + pAcquire->u.In.fOrMask, pAcquire->u.In.fNotMask, pAcquire->u.In.fFlags)); + + rc = vgdrvAcquireSessionCapabilities(pDevExt, pSession, pAcquire->u.In.fOrMask, pAcquire->u.In.fNotMask, + pAcquire->u.In.fFlags, false /*fSessionTermination*/); + if (RT_FAILURE(rc)) + LogRel(("VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES failed rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Sets the guest capabilities for a session. + * + * @returns VBox status code. + * @param pDevExt The device extension. + * @param pSession The session. + * @param fOrMask The capabilities to add. + * @param fNotMask The capabilities to remove. + * @param pfSessionCaps Where to return the guest capabilities reported + * for this session. Optional. + * @param pfGlobalCaps Where to return the guest capabilities reported + * for all the sessions. Optional. + * + * @param fSessionTermination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes the session spinlock. + */ +static int vgdrvSetSessionCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + uint32_t fOrMask, uint32_t fNotMask, uint32_t *pfSessionCaps, uint32_t *pfGlobalCaps, + bool fSessionTermination) +{ + /* + * Preallocate a request buffer so we can do all in one go without leaving the spinlock. + */ + VMMDevReqGuestCapabilities2 *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_SetGuestCapabilities); + if (RT_SUCCESS(rc)) + { + if (!fSessionTermination) + pReq->header.fRequestor = pSession->fRequestor; + } + else if (!fSessionTermination) + { + if (pfSessionCaps) + *pfSessionCaps = UINT32_MAX; + if (pfGlobalCaps) + *pfGlobalCaps = UINT32_MAX; + LogRel(("vgdrvSetSessionCapabilities: VbglR0GRAlloc failure: %Rrc\n", rc)); + return rc; + } + else + pReq = NULL; /* Ignore failure, we must do session cleanup. */ + + + RTSpinlockAcquire(pDevExt->SessionSpinlock); + +#ifndef VBOXGUEST_DISREGARD_ACQUIRE_MODE_GUEST_CAPS + /* + * Capabilities in "acquire" mode cannot be set via this API. + * (Acquire mode is only used on windows at the time of writing.) + */ + if (!(fOrMask & pDevExt->fAcquireModeGuestCaps)) +#endif + { + /* + * Apply the changes to the session mask. + */ + uint32_t fChanged; + uint32_t fPrevious = pSession->fCapabilities; + pSession->fCapabilities |= fOrMask; + pSession->fCapabilities &= ~fNotMask; + + /* + * If anything actually changed, update the global usage counters. + */ + fChanged = fPrevious ^ pSession->fCapabilities; + if (fChanged) + { + bool fGlobalChange = vgdrvBitUsageTrackerChange(&pDevExt->SetGuestCapsTracker, fChanged, fPrevious, + pDevExt->cSessions, "SetGuestCapsTracker"); + + /* + * If there are global changes, update the capabilities on the host. + */ + if (fGlobalChange || pDevExt->fGuestCapsHost == UINT32_MAX) + { + Assert(pReq || fSessionTermination); + if (pReq) + { + rc = vgdrvUpdateCapabilitiesOnHostWithReqAndLock(pDevExt, pReq); + + /* On failure, roll back (unless it's session termination time). */ + if (RT_FAILURE(rc) && !fSessionTermination) + { + vgdrvBitUsageTrackerChange(&pDevExt->SetGuestCapsTracker, fChanged, pSession->fCapabilities, + pDevExt->cSessions, "SetGuestCapsTracker"); + pSession->fCapabilities = fPrevious; + } + } + } + } + } +#ifndef VBOXGUEST_DISREGARD_ACQUIRE_MODE_GUEST_CAPS + else + rc = VERR_RESOURCE_BUSY; +#endif + + if (pfSessionCaps) + *pfSessionCaps = pSession->fCapabilities; + if (pfGlobalCaps) + *pfGlobalCaps = pDevExt->fAcquiredGuestCaps | pDevExt->SetGuestCapsTracker.fMask; + + RTSpinlockRelease(pDevExt->SessionSpinlock); + if (pReq) + VbglR0GRFree(&pReq->header); + return rc; +} + + +/** + * Handle VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES. + * + * @returns VBox status code. + * + * @param pDevExt The device extension. + * @param pSession The session. + * @param pInfo The request. + */ +static int vgdrvIoCtl_SetCapabilities(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLIOCSETGUESTCAPS pInfo) +{ + int rc; + LogFlow(("VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES: or=%#x not=%#x\n", pInfo->u.In.fOrMask, pInfo->u.In.fNotMask)); + + if (!((pInfo->u.In.fOrMask | pInfo->u.In.fNotMask) & ~VMMDEV_GUEST_CAPABILITIES_MASK)) + rc = vgdrvSetSessionCapabilities(pDevExt, pSession, pInfo->u.In.fOrMask, pInfo->u.In.fNotMask, + &pInfo->u.Out.fSessionCaps, &pInfo->u.Out.fGlobalCaps, false /*fSessionTermination*/); + else + rc = VERR_INVALID_PARAMETER; + + return rc; +} + +/** @} */ + + +/** + * Common IOCtl for user to kernel and kernel to kernel communication. + * + * This function only does the basic validation and then invokes + * worker functions that takes care of each specific function. + * + * @returns VBox status code. + * + * @param iFunction The requested function. + * @param pDevExt The device extension. + * @param pSession The client session. + * @param pReqHdr Pointer to the request. This always starts with + * a request common header. + * @param cbReq The max size of the request buffer. + */ +int VGDrvCommonIoCtl(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PVBGLREQHDR pReqHdr, size_t cbReq) +{ + uintptr_t const iFunctionStripped = VBGL_IOCTL_CODE_STRIPPED(iFunction); + int rc; + + LogFlow(("VGDrvCommonIoCtl: iFunction=%#x pDevExt=%p pSession=%p pReqHdr=%p cbReq=%zu\n", + iFunction, pDevExt, pSession, pReqHdr, cbReq)); + + /* + * Define some helper macros to simplify validation. + */ +#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \ + do { \ + if (RT_LIKELY( pReqHdr->cbIn == (cbInExpect) \ + && ( pReqHdr->cbOut == (cbOutExpect) \ + || ((cbInExpect) == (cbOutExpect) && pReqHdr->cbOut == 0) ) )) \ + { /* likely */ } \ + else \ + { \ + Log(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \ + (long)pReqHdr->cbIn, (long)(cbInExpect), (long)pReqHdr->cbOut, (long)(cbOutExpect))); \ + return pReqHdr->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_LIKELY(pReqHdr->cbIn == (cbInExpect))) \ + { /* likely */ } \ + else \ + { \ + Log(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld.\n", \ + (long)pReqHdr->cbIn, (long)(cbInExpect))); \ + return pReqHdr->rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_SIZE_OUT(Name, cbOutExpect) \ + do { \ + if (RT_LIKELY( pReqHdr->cbOut == (cbOutExpect) \ + || (pReqHdr->cbOut == 0 && pReqHdr->cbIn == (cbOutExpect)))) \ + { /* likely */ } \ + else \ + { \ + Log(( #Name ": Invalid input/output sizes. cbOut=%ld (%ld) expected %ld.\n", \ + (long)pReqHdr->cbOut, (long)pReqHdr->cbIn, (long)(cbOutExpect))); \ + return pReqHdr->rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_EXPR(Name, expr) \ + do { \ + if (RT_LIKELY(!!(expr))) \ + { /* likely */ } \ + else \ + { \ + Log(( #Name ": %s\n", #expr)); \ + return pReqHdr->rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_EXPR_FMT(expr, fmt) \ + do { \ + if (RT_LIKELY(!!(expr))) \ + { /* likely */ } \ + else \ + { \ + Log( fmt ); \ + return pReqHdr->rc = VERR_INVALID_PARAMETER; \ + } \ + } while (0) + +#define REQ_CHECK_RING0(mnemonic) \ + do { \ + if (pSession->R0Process != NIL_RTR0PROCESS) \ + { \ + LogFunc((mnemonic ": Ring-0 only, caller is %RTproc/%p\n", \ + pSession->Process, (uintptr_t)pSession->R0Process)); \ + return pReqHdr->rc = VERR_PERMISSION_DENIED; \ + } \ + } while (0) + + + /* + * Validate the request. + */ + if (RT_LIKELY(cbReq >= sizeof(*pReqHdr))) + { /* likely */ } + else + { + Log(("VGDrvCommonIoCtl: Bad ioctl request size; cbReq=%#lx\n", (long)cbReq)); + return VERR_INVALID_PARAMETER; + } + + if (pReqHdr->cbOut == 0) + pReqHdr->cbOut = pReqHdr->cbIn; + + if (RT_LIKELY( pReqHdr->uVersion == VBGLREQHDR_VERSION + && pReqHdr->cbIn >= sizeof(*pReqHdr) + && pReqHdr->cbIn <= cbReq + && pReqHdr->cbOut >= sizeof(*pReqHdr) + && pReqHdr->cbOut <= cbReq)) + { /* likely */ } + else + { + Log(("VGDrvCommonIoCtl: Bad ioctl request header; cbIn=%#lx cbOut=%#lx version=%#lx\n", + (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->uVersion)); + return VERR_INVALID_PARAMETER; + } + + if (RT_LIKELY(RT_VALID_PTR(pSession))) + { /* likely */ } + else + { + Log(("VGDrvCommonIoCtl: Invalid pSession value %p (ioctl=%#x)\n", pSession, iFunction)); + return VERR_INVALID_PARAMETER; + } + + + /* + * Deal with variably sized requests first. + */ + rc = VINF_SUCCESS; + if ( iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_VMMDEV_REQUEST(0)) + || iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_VMMDEV_REQUEST_BIG) ) + { + REQ_CHECK_EXPR(VBGL_IOCTL_VMMDEV_REQUEST, pReqHdr->uType != VBGLREQHDR_TYPE_DEFAULT); + REQ_CHECK_EXPR_FMT(pReqHdr->cbIn == pReqHdr->cbOut, + ("VBGL_IOCTL_VMMDEV_REQUEST: cbIn=%ld != cbOut=%ld\n", (long)pReqHdr->cbIn, (long)pReqHdr->cbOut)); + pReqHdr->rc = vgdrvIoCtl_VMMDevRequest(pDevExt, pSession, (VMMDevRequestHeader *)pReqHdr, cbReq); + } + else if (RT_LIKELY(pReqHdr->uType == VBGLREQHDR_TYPE_DEFAULT)) + { + if (iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_LOG(0))) + { + REQ_CHECK_SIZE_OUT(VBGL_IOCTL_LOG, VBGL_IOCTL_LOG_SIZE_OUT); + pReqHdr->rc = vgdrvIoCtl_Log(pDevExt, &((PVBGLIOCLOG)pReqHdr)->u.In.szMsg[0], pReqHdr->cbIn - sizeof(VBGLREQHDR), + pSession->fUserSession); + } +#ifdef VBOX_WITH_HGCM + else if (iFunction == VBGL_IOCTL_IDC_HGCM_FAST_CALL) /* (is variable size, but we don't bother encoding it) */ + { + REQ_CHECK_RING0("VBGL_IOCTL_IDC_HGCM_FAST_CALL"); + REQ_CHECK_EXPR(VBGL_IOCTL_IDC_HGCM_FAST_CALL, cbReq >= sizeof(VBGLIOCIDCHGCMFASTCALL) + sizeof(VMMDevHGCMCall)); + pReqHdr->rc = vgdrvIoCtl_HGCMFastCall(pDevExt, (VBGLIOCIDCHGCMFASTCALL volatile *)pReqHdr); + } + else if ( iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL(0)) +# if ARCH_BITS == 64 + || iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_32(0)) +# endif + ) + { + REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn >= sizeof(VBGLIOCHGCMCALL)); + REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn == pReqHdr->cbOut); + pReqHdr->rc = vgdrvIoCtl_HGCMCallWrapper(pDevExt, pSession, (PVBGLIOCHGCMCALL)pReqHdr, + iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_32(0)), + false /*fUserData*/, cbReq); + } + else if (iFunctionStripped == VBGL_IOCTL_CODE_STRIPPED(VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(0))) + { + REQ_CHECK_RING0("VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA"); + REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn >= sizeof(VBGLIOCHGCMCALL)); + REQ_CHECK_EXPR(VBGL_IOCTL_HGCM_CALL, pReqHdr->cbIn == pReqHdr->cbOut); + pReqHdr->rc = vgdrvIoCtl_HGCMCallWrapper(pDevExt, pSession, (PVBGLIOCHGCMCALL)pReqHdr, + ARCH_BITS == 32, true /*fUserData*/, cbReq); + } +#endif /* VBOX_WITH_HGCM */ + else + { + switch (iFunction) + { + /* + * Ring-0 only: + */ + case VBGL_IOCTL_IDC_CONNECT: + REQ_CHECK_RING0("VBGL_IOCL_IDC_CONNECT"); + REQ_CHECK_SIZES(VBGL_IOCTL_IDC_CONNECT); + pReqHdr->rc = vgdrvIoCtl_IdcConnect(pDevExt, pSession, (PVBGLIOCIDCCONNECT)pReqHdr); + break; + + case VBGL_IOCTL_IDC_DISCONNECT: + REQ_CHECK_RING0("VBGL_IOCTL_IDC_DISCONNECT"); + REQ_CHECK_SIZES(VBGL_IOCTL_IDC_DISCONNECT); + pReqHdr->rc = vgdrvIoCtl_IdcDisconnect(pDevExt, pSession, (PVBGLIOCIDCDISCONNECT)pReqHdr); + break; + + case VBGL_IOCTL_GET_VMMDEV_IO_INFO: + REQ_CHECK_RING0("GET_VMMDEV_IO_INFO"); + REQ_CHECK_SIZES(VBGL_IOCTL_GET_VMMDEV_IO_INFO); + pReqHdr->rc = vgdrvIoCtl_GetVMMDevIoInfo(pDevExt, (PVBGLIOCGETVMMDEVIOINFO)pReqHdr); + break; + + case VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK: + REQ_CHECK_RING0("SET_MOUSE_NOTIFY_CALLBACK"); + REQ_CHECK_SIZES(VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK); + pReqHdr->rc = vgdrvIoCtl_SetMouseNotifyCallback(pDevExt, (PVBGLIOCSETMOUSENOTIFYCALLBACK)pReqHdr); + break; + + /* + * Ring-3 only: + */ + case VBGL_IOCTL_DRIVER_VERSION_INFO: + REQ_CHECK_SIZES(VBGL_IOCTL_DRIVER_VERSION_INFO); + pReqHdr->rc = vgdrvIoCtl_DriverVersionInfo(pDevExt, pSession, (PVBGLIOCDRIVERVERSIONINFO)pReqHdr); + break; + + /* + * Both ring-3 and ring-0: + */ + case VBGL_IOCTL_WAIT_FOR_EVENTS: + REQ_CHECK_SIZES(VBGL_IOCTL_WAIT_FOR_EVENTS); + pReqHdr->rc = vgdrvIoCtl_WaitForEvents(pDevExt, pSession, (VBGLIOCWAITFOREVENTS *)pReqHdr, + pSession->R0Process != NIL_RTR0PROCESS); + break; + + case VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS: + REQ_CHECK_SIZES(VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS); + pReqHdr->rc = vgdrvIoCtl_CancelAllWaitEvents(pDevExt, pSession); + break; + + case VBGL_IOCTL_CHANGE_FILTER_MASK: + REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_FILTER_MASK); + pReqHdr->rc = vgdrvIoCtl_ChangeFilterMask(pDevExt, pSession, (PVBGLIOCCHANGEFILTERMASK)pReqHdr); + break; + +#ifdef VBOX_WITH_HGCM + case VBGL_IOCTL_HGCM_CONNECT: + REQ_CHECK_SIZES(VBGL_IOCTL_HGCM_CONNECT); + pReqHdr->rc = vgdrvIoCtl_HGCMConnect(pDevExt, pSession, (PVBGLIOCHGCMCONNECT)pReqHdr); + break; + + case VBGL_IOCTL_HGCM_DISCONNECT: + REQ_CHECK_SIZES(VBGL_IOCTL_HGCM_DISCONNECT); + pReqHdr->rc = vgdrvIoCtl_HGCMDisconnect(pDevExt, pSession, (PVBGLIOCHGCMDISCONNECT)pReqHdr); + break; +#endif + + case VBGL_IOCTL_CHECK_BALLOON: + REQ_CHECK_SIZES(VBGL_IOCTL_CHECK_BALLOON); + pReqHdr->rc = vgdrvIoCtl_CheckMemoryBalloon(pDevExt, pSession, (PVBGLIOCCHECKBALLOON)pReqHdr); + break; + + case VBGL_IOCTL_CHANGE_BALLOON: + REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_BALLOON); + pReqHdr->rc = vgdrvIoCtl_ChangeMemoryBalloon(pDevExt, pSession, (PVBGLIOCCHANGEBALLOON)pReqHdr); + break; + + case VBGL_IOCTL_WRITE_CORE_DUMP: + REQ_CHECK_SIZES(VBGL_IOCTL_WRITE_CORE_DUMP); + pReqHdr->rc = vgdrvIoCtl_WriteCoreDump(pDevExt, pSession, (PVBGLIOCWRITECOREDUMP)pReqHdr); + break; + + case VBGL_IOCTL_SET_MOUSE_STATUS: + REQ_CHECK_SIZES(VBGL_IOCTL_SET_MOUSE_STATUS); + pReqHdr->rc = vgdrvIoCtl_SetMouseStatus(pDevExt, pSession, ((PVBGLIOCSETMOUSESTATUS)pReqHdr)->u.In.fStatus); + break; + + case VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES: + REQ_CHECK_SIZES(VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES); + pReqHdr->rc = vgdrvIoCtl_GuestCapsAcquire(pDevExt, pSession, (PVBGLIOCACQUIREGUESTCAPS)pReqHdr); + break; + + case VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES: + REQ_CHECK_SIZES(VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES); + pReqHdr->rc = vgdrvIoCtl_SetCapabilities(pDevExt, pSession, (PVBGLIOCSETGUESTCAPS)pReqHdr); + break; + +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER + case VBGL_IOCTL_DPC_LATENCY_CHECKER: + REQ_CHECK_SIZES(VBGL_IOCTL_DPC_LATENCY_CHECKER); + pReqHdr->rc = VGDrvNtIOCtl_DpcLatencyChecker(); + break; +#endif + + default: + { + LogRel(("VGDrvCommonIoCtl: Unknown request iFunction=%#x (stripped %#x) cbReq=%#x\n", + iFunction, iFunctionStripped, cbReq)); + pReqHdr->rc = rc = VERR_NOT_SUPPORTED; + break; + } + } + } + } + else + { + Log(("VGDrvCommonIoCtl: uType=%#x, expected default (ioctl=%#x)\n", pReqHdr->uType, iFunction)); + return VERR_INVALID_PARAMETER; + } + + LogFlow(("VGDrvCommonIoCtl: returns %Rrc (req: rc=%Rrc cbOut=%#x)\n", rc, pReqHdr->rc, pReqHdr->cbOut)); + return rc; +} + + +/** + * Used by VGDrvCommonISR as well as the acquire guest capability code. + * + * @returns VINF_SUCCESS on success. On failure, ORed together + * RTSemEventMultiSignal errors (completes processing despite errors). + * @param pDevExt The VBoxGuest device extension. + * @param fEvents The events to dispatch. + */ +static int vgdrvDispatchEventsLocked(PVBOXGUESTDEVEXT pDevExt, uint32_t fEvents) +{ + PVBOXGUESTWAIT pWait; + PVBOXGUESTWAIT pSafe; + int rc = VINF_SUCCESS; + + fEvents |= pDevExt->f32PendingEvents; + + RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode) + { + uint32_t fHandledEvents = pWait->fReqEvents & fEvents; + if ( fHandledEvents != 0 + && !pWait->fResEvents) + { + /* Does this one wait on any of the events we're dispatching? We do a quick + check first, then deal with VBOXGUEST_ACQUIRE_STYLE_EVENTS as applicable. */ + if (fHandledEvents & VBOXGUEST_ACQUIRE_STYLE_EVENTS) + fHandledEvents &= vgdrvGetAllowedEventMaskForSession(pDevExt, pWait->pSession); + if (fHandledEvents) + { + pWait->fResEvents = pWait->fReqEvents & fEvents & fHandledEvents; + fEvents &= ~pWait->fResEvents; + RTListNodeRemove(&pWait->ListNode); +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode); +#else + RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode); + rc |= RTSemEventMultiSignal(pWait->Event); +#endif + if (!fEvents) + break; + } + } + } + + ASMAtomicWriteU32(&pDevExt->f32PendingEvents, fEvents); + return rc; +} + + +/** + * Simply checks whether the IRQ is ours or not, does not do any interrupt + * procesing. + * + * @returns true if it was our interrupt, false if it wasn't. + * @param pDevExt The VBoxGuest device extension. + */ +bool VGDrvCommonIsOurIRQ(PVBOXGUESTDEVEXT pDevExt) +{ + VMMDevMemory volatile *pVMMDevMemory; + bool fOurIrq; + + RTSpinlockAcquire(pDevExt->EventSpinlock); + pVMMDevMemory = pDevExt->pVMMDevMemory; + fOurIrq = pVMMDevMemory ? pVMMDevMemory->V.V1_04.fHaveEvents : false; + RTSpinlockRelease(pDevExt->EventSpinlock); + + return fOurIrq; +} + + +/** + * Common interrupt service routine. + * + * This deals with events and with waking up thread waiting for those events. + * + * @returns true if it was our interrupt, false if it wasn't. + * @param pDevExt The VBoxGuest device extension. + */ +bool VGDrvCommonISR(PVBOXGUESTDEVEXT pDevExt) +{ + VMMDevEvents volatile *pReq; + bool fMousePositionChanged = false; + int rc = 0; + VMMDevMemory volatile *pVMMDevMemory; + bool fOurIrq; + + /* + * Make sure we've initialized the device extension. + */ + if (RT_LIKELY(pDevExt->fHostFeatures & VMMDEV_HVF_FAST_IRQ_ACK)) + pReq = NULL; + else if (RT_LIKELY((pReq = pDevExt->pIrqAckEvents) != NULL)) + { /* likely */ } + else + return false; + + /* + * Enter the spinlock and check if it's our IRQ or not. + */ + RTSpinlockAcquire(pDevExt->EventSpinlock); + pVMMDevMemory = pDevExt->pVMMDevMemory; + fOurIrq = pVMMDevMemory ? pVMMDevMemory->V.V1_04.fHaveEvents : false; + if (fOurIrq) + { + /* + * Acknowledge events. + * We don't use VbglR0GRPerform here as it may take another spinlocks. + */ + uint32_t fEvents; + if (!pReq) + { + fEvents = ASMInU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST_FAST); + ASMCompilerBarrier(); /* paranoia */ + rc = fEvents != UINT32_MAX ? VINF_SUCCESS : VERR_INTERNAL_ERROR; + } + else + { + pReq->header.rc = VERR_INTERNAL_ERROR; + pReq->events = 0; + ASMCompilerBarrier(); + ASMOutU32(pDevExt->IOPortBase + VMMDEV_PORT_OFF_REQUEST, (uint32_t)pDevExt->PhysIrqAckEvents); + ASMCompilerBarrier(); /* paranoia */ + fEvents = pReq->events; + rc = pReq->header.rc; + } + if (RT_SUCCESS(rc)) + { + Log3(("VGDrvCommonISR: acknowledge events succeeded %#RX32\n", fEvents)); + + /* + * VMMDEV_EVENT_MOUSE_POSITION_CHANGED can only be polled for. + */ + if (fEvents & VMMDEV_EVENT_MOUSE_POSITION_CHANGED) + { + fMousePositionChanged = true; + fEvents &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED; +#if !defined(VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT) + if (pDevExt->pfnMouseNotifyCallback) + pDevExt->pfnMouseNotifyCallback(pDevExt->pvMouseNotifyCallbackArg); +#endif + } + +#ifdef VBOX_WITH_HGCM + /* + * The HGCM event/list is kind of different in that we evaluate all entries. + */ + if (fEvents & VMMDEV_EVENT_HGCM) + { + PVBOXGUESTWAIT pWait; + PVBOXGUESTWAIT pSafe; + RTListForEachSafe(&pDevExt->HGCMWaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode) + { + if (pWait->pHGCMReq->fu32Flags & VBOX_HGCM_REQ_DONE) + { + pWait->fResEvents = VMMDEV_EVENT_HGCM; + RTListNodeRemove(&pWait->ListNode); +# ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode); +# else + RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode); + rc |= RTSemEventMultiSignal(pWait->Event); +# endif + } + } + fEvents &= ~VMMDEV_EVENT_HGCM; + } +#endif + + /* + * Normal FIFO waiter evaluation. + */ + rc |= vgdrvDispatchEventsLocked(pDevExt, fEvents); + } + else /* something is serious wrong... */ + Log(("VGDrvCommonISR: acknowledge events failed rc=%Rrc (events=%#x)!!\n", rc, fEvents)); + } + else + Log3(("VGDrvCommonISR: not ours\n")); + + RTSpinlockRelease(pDevExt->EventSpinlock); + + /* + * Execute the mouse notification callback here if it cannot be executed while + * holding the interrupt safe spinlock, see @bugref{8639}. + */ +#if defined(VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT) && !defined(RT_OS_WINDOWS) /* (Windows does this in the Dpc callback) */ + if ( fMousePositionChanged + && pDevExt->pfnMouseNotifyCallback) + pDevExt->pfnMouseNotifyCallback(pDevExt->pvMouseNotifyCallbackArg); +#endif + +#if defined(VBOXGUEST_USE_DEFERRED_WAKE_UP) && !defined(RT_OS_WINDOWS) + /* + * Do wake-ups. + * Note. On Windows this isn't possible at this IRQL, so a DPC will take + * care of it. Same on darwin, doing it in the work loop callback. + */ + VGDrvCommonWaitDoWakeUps(pDevExt); +#endif + + /* + * Work the poll and async notification queues on OSes that implements that. + * (Do this outside the spinlock to prevent some recursive spinlocking.) + */ + if (fMousePositionChanged) + { + ASMAtomicIncU32(&pDevExt->u32MousePosChangedSeq); + VGDrvNativeISRMousePollEvent(pDevExt); + } + + AssertMsg(rc == 0, ("rc=%#x (%d)\n", rc, rc)); + return fOurIrq; +} diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm b/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm new file mode 100644 index 00000000..4bd431fc --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm @@ -0,0 +1,1679 @@ +; $Id: VBoxGuestA-os2.asm $ +;; @file +; VBoxGuest - OS/2 assembly file, the first file in the link. +; + +; +; Copyright (C) 2007-2022 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox 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. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +;----------------------------------------------------------------------------- +; This code is based on: +; +; VBoxDrv - OS/2 assembly file, the first file in the link. +; +; Copyright (c) 2007-2010 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" +%include "iprt/err.mac" +%include "VBox/VBoxGuest.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 + + +;******************************************************************************* +;* Defined Constants And Macros * +;******************************************************************************* +; Some devhdr.inc stuff. +%define DEVLEV_3 0180h +%define DEV_30 0800h +%define DEV_IOCTL 4000h +%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_EOI 031h +%define DevHlp_SetIRQ 01bh +%define DevHlp_PhysToVirt 015h + +; Fast IOCtl category, also defined in VBoxGuest.h +%define VBGL_IOCTL_CATEGORY_FAST 0c3h + +;; +; Got some nasm/link trouble, so emit the stuff ourselves. +; @param %1 Must be a GLOBALNAME. +%macro JMP32TO16 1 + ;jmp far dword NAME(%1) wrt CODE16 + db 066h + db 0eah + dw NAME(%1) wrt CODE16 + dw CODE16 +%endmacro + +;; +; Got some nasm/link trouble, so emit the stuff ourselves. +; @param %1 Must be a GLOBALNAME. +%macro JMP16TO32 1 + ;jmp far dword NAME(%1) wrt FLAT + db 066h + db 0eah + dd NAME(%1) ;wrt FLAT + dw TEXT32 wrt FLAT +%endmacro + + +;******************************************************************************* +;* External Symbols * +;******************************************************************************* +segment CODE16 +extern DOS16OPEN +extern DOS16CLOSE +extern DOS16WRITE +extern DOS16DEVIOCTL2 +segment TEXT32 +extern KernThunkStackTo32 +extern KernThunkStackTo16 + +extern NAME(vgdrvOS2Init) +extern NAME(vgdrvOS2Open) +extern NAME(vgdrvOS2Close) +extern NAME(vgdrvOS2IOCtl) +extern NAME(vgdrvOS2IOCtlFast) +extern NAME(vgdrvOS2IDCConnect) +extern NAME(VGDrvOS2IDCService) +extern NAME(vgdrvOS2ISR) + + +segment DATA16 + +;; +; Device headers. The first one is the one we'll be opening and the +; latter is only used for 32-bit initialization. +GLOBALNAME g_VBoxGuestHdr1 + dw NAME(g_VBoxGuestHdr2) wrt DATA16 ; NextHeader.off + dw DATA16 ; NextHeader.sel + dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV | DEV_IOCTL; SDevAtt + dw NAME(VGDrvOS2Entrypoint) wrt CODE16 ; StrategyEP + dw NAME(VGDrvOS2IDC) wrt CODE16 ; IDCEP + db 'vboxgst$' ; DevName + dw 0 ; SDevProtCS + dw 0 ; SDevProtDS + dw 0 ; SDevRealCS + dw 0 ; SDevRealDS + dd DEV_16MB | DEV_IOCTL2 ; SDevCaps + +align 4 +GLOBALNAME g_VBoxGuestHdr2 + dd 0ffffffffh ; NextHeader (NIL) + dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV ; SDevAtt + dw NAME(vgdrvOS2InitEntrypoint) wrt CODE16 ; StrategyEP + dw 0 ; IDCEP + db 'vboxgs1$' ; 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 + + +;; vgdrvFindAdapter Output +; @{ + +;; The MMIO base of the VMMDev. +GLOBALNAME g_PhysMMIOBase + dd 0 +;; The size of the MMIO memory of the VMMDev. +GLOBALNAME g_cbMMIO + dd 0 +;; The I/O port base of the VMMDev. +GLOBALNAME g_IOPortBase + dw 0 +;; The VMMDev Interrupt Line. +GLOBALNAME g_bInterruptLine + db 0 +;; The PCI bus number returned by Find PCI Device. +GLOBALNAME g_bPciBusNo + db 0 +;; The PCI Device No / Function Number returned by Find PCI Device. +; (The upper 5 bits is the number, and the lower 3 the function.) +GLOBALNAME g_bPciDevFunNo + db 0 +;; Flag that is set by the vboxgst$ init routine if VMMDev was found. +; Both init routines must refuse loading the driver if the +; device cannot be located. +GLOBALNAME g_fFoundAdapter + db 0 +;; @} + + +%ifdef DEBUG_READ +;; 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 +%endif ; DEBUG_READ + + +; +; 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 + +;; Message string that's written on failure. +g_achLoadFailureMsg1: + db 0dh,0ah,'VBoxGuest: load failure no. ' +g_cchLoadFailureMsg1 EQU $ - g_achLoadFailureMsg1 +g_achLoadFailureMsg2: + db '!',0dh,0ah +g_cchLoadFailureMsg2 EQU $ - g_achLoadFailureMsg2 + + +; +; 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 VGDrvOS2Entrypoint + 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 vgdrvOS2EP_NotGenIOCtl + + + ; + ; Generic I/O Control Request. + ; +vgdrvOS2EP_GenIOCtl: + + ; Fast IOCtl? + cmp byte [es:bx + PKTIOCTL.cat], VBGL_IOCTL_CATEGORY_FAST + jne vgdrvOS2EP_GenIOCtl_Other + + ; + ; Fast IOCtl. + ; DECLASM(int) vgdrvOS2IOCtlFast(uint16_t sfn, uint8_t iFunction, uint16_t *pcbParm) + ; +vgdrvOS2EP_GenIOCtl_Fast: + mov ax, [es:bx + PKTIOCTL.pData + 2] ; LDT selector to flat address. + shr ax, 3 + shl eax, 16 + mov ax, [es:bx + PKTIOCTL.pData] + push eax ; 08h - pointer to the rc buffer. + + ; function. + movzx edx, byte [es:bx + PKTIOCTL.fun] + push edx ; 04h + + ; system file number. + movzx eax, word [es:bx + PKTIOCTL.sfn] + push eax ; 00h + + JMP16TO32 vgdrvOS2EP_GenIOCtl_Fast_32 +segment TEXT32 +GLOBALNAME vgdrvOS2EP_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(vgdrvOS2IOCtlFast) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + JMP32TO16 vgdrvOS2EP_GenIOCtl_Fast_32 +segment CODE16 +GLOBALNAME vgdrvOS2EP_GenIOCtl_Fast_16 + + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near vgdrvOS2EP_GeneralFailure + + ; setup output stuff. + mov edx, esp + mov eax, [ss:edx + 0ch] ; 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 + + ; + ; Other IOCtl (slow) + ; +vgdrvOS2EP_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 vgdrvOS2EP_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 vgdrvOS2EP_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 + + JMP16TO32 vgdrvOS2EP_GenIOCtl_Other_32 +segment TEXT32 +GLOBALNAME vgdrvOS2EP_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(vgdrvOS2IOCtl) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + JMP32TO16 vgdrvOS2EP_GenIOCtl_Other_16 +segment CODE16 +GLOBALNAME vgdrvOS2EP_GenIOCtl_Other_16 + + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near vgdrvOS2EP_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. + ; +vgdrvOS2EP_NotGenIOCtl: + cmp byte [es:bx + PKTHDR.cmd], 0dh ; Open + je vgdrvOS2EP_Open + cmp byte [es:bx + PKTHDR.cmd], 0eh ; Close + je vgdrvOS2EP_Close + cmp byte [es:bx + PKTHDR.cmd], 00h ; Init + je vgdrvOS2EP_Init +%ifdef DEBUG_READ + cmp byte [es:bx + PKTHDR.cmd], 04h ; Read + je near vgdrvOS2EP_Read +%endif + jmp near vgdrvOS2EP_NotSupported + + + ; + ; Open Request. w/ ring-0 init. + ; +vgdrvOS2EP_Open: + cmp byte [NAME(g_fInitialized)], 1 + jne vgdrvOS2EP_OpenOther + + ; First argument, the system file number. + movzx eax, word [es:bx + PKTOPEN.sfn] + push eax + + JMP16TO32 vgdrvOS2EP_Open_32 +segment TEXT32 +GLOBALNAME vgdrvOS2EP_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(vgdrvOS2Open) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + JMP32TO16 vgdrvOS2EP_Open_16 +segment CODE16 +GLOBALNAME vgdrvOS2EP_Open_16 + + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near vgdrvOS2EP_GeneralFailure + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + jmp near vgdrvOS2EP_Done + + ; Initializing or failed init? +vgdrvOS2EP_OpenOther: + cmp byte [NAME(g_fInitialized)], 0 + jne vgdrvOS2EP_OpenFailed + + mov byte [NAME(g_fInitialized)], -1 + call NAME(vgdrvRing0Init) + cmp byte [NAME(g_fInitialized)], 1 + je vgdrvOS2EP_Open + +vgdrvOS2EP_OpenFailed: + mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. + jmp near vgdrvOS2EP_Done + + + ; + ; Close Request. + ; +vgdrvOS2EP_Close: + ; First argument, the system file number. + movzx eax, word [es:bx + PKTOPEN.sfn] + push eax + + JMP16TO32 vgdrvOS2EP_Close_32 +segment TEXT32 +GLOBALNAME vgdrvOS2EP_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(vgdrvOS2Close) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + JMP32TO16 vgdrvOS2EP_Close_16 +segment CODE16 +GLOBALNAME vgdrvOS2EP_Close_16 + + les bx, [bp - 4] ; Reload the packet pointer. + or eax, eax + jnz near vgdrvOS2EP_GeneralFailure + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. + jmp near vgdrvOS2EP_Done + + + ; + ; Init Request. + ; Find the VMMDev adapter so we can unload the driver (and avoid trouble) if not found. + ; +vgdrvOS2EP_Init: + call NAME(vgdrvFindAdapter) + test ax, ax + jz .ok + mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. + call NAME(vgdrvOS2InitFlushText) + jmp .next +.ok: + mov word [es:bx + PKTHDR.status], 00100h ; done, ok. +.next: + 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 vgdrvOS2EP_Done + + +%ifdef DEBUG_READ + ; + ; Read Request. + ; Return log data. + ; +vgdrvOS2EP_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 vgdrvOS2EP_Done + +.log_phystovirt_failed: + les bx, [bp - 4] ; Reload the packet pointer. + jmp vgdrvOS2EP_GeneralFailure +%endif ; DEBUG_READ + + + ; + ; Return 'unknown command' error. + ; +vgdrvOS2EP_NotSupported: + mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. + jmp vgdrvOS2EP_Done + + ; + ; Return 'general failure' error. + ; +vgdrvOS2EP_GeneralFailure: + mov word [es:bx + PKTHDR.status], 0810ch ; error, done, general failure. + jmp vgdrvOS2EP_Done + + ; + ; Non-optimized return path. + ; +vgdrvOS2EP_Done: + mov sp, bp + pop ebp + retf +ENDPROC VGDrvOS2Entrypoint + + +;; +; 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 vgdrvOS2InitEntrypoint + ; The only request we're servicing is the 'init' one. + cmp word [es:bx + PKTHDR.cmd], 0 + je near NAME(vgdrvOS2InitEntrypointServiceInitReq) + + ; Ok, it's not the init request, just fail it. + mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. + retf + + +;; +; The OS/2 IDC entry point. +; +; This is only used to setup connection, the returned structure +; will provide the entry points that we'll be using. +; +; @cproto void far __cdecl VGDrvOS2IDC(VBOXGUESTOS2IDCCONNECT far *fpConnectInfo); +; +; @param fpConnectInfo [bp + 8] Pointer to an VBOXGUESTOS2IDCCONNECT structure. +; +GLOBALNAME VGDrvOS2IDC + push ebp ; bp - 0h + mov ebp, esp + ; save everything we might touch. + push es ; bp - 2h + push ds ; bp - 4h + push eax ; bp - 8h + push ebx ; bp - 0ch + push ecx ; bp - 10h + push edx ; bp - 14h + and sp, 0fffch + + JMP16TO32 VGDrvOS2IDC_32 +segment TEXT32 +GLOBALNAME VGDrvOS2IDC_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(vgdrvOS2IDCConnect) + + ; + ; Load the return buffer address into ds:ebx and setup the buffer. + ; (eax == u32Session) + ; + mov cx, [ebp + 08h + 2] + mov ds, cx + movzx ebx, word [ebp + 08h] + + mov dword [ebx + VBGLOS2ATTACHDD.u32Version ], VBGL_IOC_VERSION + mov dword [ebx + VBGLOS2ATTACHDD.u32Session ], eax + mov dword [ebx + VBGLOS2ATTACHDD.pfnServiceEP ], NAME(VGDrvOS2IDCService) + mov word [ebx + VBGLOS2ATTACHDD.fpfnServiceEP ], NAME(VGDrvOS2IDCService16) wrt CODE16 + mov word [ebx + VBGLOS2ATTACHDD.fpfnServiceEP + 2], CODE16 + mov word [ebx + VBGLOS2ATTACHDD.fpfnServiceAsmEP ], NAME(VGDrvOS2IDCService16Asm) wrt CODE16 + mov word [ebx + VBGLOS2ATTACHDD.fpfnServiceAsmEP+2],CODE16 + + mov ax, DATA32 wrt FLAT + mov ds, ax + + ; switch back the stack. + call KernThunkStackTo16 + + JMP32TO16 VGDrvOS2IDC_16 +segment CODE16 +GLOBALNAME VGDrvOS2IDC_16 + + ; restore. + lea sp, [bp - 14h] + pop edx + pop ecx + pop ebx + pop eax + pop ds + pop es + pop ebp + retf +ENDPROC VGDrvOS2IDC + + +;; +; The 16-bit IDC entry point, cdecl. +; +; All this does is thunking the request into something that fits +; the 32-bit IDC service routine. +; +; +; @returns VBox status code. +; @param u32Session bp + 8h - The above session handle. +; @param iFunction bp + 0ch - The requested function. +; @param fpReqHdr bp + 0eh - The input/output data buffer. The caller ensures that this +; cannot be swapped out, or that it's acceptable to take a +; page in fault in the current context. If the request doesn't +; take input or produces output, passing NULL is okay. +; @param cbReq bp + 12h - The size of the data buffer. +; +; @cproto long far __cdecl VGDrvOS2IDCService16(uint32_t u32Session, uint16_t iFunction, void far *fpReqHdr, uint16_t cbReq); +; +GLOBALNAME VGDrvOS2IDCService16 + push ebp ; bp - 0h + mov ebp, esp + push es ; bp - 2h + push ds ; bp - 4h + push ecx ; bp - 8h + push edx ; bp - 0ch + push esi ; bp - 10h + and sp, 0fffch ; align the stack. + + ; locals + push dword 0 ; esp + 18h (dd): cbDataReturned + + ; load our ds (for g_fpfnDevHlp). + mov ax, DATA16 + mov ds, ax + + ; + ; Create the call frame before switching. + ; + movzx ecx, word [bp + 12h] + push ecx ; esp + 10h: cbData + + ; thunk data argument if present. + mov ax, [bp + 0eh + 2] ; selector + cmp ax, 3 ; <= 3 -> nil selector... + jbe .no_data + movzx esi, word [bp + 0eh] ; offset + mov dl, DevHlp_VirtToLin + call far [NAME(g_fpfnDevHlp)] + jc near VGDrvOS2IDCService16_InvalidPointer + jmp .finish_data +.no_data: + xor eax, eax +.finish_data: + push eax ; esp + 08h: pvData + movzx edx, word [bp + 0ch] + push edx ; esp + 04h: iFunction + mov ecx, [bp + 08h] + push ecx ; esp + 00h: u32Session + + JMP16TO32 VGDrvOS2IDCService16_32 +segment TEXT32 +GLOBALNAME VGDrvOS2IDCService16_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(VGDrvOS2IDCService) + + ; switch back the stack. + push eax + call KernThunkStackTo16 + pop eax + + JMP32TO16 VGDrvOS2IDCService16_16 +segment CODE16 +GLOBALNAME VGDrvOS2IDCService16_16 + +VGDrvOS2IDCService16_Done: + lea sp, [bp - 10h] + pop esi + pop edx + pop ecx + pop ds + pop es + pop ebp + retf + +VGDrvOS2IDCService16_InvalidPointer: + mov ax, VERR_INVALID_POINTER + jmp VGDrvOS2IDCService16_Done +ENDPROC VGDrvOS2IDCService16 + + +;; +; The 16-bit IDC entry point, register based. +; +; This is just a wrapper around VGDrvOS2IDCService16 to simplify +; calls from 16-bit assembly code. +; +; @returns ax: VBox status code; cx: The amount of data returned. +; +; @param u32Session eax - The above session handle. +; @param iFunction dl - The requested function. +; @param pvData es:bx - The input/output data buffer. +; @param cbData cx - The size of the data buffer. +; +GLOBALNAME VGDrvOS2IDCService16Asm + push ebp ; bp - 0h + mov ebp, esp + push edx ; bp - 4h + + push cx ; cbData + push es + xor dh, dh + push dx + push eax + call NAME(VGDrvOS2IDCService16) + + mov cx, [es:bx + VBGLREQHDR.cbOut] + + mov edx, [bp - 4] + mov esp, ebp + pop ebp + retf +ENDPROC VGDrvOS2IDCService16Asm + + + +;; +; The 16-bit interrupt service routine. +; +; OS/2 saves all registers according to the docs, although it doesn't say whether +; this includes the 32-bit parts. Since it doesn't cost much to be careful, save +; everything. +; +; @returns CF=0 if it's our interrupt, CF=1 it it isn't. +; +; +GLOBALNAME vgdrvOS2ISR16 + push ebp + mov ebp, esp + pushf ; bp - 02h + cli + push eax ; bp - 06h + push edx ; bp - 0ah + push ebx ; bp - 0eh + push ds ; bp - 10h + push es ; bp - 12h + push ecx ; bp - 16h + push esi ; bp - 1ah + push edi ; bp - 1eh + + and sp, 0fff0h ; align the stack (16-bytes make GCC extremely happy). + + JMP16TO32 vgdrvOS2ISR16_32 +segment TEXT32 +GLOBALNAME vgdrvOS2ISR16_32 + + mov ax, DATA32 wrt FLAT + mov ds, ax + mov es, ax + + call KernThunkStackTo32 + + call NAME(vgdrvOS2ISR) + mov ebx, eax + + call KernThunkStackTo16 + + JMP32TO16 vgdrvOS2ISR16_16 +segment CODE16 +GLOBALNAME vgdrvOS2ISR16_16 + + lea sp, [bp - 1eh] + pop edi + pop esi + pop ecx + pop es + pop ds + test bl, 0ffh + jnz .our + pop ebx + pop edx + pop eax + popf + pop ebp + stc + retf + + ; + ; Do EIO. + ; +.our: + mov al, [NAME(g_bInterruptLine)] + mov dl, DevHlp_EOI + call far [NAME(g_fpfnDevHlp)] + + pop ebx + pop edx + pop eax + popf + pop ebp + clc + retf +ENDPROC vgdrvOS2ISR16 + + + + + + +; +; The 32-bit text segment. +; +segment TEXT32 +;; +; 32-bit worker for registering the ISR. +; +; @returns 0 on success, some non-zero OS/2 error code on failure. +; @param bIrq [ebp + 8] The IRQ number. (uint8_t) +; +GLOBALNAME vgdrvOS2DevHlpSetIRQ + push ebp + mov ebp, esp + push ebx + push ds + + call KernThunkStackTo16 + + movzx ebx, byte [ebp + 8] ; load bIrq into BX. + + JMP32TO16 vgdrvOS2DevHlpSetIRQ_16 +segment CODE16 +GLOBALNAME vgdrvOS2DevHlpSetIRQ_16 + + mov ax, DATA16 ; for g_fpfnDevHlp. + mov ds, ax + mov ax, NAME(vgdrvOS2ISR16) ; The devhlp assume it's relative to DS. + mov dh, 1 ; 1 = shared + mov dl, DevHlp_SetIRQ + call far [NAME(g_fpfnDevHlp)] + jnc .ok + movzx eax, ax + or eax, eax + jnz .go_back + or eax, 6 + jmp .go_back +.ok: + xor eax, eax + +.go_back: + JMP16TO32 vgdrvOS2DevHlpSetIRQ_32 +segment TEXT32 +GLOBALNAME vgdrvOS2DevHlpSetIRQ_32 + + pop ds ; KernThunkStackTo32 ASSUMES flat DS and ES. + + mov ebx, eax + call KernThunkStackTo32 + mov eax, ebx + + pop ebx + pop ebp + ret +ENDPROC vgdrvOS2DevHlpSetIRQ + + + + +; +; The 16-bit init code. +; +segment CODE16_INIT +GLOBALNAME g_InitCodeStart + +;; The device name for DosOpen. +g_szDeviceName: + db '\DEV\vboxgst$', 0 + +; icsdebug can't see where stuff starts otherwise. (kDevTest) +int3 +int3 +int3 +int3 +int3 +int3 + +;; +; The Ring-3 init code. +; +BEGINPROC vgdrvOS2InitEntrypointServiceInitReq + 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 + + ; check that we found the VMMDev. + test byte [NAME(g_fFoundAdapter)], 1 + jz near .done_err + + ; + ; 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(vgdrvOS2InitFlushText) + 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 vgdrvOS2InitEntrypointServiceInitReq + + +;; +; The Ring-0 init code. +; +BEGINPROC vgdrvRing0Init + 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 vgdrvRing0Init_done ; eax is non-zero on failure (can't happen) + push eax ; 00h - pszArgs (for vgdrvOS2Init). + + ; + ; Do 16-bit init? + ; + + + ; + ; Do 32-bit init + ; + JMP16TO32 vgdrvRing0Init_32 +segment TEXT32 +GLOBALNAME vgdrvRing0Init_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(vgdrvOS2Init) + + ; switch back the stack and reload ds. + push eax + call KernThunkStackTo16 + pop eax + + mov dx, seg NAME(g_fInitialized) + mov ds, dx + + JMP32TO16 vgdrvRing0Init_16 +segment CODE16_INIT +GLOBALNAME vgdrvRing0Init_16 + + ; check the result and set g_fInitialized on success. + or eax, eax + jnz vgdrvRing0Init_done + mov byte [NAME(g_fInitialized)], 1 + +vgdrvRing0Init_done: + mov sp, bp + pop ebp + pop esi + pop es + ret +ENDPROC vgdrvRing0Init + + +;; +; Flush any text in the text buffer. +; +BEGINPROC vgdrvOS2InitFlushText + 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 vgdrvOS2InitFlushText + + +;; The device name for DosOpen. +g_szOemHlpDevName: + db '\DEV\OEMHLP$', 0 + + +;; +; Talks to OEMHLP$ about finding the VMMDev PCI adapter. +; +; On success g_fFoundAdapter is set to 1, and g_cbMMIO, +; g_PhysMMIOBase and g_IOPortBase are initialized with +; the PCI data. +; +; @returns 0 on success, non-zero on failure. (eax) +; +; @remark ASSUMES DS:DATA16. +; @uses nothing. +; +BEGINPROC vgdrvFindAdapter + push ebx + push ecx + push edx + push esi + push edi + push ebp + mov ebp, esp + push -1 ; bp - 2: hfOpen +%define hfOpen bp - 2 + push 0 ; bp - 4: usAction +%define usAction bp - 4 + sub sp, 20h ; bp - 44: 32 byte parameter buffer. +%define abParm bp - 24h + sub sp, 40h ; bp - c4: 32 byte data buffer. +%define abData bp - 44h + +;; VBox stuff +%define VBOX_PCI_VENDORID 080eeh +%define VMMDEV_DEVICEID 0cafeh + +;; OEMHLP$ stuff. +%define IOCTL_OEMHLP 80h +%define OEMHLP_PCI 0bh +%define PCI_FIND_DEVICE 1 +%define PCI_READ_CONFIG 3 + +;; PCI stuff +%define PCI_INTERRUPT_LINE 03ch ;;< 8-bit RW - Interrupt line. +%define PCI_BASE_ADDRESS_0 010h ;;< 32-bit RW */ +%define PCI_BASE_ADDRESS_1 014h ;;< 32-bit RW */ + + +%macro CallIOCtl 2 + ; APIRET _Pascal DosDevIOCtl2(PVOID pData, USHORT cbData, PVOID pParm, + ; USHORT cbParm, USHORT usFun, USHORT usCategory, + ; HFILE hDev); + push ss ; pData + lea dx, [abData] + push dx + push %2 ; cbData + + push ss ; pParm + lea dx, [abParm] + push dx + push %1 ; cbParm + push OEMHLP_PCI ; usFun + push IOCTL_OEMHLP ; usCategory + + mov ax, [hfOpen] ; hDev + push ax + call far DOS16DEVIOCTL2 + + ; check for error. + test ax, ax + jnz near .done_err_close + cmp [abData + 0], byte 0 + jne near .done_err_close +%endmacro + + + ; + ; Open the OEMHLP$ driver. + ; + + ; APIRET _Pascal DosOpen(PSZ pszFname, PHFILE phfOpen, PUSHORT pusAction, + ; ULONG ulFSize, USHORT usAttr, USHORT fsOpenFlags, + ; USHORT fsOpenMode, ULONG ulReserved); + mov di, '0' + push seg g_szOemHlpDevName ; pszFname + push g_szOemHlpDevName + push ss ; phfOpen + lea dx, [hfOpen] + push dx + push ss ; pusAction + lea dx, [usAction] + 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 + or ax, ax + jnz near .done + + + ; + ; Send a PCI_FIND_DEVICE request. + ; + + ; Initialize the parameter packet. + mov [abParm + 0], byte PCI_FIND_DEVICE ; 0 - db - SubFunction Number + mov [abParm + 1], word VMMDEV_DEVICEID ; 1 - dw - Device ID + mov [abParm + 3], word VBOX_PCI_VENDORID ; 3 - dw - Vendor ID + mov [abParm + 5], byte 0 ; 5 - db - (Device) Index + + ; Zero padd the data packet. + mov [abData + 0], dword 0 + + mov di, '1' + CallIOCtl 6, 3 + + mov al, [abData + 1] ; 1 - db - Bus Number. + mov [NAME(g_bPciBusNo)], al + mov al, [abData + 2] ; 2 - db - DevFunc Number. + mov [NAME(g_bPciDevFunNo)], al + + ; + ; Read the interrupt register (byte). + ; + mov di, '2' + mov ax, PCI_INTERRUPT_LINE | 0100h + call .NestedReadReg + mov [NAME(g_bInterruptLine)], al + + ; + ; Read the first base address (dword), this shall must be in I/O space. + ; + mov di, '3' + mov ax, PCI_BASE_ADDRESS_0 | 0400h + call .NestedReadReg + mov di, '4' + test al, 1h ; Test that it's an I/O space address. + jz .done_err_close + mov di, '5' + test eax, 0ffff0002h ; These shall all be 0 according to the specs. + jnz .done_err_close + and ax, 0fffeh + mov [NAME(g_IOPortBase)], ax + + ; + ; Read the second base address (dword), this shall be in memory space if present. + ; + mov di, '6' + mov ax, PCI_BASE_ADDRESS_1 | 0400h + call .NestedReadReg + mov di, '7' + test al, 1h ; Test that it's a memory space address. + jnz .done_err_close + and eax, 0fffffff0h + mov [NAME(g_PhysMMIOBase)], eax + + ;or eax, eax + ;jz .done_success ; No memory region. + ;; @todo If there is a simple way of determining the size do that, if + ; not we can easily handle it the code that does the actual mapping. + + + ; + ; Ok, we're good! + ; +.done_success: + or [NAME(g_fFoundAdapter)], byte 1 + jmp .done_close + + ; + ; Close the OEMHLP$ driver. + ; +.done_err_close: + or ax, 80h +.done_close: + ; APIRET APIENTRY DosClose(HFILE hf); + push ax ; Save result + mov cx, [hfOpen] + push cx + call far DOS16CLOSE + or ax, ax + jnz .bitch ; This can't happen (I hope). + pop ax + or ax, ax + jnz .bitch + + ; + ; Return to vgdrvOS2EP_Init. + ; +.done: + mov esp, ebp + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + ret + + + ; + ; Puts the reason for failure in message buffer. + ; The caller will flush this. + ; +.bitch: + push es + + mov ax, ds + mov es, ax + mov ax, di ; save the reason. + mov di, NAME(g_szInitText) + add di, [NAME(g_cchInitText)] + + mov si, g_achLoadFailureMsg1 + mov cx, g_cchLoadFailureMsg1 + rep movsb + + stosb + + mov si, g_achLoadFailureMsg2 + mov cx, g_cchLoadFailureMsg2 + rep movsb + + mov [di], byte 0 + sub di, NAME(g_szInitText) + mov [NAME(g_cchInitText)], di + + pop es + jmp .done + + + ; + ; Nested function which reads a PCI config register. + ; (This operates on the vgdrvFindAdapter stack frame.) + ; + ; Input: + ; al - register to read + ; ah - register size. + ; + ; Output: + ; eax - the value. + ; + ; Uses: + ; dx + ; +.NestedReadReg: + ; Fill in the request packet. + mov [abParm + 0], byte PCI_READ_CONFIG ; 0 - db - SubFunction Number + mov dl, [NAME(g_bPciBusNo)] + mov [abParm + 1], dl ; 1 - db - Bus Number + mov dl, [NAME(g_bPciDevFunNo)] + mov [abParm + 2], dl ; 2 - db - DevFunc Number + mov [abParm + 3], al ; 3 - db - Configuration Register + mov [abParm + 4], ah ; 4 - db - (Register) Size + + ; Pad the data packet. + mov [abData + 0], dword 0 + mov [abData + 4], dword 0 + + CallIOCtl 5, 5 + + mov eax, [abData + 1] ; 1 - dd - Data + + ret + +ENDPROC vgdrvFindAdapter + + + +;; +; This must be present +segment DATA32 +g_pfnDos16Write: + dd DOS16WRITE ; flat + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h new file mode 100644 index 00000000..9c2fc1d8 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h @@ -0,0 +1,415 @@ +/* $Id: VBoxGuestInternal.h $ */ +/** @file + * VBoxGuest - Guest Additions Driver, Internal Header. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h +#define GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> +#include <iprt/list.h> +#include <iprt/semaphore.h> +#include <iprt/spinlock.h> +#include <iprt/timer.h> +#include <VBox/VMMDev.h> +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> + +/** @def VBOXGUEST_USE_DEFERRED_WAKE_UP + * Defer wake-up of waiting thread when defined. */ +#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING) +# define VBOXGUEST_USE_DEFERRED_WAKE_UP +#endif + +/** @def VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT + * The mouse notification callback can cause preemption and must not be invoked + * while holding a high-level spinlock. + */ +#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING) +# define VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT +#endif + +/** Pointer to the VBoxGuest per session data. */ +typedef struct VBOXGUESTSESSION *PVBOXGUESTSESSION; + +/** Pointer to a wait-for-event entry. */ +typedef struct VBOXGUESTWAIT *PVBOXGUESTWAIT; + +/** + * VBox guest wait for event entry. + * + * Each waiting thread allocates one of these items and adds + * it to the wait list before going to sleep on the event sem. + */ +typedef struct VBOXGUESTWAIT +{ + /** The list node. */ + RTLISTNODE ListNode; + /** The events we are waiting on. */ + uint32_t fReqEvents; + /** The events we received. */ + uint32_t volatile fResEvents; +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + /** Set by VGDrvCommonWaitDoWakeUps before leaving the spinlock to call + * RTSemEventMultiSignal. */ + bool volatile fPendingWakeUp; + /** Set by the requestor thread if it got the spinlock before the + * signaller. Deals with the race in VGDrvCommonWaitDoWakeUps. */ + bool volatile fFreeMe; +#endif + /** The event semaphore. */ + RTSEMEVENTMULTI Event; + /** The session that's waiting. */ + PVBOXGUESTSESSION pSession; +#ifdef VBOX_WITH_HGCM + /** The HGCM request we're waiting for to complete. */ + VMMDevHGCMRequestHeader volatile *pHGCMReq; +#endif +} VBOXGUESTWAIT; + + +/** + * VBox guest memory balloon. + */ +typedef struct VBOXGUESTMEMBALLOON +{ + /** Mutex protecting the members below from concurrent access. */ + RTSEMFASTMUTEX hMtx; + /** The current number of chunks in the balloon. */ + uint32_t cChunks; + /** The maximum number of chunks in the balloon (typically the amount of guest + * memory / chunksize). */ + uint32_t cMaxChunks; + /** This is true if we are using RTR0MemObjAllocPhysNC() / RTR0MemObjGetPagePhysAddr() + * and false otherwise. */ + bool fUseKernelAPI; + /** The current owner of the balloon. + * This is automatically assigned to the first session using the ballooning + * API and first released when the session closes. */ + PVBOXGUESTSESSION pOwner; + /** The pointer to the array of memory objects holding the chunks of the + * balloon. This array is cMaxChunks in size when present. */ + PRTR0MEMOBJ paMemObj; +} VBOXGUESTMEMBALLOON; +/** Pointer to a memory balloon. */ +typedef VBOXGUESTMEMBALLOON *PVBOXGUESTMEMBALLOON; + + +/** + * Per bit usage tracker for a uint32_t mask. + * + * Used for optimal handling of guest properties, mouse status and event filter. + */ +typedef struct VBOXGUESTBITUSAGETRACER +{ + /** Per bit usage counters. */ + uint32_t acPerBitUsage[32]; + /** The current mask according to acPerBitUsage. */ + uint32_t fMask; +} VBOXGUESTBITUSAGETRACER; +/** Pointer to a per bit usage tracker. */ +typedef VBOXGUESTBITUSAGETRACER *PVBOXGUESTBITUSAGETRACER; +/** Pointer to a const per bit usage tracker. */ +typedef VBOXGUESTBITUSAGETRACER const *PCVBOXGUESTBITUSAGETRACER; + + +/** + * VBox guest device (data) extension. + */ +typedef struct VBOXGUESTDEVEXT +{ + /** VBOXGUESTDEVEXT_INIT_STATE_XXX. */ + uint32_t uInitState; + /** The base of the adapter I/O ports. */ + RTIOPORT IOPortBase; + /** Pointer to the mapping of the VMMDev adapter memory. */ + VMMDevMemory volatile *pVMMDevMemory; + /** The memory object reserving space for the guest mappings. */ + RTR0MEMOBJ hGuestMappings; + /** Spinlock protecting the signaling and resetting of the wait-for-event + * semaphores as well as the event acking in the ISR. */ + RTSPINLOCK EventSpinlock; + /** Host feature flags (VMMDEV_HVF_XXX). */ + uint32_t fHostFeatures; + /** Preallocated VMMDevEvents for the IRQ handler. */ + VMMDevEvents *pIrqAckEvents; + /** The physical address of pIrqAckEvents. */ + RTCCPHYS PhysIrqAckEvents; + /** Wait-for-event list for threads waiting for multiple events + * (VBOXGUESTWAIT). */ + RTLISTANCHOR WaitList; +#ifdef VBOX_WITH_HGCM + /** Wait-for-event list for threads waiting on HGCM async completion + * (VBOXGUESTWAIT). + * + * The entire list is evaluated upon the arrival of an HGCM event, unlike + * the other lists which are only evaluated till the first thread has + * been woken up. */ + RTLISTANCHOR HGCMWaitList; +#endif +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + /** List of wait-for-event entries that needs waking up + * (VBOXGUESTWAIT). */ + RTLISTANCHOR WakeUpList; +#endif + /** List of wait-for-event entries that has been woken up + * (VBOXGUESTWAIT). */ + RTLISTANCHOR WokenUpList; + /** List of free wait-for-event entries (VBOXGUESTWAIT). */ + RTLISTANCHOR FreeList; + /** Mask of pending events. */ + uint32_t volatile f32PendingEvents; + /** Current VMMDEV_EVENT_MOUSE_POSITION_CHANGED sequence number. + * Used to implement polling. */ + uint32_t volatile u32MousePosChangedSeq; + + /** Spinlock various items in the VBOXGUESTSESSION. */ + RTSPINLOCK SessionSpinlock; + /** List of guest sessions (VBOXGUESTSESSION). We currently traverse this + * but do not search it, so a list data type should be fine. Use under the + * #SessionSpinlock lock. */ + RTLISTANCHOR SessionList; + /** Number of session. */ + uint32_t cSessions; + /** Flag indicating whether logging to the release log + * is enabled. */ + bool fLoggingEnabled; + /** Memory balloon information for RTR0MemObjAllocPhysNC(). */ + VBOXGUESTMEMBALLOON MemBalloon; + /** Mouse notification callback function. */ + PFNVBOXGUESTMOUSENOTIFY pfnMouseNotifyCallback; + /** The callback argument for the mouse ntofication callback. */ + void *pvMouseNotifyCallbackArg; + + /** @name Host Event Filtering + * @{ */ + /** Events we won't permit anyone to filter out. */ + uint32_t fFixedEvents; + /** Usage counters for the host events. (Fixed events are not included.) */ + VBOXGUESTBITUSAGETRACER EventFilterTracker; + /** The event filter last reported to the host (UINT32_MAX on failure). */ + uint32_t fEventFilterHost; + /** @} */ + + /** @name Mouse Status + * @{ */ + /** Usage counters for the mouse statuses (VMMDEV_MOUSE_XXX). */ + VBOXGUESTBITUSAGETRACER MouseStatusTracker; + /** The mouse status last reported to the host (UINT32_MAX on failure). */ + uint32_t fMouseStatusHost; + /** @} */ + + /** @name Guest Capabilities + * @{ */ + /** Guest capabilities which have been set to "acquire" mode. This means + * that only one session can use them at a time, and that they will be + * automatically cleaned up if that session exits without doing so. + * + * Protected by VBOXGUESTDEVEXT::SessionSpinlock, but is unfortunately read + * without holding the lock in a couple of places. */ + uint32_t volatile fAcquireModeGuestCaps; + /** Guest capabilities which have been set to "set" mode. This just means + * that they have been blocked from ever being set to "acquire" mode. */ + uint32_t fSetModeGuestCaps; + /** Mask of all capabilities which are currently acquired by some session + * and as such reported to the host. */ + uint32_t fAcquiredGuestCaps; + /** Usage counters for guest capabilities in "set" mode. Indexed by + * capability bit number, one count per session using a capability. */ + VBOXGUESTBITUSAGETRACER SetGuestCapsTracker; + /** The guest capabilities last reported to the host (UINT32_MAX on failure). */ + uint32_t fGuestCapsHost; + /** @} */ + + /** Heartbeat timer which fires with interval + * cNsHearbeatInterval and its handler sends + * VMMDevReq_GuestHeartbeat to VMMDev. */ + PRTTIMER pHeartbeatTimer; + /** Heartbeat timer interval in nanoseconds. */ + uint64_t cNsHeartbeatInterval; + /** Preallocated VMMDevReq_GuestHeartbeat request. */ + VMMDevRequestHeader *pReqGuestHeartbeat; +} VBOXGUESTDEVEXT; +/** Pointer to the VBoxGuest driver data. */ +typedef VBOXGUESTDEVEXT *PVBOXGUESTDEVEXT; + +/** @name VBOXGUESTDEVEXT_INIT_STATE_XXX - magic values for validating init + * state of the device extension structur. + * @{ */ +#define VBOXGUESTDEVEXT_INIT_STATE_FUNDAMENT UINT32_C(0x0badcafe) +#define VBOXGUESTDEVEXT_INIT_STATE_RESOURCES UINT32_C(0xcafebabe) +#define VBOXGUESTDEVEXT_INIT_STATE_DELETED UINT32_C(0xdeadd0d0) +/** @} */ + +/** + * The VBoxGuest per session data. + */ +typedef struct VBOXGUESTSESSION +{ + /** The list node. */ + RTLISTNODE ListNode; +#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS) + /** Pointer to the next session with the same hash. */ + PVBOXGUESTSESSION pNextHash; +#endif +#if defined(RT_OS_OS2) + /** The system file number of this session. */ + uint16_t sfn; + uint16_t Alignment; /**< Alignment */ +#endif + /** The requestor information to pass to the host for this session. + * @sa VMMDevRequestHeader::fRequestor */ + uint32_t fRequestor; + /** The process (id) of the session. + * This is NIL if it's a kernel session. */ + RTPROCESS Process; + /** Which process this session is associated with. + * This is NIL if it's a kernel session. */ + RTR0PROCESS R0Process; + /** Pointer to the device extension. */ + PVBOXGUESTDEVEXT pDevExt; + +#ifdef VBOX_WITH_HGCM + /** Array containing HGCM client IDs associated with this session. + * This will be automatically disconnected when the session is closed. */ + uint32_t volatile aHGCMClientIds[64]; +#endif + /** The last consumed VMMDEV_EVENT_MOUSE_POSITION_CHANGED sequence number. + * Used to implement polling. */ + uint32_t volatile u32MousePosChangedSeq; + /** Host events requested by the session. + * An event type requested in any guest session will be added to the host + * filter. Protected by VBOXGUESTDEVEXT::SessionSpinlock. */ + uint32_t fEventFilter; + /** Guest capabilities held in "acquired" by this session. + * Protected by VBOXGUESTDEVEXT::SessionSpinlock, but is unfortunately read + * without holding the lock in a couple of places. */ + uint32_t volatile fAcquiredGuestCaps; + /** Guest capabilities in "set" mode for this session. + * These accumulated for sessions via VBOXGUESTDEVEXT::acGuestCapsSet and + * reported to the host. Protected by VBOXGUESTDEVEXT::SessionSpinlock. */ + uint32_t fCapabilities; + /** Mouse features supported. A feature enabled in any guest session will + * be enabled for the host. + * @note We invert the VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR feature in this + * bitmap. The logic of this is that the real feature is when the host + * cursor is not needed, and we tell the host it is not needed if any + * session explicitly fails to assert it. Storing it inverted simplifies + * the checks. + * Use under the VBOXGUESTDEVEXT#SessionSpinlock lock. */ + uint32_t fMouseStatus; +#ifdef RT_OS_DARWIN + /** Pointer to the associated org_virtualbox_VBoxGuestClient object. */ + void *pvVBoxGuestClient; + /** Whether this session has been opened or not. */ + bool fOpened; +#endif + /** Whether a CANCEL_ALL_WAITEVENTS is pending. This happens when + * CANCEL_ALL_WAITEVENTS is called, but no call to WAITEVENT is in process + * in the current session. In that case the next call will be interrupted + * at once. */ + bool volatile fPendingCancelWaitEvents; + /** Does this session belong to a root process or a user one? */ + bool fUserSession; +} VBOXGUESTSESSION; + +RT_C_DECLS_BEGIN + +int VGDrvCommonInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, void *pvMMIOBase, uint32_t cbMMIO, + VBOXOSTYPE enmOSType, uint32_t fEvents); +void VGDrvCommonDeleteDevExt(PVBOXGUESTDEVEXT pDevExt); + +int VGDrvCommonInitLoggers(void); +void VGDrvCommonDestroyLoggers(void); +int VGDrvCommonInitDevExtFundament(PVBOXGUESTDEVEXT pDevExt); +void VGDrvCommonDeleteDevExtFundament(PVBOXGUESTDEVEXT pDevExt); +int VGDrvCommonInitDevExtResources(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, + void *pvMMIOBase, uint32_t cbMMIO, VBOXOSTYPE enmOSType, uint32_t fFixedEvents); +void VGDrvCommonDeleteDevExtResources(PVBOXGUESTDEVEXT pDevExt); +int VGDrvCommonReinitDevExtAfterHibernation(PVBOXGUESTDEVEXT pDevExt, VBOXOSTYPE enmOSType); + +bool VBDrvCommonIsOptionValueTrue(const char *pszValue); +void VGDrvCommonProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue); +void VGDrvCommonProcessOptionsFromHost(PVBOXGUESTDEVEXT pDevExt); +bool VGDrvCommonIsOurIRQ(PVBOXGUESTDEVEXT pDevExt); +bool VGDrvCommonISR(PVBOXGUESTDEVEXT pDevExt); + +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP +void VGDrvCommonWaitDoWakeUps(PVBOXGUESTDEVEXT pDevExt); +#endif + +int VGDrvCommonCreateUserSession(PVBOXGUESTDEVEXT pDevExt, uint32_t fRequestor, PVBOXGUESTSESSION *ppSession); +int VGDrvCommonCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession); +void VGDrvCommonCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); + +int VGDrvCommonIoCtlFast(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); +int VGDrvCommonIoCtl(uintptr_t iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + PVBGLREQHDR pReqHdr, size_t cbReq); + +/** + * ISR callback for notifying threads polling for mouse events. + * + * This is called at the end of the ISR, after leaving the event spinlock, if + * VMMDEV_EVENT_MOUSE_POSITION_CHANGED was raised by the host. + * + * @param pDevExt The device extension. + */ +void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt); + +/** + * Hook for handling OS specfic options from the host. + * + * @returns true if handled, false if not. + * @param pDevExt The device extension. + * @param pszName The option name. + * @param pszValue The option value. + */ +bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue); + + +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER +int VGDrvNtIOCtl_DpcLatencyChecker(void); +#endif + +#ifdef VBOXGUEST_MOUSE_NOTIFY_CAN_PREEMPT +int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify); +#endif + +RT_C_DECLS_END + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_VBoxGuestInternal_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist b/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist new file mode 100644 index 00000000..f4384535 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//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>VBoxGuest</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxGuest</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxGuest</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>NSHumanReadableCopyright</key> <string>Copyright © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string> + <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</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>VBoxGuest</key> + <dict> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxGuest</string> + <key>IOClass</key> <string>org_virtualbox_VBoxGuest</string> + <key>IOMatchCategory</key> <string>org_virtualbox_VBoxGuest</string> + <key>IOUserClientClass</key> <string>org_virtualbox_VBoxGuestClient</string> + <key>IOKitDebug</key> <integer>65535</integer> + <key>IOProviderClass</key> <string>IOPCIDevice</string> + <key>IOPCIPrimaryMatch</key> <string>0xcafe80ee</string> + <!-- <key>IONameMatch</key> <string>pci80ee,cafe</string> --> + </dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.iokit.IOPCIFamily</key> <string>2.5</string> <!-- TODO: Figure the version in mac os x 10.4. --> + <key>com.apple.kpi.bsd</key> <string>8.0.0</string> + <key>com.apple.kpi.mach</key> <string>8.0.0</string> + <key>com.apple.kpi.libkern</key> <string>8.0.0</string> + <key>com.apple.kpi.unsupported</key> <string>8.0.0</string> + <key>com.apple.kpi.iokit</key> <string>8.0.0</string> + </dict> + <key>OSBundleLibraries_x86_64</key> + <dict> + <key>com.apple.iokit.IOPCIFamily</key> <string>2.6</string> + <key>com.apple.kpi.bsd</key> <string>10.0.0d4</string> + <key>com.apple.kpi.mach</key> <string>10.0.0d3</string> + <key>com.apple.kpi.libkern</key> <string>10.0.0d3</string> + <key>com.apple.kpi.iokit</key> <string>10.0.0d3</string> + <key>com.apple.kpi.unsupported</key> <string>10.0.0d3</string> + </dict> +</dict> +</plist> + diff --git a/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile new file mode 100644 index 00000000..bb89f918 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile @@ -0,0 +1,202 @@ +# $Id: Makefile $ +## @file +# VirtualBox Guest Additions Module Makefile. +# + +# +# Copyright (C) 2006-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# +KMOD = vboxguest + +CFLAGS += -DRT_OS_FREEBSD -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -Iinclude -I. -Ir0drv -w -DVBGL_VBOXGUEST -DVBOX_WITH_HGCM -DVBOX_WITH_64_BITS_GUESTS + +.if (${MACHINE_ARCH} == "i386") + CFLAGS += -DRT_ARCH_X86 +.elif (${MACHINE_ARCH} == "amd64") + CFLAGS += -DRT_ARCH_AMD64 +.endif + +SRCS = \ + VBoxGuest.c \ + VBoxGuest-freebsd.c \ + VBoxGuestR0LibGenericRequest.c \ + VBoxGuestR0LibHGCMInternal.c \ + VBoxGuestR0LibInit.c \ + VBoxGuestR0LibPhysHeap.c \ + VBoxGuestR0LibVMMDev.c + +# Include needed interface headers so they are created during build +SRCS += \ + device_if.h \ + bus_if.h \ + pci_if.h \ + +.PATH: ${.CURDIR}/alloc +SRCS += \ + heapsimple.c + +.PATH: ${.CURDIR}/common/err +SRCS += \ + RTErrConvertFromErrno.c \ + RTErrConvertToErrno.c \ + errinfo.c + +.PATH: ${.CURDIR}/common/log +SRCS += \ + log.c \ + logellipsis.c \ + logrel.c \ + logrelellipsis.c \ + logcom.c \ + logformat.c \ + RTLogCreateEx.c + +.PATH: ${.CURDIR}/common/misc +SRCS += \ + RTAssertMsg1Weak.c \ + RTAssertMsg2.c \ + RTAssertMsg2Add.c \ + RTAssertMsg2AddWeak.c \ + RTAssertMsg2AddWeakV.c \ + RTAssertMsg2Weak.c \ + RTAssertMsg2WeakV.c \ + assert.c \ + handletable.c \ + handletablectx.c \ + once.c \ + thread.c + +.PATH: ${.CURDIR}/common/string +SRCS += \ + RTStrCat.c \ + RTStrCmp.c \ + RTStrCopy.c \ + RTStrCopyEx.c \ + RTStrCopyP.c \ + RTStrEnd.c \ + RTStrICmpAscii.c \ + RTStrNICmpAscii.c \ + RTStrNCmp.c \ + RTStrNLen.c \ + stringalloc.c \ + strformat.c \ + RTStrFormat.c \ + strformatnum.c \ + strformatrt.c \ + strformattype.c \ + strprintf.c \ + strprintf-ellipsis.c \ + strprintf2.c \ + strprintf2-ellipsis.c \ + strtonum.c \ + memchr.c \ + utf-8.c + +.PATH: ${.CURDIR}/common/rand +SRCS += \ + rand.c \ + randadv.c \ + randparkmiller.c + +.PATH: ${.CURDIR}/common/path +SRCS += \ + RTPathStripFilename.c + +.PATH: ${.CURDIR}/common/checksum +SRCS += \ + crc32.c \ + ipv4.c + +.PATH: ${.CURDIR}/common/table +SRCS += \ + avlpv.c + +.PATH: ${.CURDIR}/common/time +SRCS += \ + time.c + +.PATH: ${.CURDIR}/generic +SRCS += \ + uuid-generic.c \ + RTAssertShouldPanic-generic.c \ + RTLogWriteDebugger-generic.c \ + RTLogWriteStdOut-stub-generic.c \ + RTLogWriteStdErr-stub-generic.c \ + RTRandAdvCreateSystemFaster-generic.c \ + RTRandAdvCreateSystemTruer-generic.c \ + RTSemEventWait-2-ex-generic.c \ + RTSemEventWaitNoResume-2-ex-generic.c \ + RTSemEventMultiWait-2-ex-generic.c \ + RTSemEventMultiWaitNoResume-2-ex-generic.c \ + RTTimerCreate-generic.c \ + rtStrFormatKernelAddress-generic.c \ + timer-generic.c \ + errvars-generic.c \ + mppresent-generic.c + +.PATH: ${.CURDIR}/r0drv +SRCS += \ + alloc-r0drv.c \ + initterm-r0drv.c \ + memobj-r0drv.c \ + powernotification-r0drv.c + +.PATH: ${.CURDIR}/r0drv/freebsd +SRCS += \ + assert-r0drv-freebsd.c \ + alloc-r0drv-freebsd.c \ + initterm-r0drv-freebsd.c \ + memobj-r0drv-freebsd.c \ + memuserkernel-r0drv-freebsd.c \ + mp-r0drv-freebsd.c \ + process-r0drv-freebsd.c \ + semevent-r0drv-freebsd.c \ + semeventmulti-r0drv-freebsd.c \ + semfastmutex-r0drv-freebsd.c \ + semmutex-r0drv-freebsd.c \ + spinlock-r0drv-freebsd.c \ + thread-r0drv-freebsd.c \ + thread2-r0drv-freebsd.c \ + time-r0drv-freebsd.c + +.PATH: ${.CURDIR}/r0drv/generic +SRCS += \ + semspinmutex-r0drv-generic.c \ + mpnotification-r0drv-generic.c \ + RTMpIsCpuWorkPending-r0drv-generic.c + +.PATH: ${.CURDIR}/VBox +SRCS += \ + log-vbox.c \ + logbackdoor.c \ + RTLogWriteVmm-amd64-x86. + +.include <bsd.kmod.mk> + diff --git a/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile.kup b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile.kup diff --git a/src/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest b/src/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest new file mode 100755 index 00000000..3bd7d392 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest @@ -0,0 +1,240 @@ +#!/bin/sh +# $Id: files_vboxguest $ +## @file +# Shared file between Makefile.kmk and export_modules.sh. +# + +# +# Copyright (C) 2007-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +FILES_VBOXGUEST_NOBIN=" \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \ + ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.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/errcore.h=>include/iprt/errcore.h \ + ${PATH_ROOT}/include/iprt/errno.h=>include/iprt/errno.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/handletable.h=>include/iprt/handletable.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \ + ${PATH_ROOT}/include/iprt/list.h=>include/iprt/list.h \ + ${PATH_ROOT}/include/iprt/lockvalidator.h=>include/iprt/lockvalidator.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.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/utf16.h=>include/iprt/utf16.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/crc.h=>include/iprt/crc.h \ + ${PATH_ROOT}/include/iprt/net.h=>include/iprt/net.h \ + ${PATH_ROOT}/include/iprt/rand.h=>include/iprt/rand.h \ + ${PATH_ROOT}/include/iprt/path.h=>include/iprt/path.h \ + ${PATH_ROOT}/include/iprt/once.h=>include/iprt/once.h \ + ${PATH_ROOT}/include/iprt/critsect.h=>include/iprt/critsect.h \ + ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \ + ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.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/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/include/VBox/ostypes.h=>include/VBox/ostypes.h \ + ${PATH_ROOT}/include/VBox/VMMDev.h=>include/VBox/VMMDev.h \ + ${PATH_ROOT}/include/VBox/VMMDevCoreTypes.h=>include/VBox/VMMDevCoreTypes.h \ + ${PATH_ROOT}/include/VBox/VBoxGuest.h=>include/VBox/VBoxGuest.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestCoreTypes.h=>include/VBox/VBoxGuestCoreTypes.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestLib.h=>include/VBox/VBoxGuestLib.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestMangling.h=>include/VBox/VBoxGuestMangling.h \ + ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \ + ${PATH_ROOT}/include/VBox/HostServices/GuestPropertySvc.h=>include/VBox/HostServices/GuestPropertySvc.h \ + ${PATH_ROOT}/include/VBox/vmm/cpuidcall.h=>include/VBox/vmm/cpuidcall.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp=>VBoxGuest.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c=>VBoxGuest-freebsd.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h=>VBoxGuestInternal.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile=>Makefile \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h=>VBoxGuestR0LibInternal.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp=>VBoxGuestR0LibGenericRequest.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp=>VBoxGuestR0LibHGCMInternal.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp=>VBoxGuestR0LibInit.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp=>VBoxGuestR0LibPhysHeap.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp=>VBoxGuestR0LibVMMDev.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/err/errinfo.cpp=>common/err/errinfo.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/log/RTLogCreateEx.cpp=>common/log/RTLogCreateEx.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/handletable.cpp=>common/misc/handletable.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/handletable.h=>common/misc/handletable.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/handletablectx.cpp=>common/misc/handletablectx.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/once.cpp=>common/misc/once.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/thread.cpp=>common/misc/thread.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg1Weak.cpp=>common/misc/RTAssertMsg1Weak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2.cpp=>common/misc/RTAssertMsg2.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2Add.cpp=>common/misc/RTAssertMsg2Add.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2AddWeak.cpp=>common/misc/RTAssertMsg2AddWeak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2AddWeakV.cpp=>common/misc/RTAssertMsg2AddWeakV.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2Weak.cpp=>common/misc/RTAssertMsg2Weak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2WeakV.cpp=>common/misc/RTAssertMsg2WeakV.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/assert.cpp=>common/misc/assert.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCat.cpp=>common/string/RTStrCat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCmp.cpp=>common/string/RTStrCmp.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopy.cpp=>common/string/RTStrCopy.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyEx.cpp=>common/string/RTStrCopyEx.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyP.cpp=>common/string/RTStrCopyP.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrEnd.cpp=>common/string/RTStrEnd.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrICmpAscii.cpp=>common/string/RTStrICmpAscii.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNICmpAscii.cpp=>common/string/RTStrNICmpAscii.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNCmp.cpp=>common/string/RTStrNCmp.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNLen.cpp=>common/string/RTStrNLen.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/stringalloc.cpp=>common/string/stringalloc.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrFormat.cpp=>common/string/RTStrFormat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatnum.cpp=>common/string/strformatnum.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/strprintf-ellipsis.cpp=>common/string/strprintf-ellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2.cpp=>common/string/strprintf2.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2-ellipsis.cpp=>common/string/strprintf2-ellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/memchr.cpp=>common/string/memchr.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/utf-8.cpp=>common/string/utf-8.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/rand/rand.cpp=>common/rand/rand.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/rand/randadv.cpp=>common/rand/randadv.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/rand/randparkmiller.cpp=>common/rand/randparkmiller.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/path/RTPathStripFilename.cpp=>common/path/RTPathStripFilename.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/checksum/crc32.cpp=>common/checksum/crc32.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/checksum/ipv4.cpp=>common/checksum/ipv4.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avlpv.cpp=>common/table/avlpv.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Base.cpp.h=>common/table/avl_Base.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Get.cpp.h=>common/table/avl_Get.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_GetBestFit.cpp.h=>common/table/avl_GetBestFit.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_RemoveBestFit.cpp.h=>common/table/avl_RemoveBestFit.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_DoWithAll.cpp.h=>common/table/avl_DoWithAll.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Destroy.cpp.h=>common/table/avl_Destroy.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/time/time.cpp=>common/time/time.c \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/assert.h=>include/internal/assert.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/initterm.h=>include/internal/initterm.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/iprt.h=>include/internal/iprt.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/lockvalidator.h=>include/internal/lockvalidator.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/magics.h=>include/internal/magics.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/mem.h=>include/internal/mem.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/include/internal/time.h=>include/internal/time.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/rand.h=>include/internal/rand.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/sched.h=>include/internal/sched.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/process.h=>include/internal/process.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/RTLogWriteDebugger-generic.cpp=>generic/RTLogWriteDebugger-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTRandAdvCreateSystemFaster-generic.cpp=>generic/RTRandAdvCreateSystemFaster-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTRandAdvCreateSystemTruer-generic.cpp=>generic/RTRandAdvCreateSystemTruer-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/uuid-generic.cpp=>generic/uuid-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp=>generic/RTSemEventWait-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp=>generic/RTSemEventWaitNoResume-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp=>generic/RTSemEventMultiWait-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp=>generic/RTSemEventMultiWaitNoResume-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTTimerCreate-generic.cpp=>generic/RTTimerCreate-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp=>generic/rtStrFormatKernelAddress-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/errvars-generic.cpp=>generic/errvars-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/timer-generic.cpp=>generic/timer-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/mppresent-generic.cpp=>generic/mppresent-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/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/freebsd/assert-r0drv-freebsd.c=>r0drv/freebsd/assert-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c=>r0drv/freebsd/alloc-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c=>r0drv/freebsd/initterm-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c=>r0drv/freebsd/memobj-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c=>r0drv/freebsd/memuserkernel-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c=>r0drv/freebsd/mp-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c=>r0drv/freebsd/process-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c=>r0drv/freebsd/semevent-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c=>r0drv/freebsd/semeventmulti-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c=>r0drv/freebsd/semfastmutex-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c=>r0drv/freebsd/semmutex-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c=>r0drv/freebsd/spinlock-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h=>r0drv/freebsd/the-freebsd-kernel.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c=>r0drv/freebsd/thread-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c=>r0drv/freebsd/thread2-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c=>r0drv/freebsd/time-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c=>r0drv/freebsd/timer-r0drv-freebsd.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h=>r0drv/freebsd/sleepqueue-r0drv-freebsd.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c=>r0drv/generic/semspinmutex-r0drv-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/generic/mpnotification-r0drv-generic.cpp=>r0drv/generic/mpnotification-r0drv-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.cpp=>r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.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/logbackdoor.cpp=>VBox/logbackdoor.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/RTLogWriteVmm-amd64-x86.cpp=>VBox/RTLogWriteVmm-amd64-x86.c \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ + ${PATH_OUT}/product-generated.h=>product-generated.h \ + ${PATH_OUT}/revision-generated.h=>revision-generated.h \ +" + +FILES_VBOXGUEST_BIN=" \ +" + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk new file mode 100644 index 00000000..d5f4c001 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk @@ -0,0 +1,262 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the common guest addition code library. +# + +# +# Copyright (C) 2006-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +SUB_DEPTH = ../../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# Target config. +# +if defined(VBOX_WITH_ADDITION_DRIVERS) && !defined(VBOX_ONLY_VALIDATIONKIT) + LIBRARIES += \ + VBoxGuestR0Lib \ + VBoxGuestR0LibBase +endif +LIBRARIES += \ + VBoxGuestR3Lib \ + VBoxGuestR3LibShared +ifndef VBOX_ONLY_VALIDATIONKIT + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd) + ifndef VBOX_USE_SYSTEM_XORG_HEADERS + LIBRARIES += \ + VBoxGuestR3LibXFree86 + endif + endif + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) + LIBRARIES += \ + VBoxGuestR3LibXOrg + endif +endif +LIBRARIES.win.amd64 += VBoxGuestR3Lib-x86 VBoxGuestR3LibShared-x86 + + +# +# VBoxGuestR0Lib +# +VBoxGuestR0Lib_TEMPLATE = VBOXGUESTR0LIB +VBoxGuestR0Lib_DEFS = VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxGuestR0Lib_INCS = \ + $(VBoxGuestR0Lib_0_OUTDIR) +VBoxGuestR0Lib_SOURCES = \ + VBoxGuestR0LibInit.cpp \ + VBoxGuestR0LibPhysHeap.cpp \ + VBoxGuestR0LibGenericRequest.cpp \ + VBoxGuestR0LibVMMDev.cpp \ + VBoxGuestR0LibHGCM.cpp \ + VbglR0CanUsePhysPageList.cpp \ + \ + VBoxGuestR0LibIdc.cpp \ + VBoxGuestR0LibSharedFolders.c \ + VBoxGuestR0LibCrOgl.cpp \ + VBoxGuestR0LibMouse.cpp +VBoxGuestR0Lib_SOURCES.os2 = VBoxGuestR0LibIdc-os2.cpp +VBoxGuestR0Lib_SOURCES.solaris = VBoxGuestR0LibIdc-solaris.cpp +VBoxGuestR0Lib_SOURCES.win = VBoxGuestR0LibIdc-win.cpp +ifn1of ($(KBUILD_TARGET), os2 solaris win) + VBoxGuestR0Lib_SOURCES += VBoxGuestR0LibIdc-unix.cpp +endif + +# +# VBoxGuestR0LibBase +# +VBoxGuestR0LibBase_TEMPLATE = VBOXGUESTR0LIB +VBoxGuestR0LibBase_DEFS = VBOX_WITH_HGCM VBGL_VBOXGUEST \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxGuestR0LibBase_INCS = $(VBoxGuestR0Lib_INCS) +VBoxGuestR0LibBase_INCS.win = $(VBoxGuestR0Lib_INCS.win) +VBoxGuestR0LibBase_SOURCES = \ + VBoxGuestR0LibInit.cpp \ + VBoxGuestR0LibPhysHeap.cpp \ + VBoxGuestR0LibGenericRequest.cpp \ + VBoxGuestR0LibVMMDev.cpp \ + VBoxGuestR0LibHGCMInternal.cpp \ + VbglR0CanUsePhysPageList.cpp + +# +# VBoxGuestR3Lib +# +if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Lib everywhere +VBoxGuestR3Lib_TEMPLATE = VBoxGuestR3Lib +else +VBoxGuestR3Lib_TEMPLATE = VBOXGUESTR3LIB +endif +VBoxGuestR3Lib_DEFS = \ + VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD),VBOX_WITH_SHARED_CLIPBOARD,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) \ + $(if $(VBOX_WITH_SHARED_FOLDERS),VBOX_WITH_SHARED_FOLDERS,) \ + $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) +VBoxGuestR3Lib_SOURCES = \ + VBoxGuestR3Lib.cpp \ + VBoxGuestR3LibAdditions.cpp \ + VBoxGuestR3LibAutoLogon.cpp \ + VBoxGuestR3LibBalloon.cpp \ + VBoxGuestR3LibCoreDump.cpp \ + VBoxGuestR3LibCpuHotPlug.cpp \ + VBoxGuestR3LibCredentials.cpp \ + VBoxGuestR3LibDrmClient.cpp \ + VBoxGuestR3LibEvent.cpp \ + VBoxGuestR3LibGuestUser.cpp \ + VBoxGuestR3LibGR.cpp \ + VBoxGuestR3LibHGCM.cpp \ + VBoxGuestR3LibHostChannel.cpp \ + VBoxGuestR3LibLog.cpp \ + VBoxGuestR3LibMisc.cpp \ + VBoxGuestR3LibStat.cpp \ + VBoxGuestR3LibTime.cpp \ + VBoxGuestR3LibModule.cpp \ + VBoxGuestR3LibPidFile.cpp \ + VBoxGuestR3LibVrdp.cpp \ + VBoxGuestR3LibMouse.cpp \ + VBoxGuestR3LibSeamless.cpp \ + VBoxGuestR3LibVideo.cpp +ifneq ($(KBUILD_TARGET),win) + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibDaemonize.cpp +endif +ifdef VBOX_WITH_GUEST_PROPS + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibGuestProp.cpp \ + VBoxGuestR3LibHostVersion.cpp +endif +ifdef VBOX_WITH_SHARED_CLIPBOARD + VBoxGuestR3Lib_DEFS += VBOX_WITH_SHARED_CLIPBOARD_GUEST + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibClipboard.cpp + ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + VBoxGuestR3Lib_SOURCES += \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardMIME.cpp + endif +endif +ifdef VBOX_WITH_SHARED_FOLDERS + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibSharedFolders.cpp +endif +ifdef VBOX_WITH_GUEST_CONTROL + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibGuestCtrl.cpp +endif +ifdef VBOX_WITH_DRAG_AND_DROP + VBoxGuestR3Lib_DEFS += \ + VBOX_WITH_DRAG_AND_DROP \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibDragAndDrop.cpp +endif + +VBoxGuestR3LibAdditions.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV) + +# +# VBoxGuestR3LibShared - a PIC variant of VBoxGuestR3Lib for linking into .so/.dll/.dylib. +# +if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Lib everywhere +VBoxGuestR3LibShared_TEMPLATE = VBoxGuestR3Dll +else +VBoxGuestR3LibShared_TEMPLATE = VBOXGUESTR3DLL +endif +VBoxGuestR3LibShared_DEFS := $(VBoxGuestR3Lib_DEFS) +VBoxGuestR3LibShared_SOURCES := $(VBoxGuestR3Lib_SOURCES) +VBoxGuestR3LibShared_INST := $(INST_ADDITIONS_LIB) + + +# +# VBoxGuestR3Lib-x86 - an x86 (32-bit) variant of VBoxGuestR3Lib for 64-bit Windows. +# +VBoxGuestR3Lib-x86_EXTENDS := VBoxGuestR3Lib +VBoxGuestR3Lib-x86_BLD_TRG_ARCH := x86 + + +# +# VBoxGuestR3LibShared-x86 - an x86 (32-bit) variant of VBoxGuestR3LibShared for 64-bit Windows. +# +VBoxGuestR3LibShared-x86_EXTENDS := VBoxGuestR3LibShared +VBoxGuestR3LibShared-x86_BLD_TRG_ARCH := x86 + + +# +# VBoxGuestR3LibXFree86 - a reduced version of the guest library which uses +# the X server runtime instead of IPRT, for use with old servers where the +# C library is not available. +# +VBoxGuestR3LibXFree86_TEMPLATE = VBOXGUESTR3XF86LIB +VBoxGuestR3LibXFree86_DEFS = \ + VBOX_WITH_HGCM \ + VBOX_VBGLR3_XFREE86 \ + RTMEM_NO_WRAP_TO_EF_APIS \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) +VBoxGuestR3LibXFree86_SOURCES = \ + VBoxGuestR3Lib.cpp \ + VBoxGuestR3LibGR.cpp \ + $(if $(VBOX_WITH_GUEST_PROPS),VBoxGuestR3LibGuestProp.cpp,) \ + VBoxGuestR3LibMouse.cpp \ + VBoxGuestR3LibMisc.cpp \ + VBoxGuestR3LibSeamless.cpp \ + VBoxGuestR3LibVideo.cpp \ + VBoxGuestR3LibRuntimeXF86.cpp +VBoxGuestR3LibXFree86_INCS = \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/Xserver \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11 + +# +# VBoxGuestR3LibXOrg - a reduced version of the guest library which uses +# the C server runtime instead of IPRT. +# +VBoxGuestR3LibXOrg_TEMPLATE = VBOXGUESTR3XORGLIB +VBoxGuestR3LibXOrg_DEFS = \ + VBOX_WITH_HGCM \ + VBOX_VBGLR3_XORG \ + RTMEM_NO_WRAP_TO_EF_APIS \ + IN_RT_STATIC \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) +VBoxGuestR3LibXOrg_SOURCES = $(VBoxGuestR3LibXFree86_SOURCES) + +VBoxGuestR3LibRuntimeXF86.cpp_CXXFLAGS = -Wno-shadow + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp new file mode 100644 index 00000000..fa545463 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp @@ -0,0 +1,134 @@ +/* $Id: VBoxGuestR0LibCrOgl.cpp $ */ +/** @file + * VBoxGuestLib - Ring-3 Support Library for VirtualBox guest additions, Chromium OpenGL Service. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include "VBoxGuestR0LibInternal.h" + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +DECLR0VBGL(int) VbglR0CrCtlCreate(VBGLCRCTLHANDLE *phCtl) +{ + int rc; + + if (phCtl) + { + struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc(); + if (pHandleData) + { + rc = VbglR0IdcOpen(&pHandleData->IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/); + if (RT_SUCCESS(rc)) + { + *phCtl = pHandleData; + return VINF_SUCCESS; + } + + vbglR0HGCMHandleFree(pHandleData); + } + else + rc = VERR_NO_MEMORY; + + *phCtl = NULL; + } + else + rc = VERR_INVALID_PARAMETER; + + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlDestroy(VBGLCRCTLHANDLE hCtl) +{ + VbglR0IdcClose(&hCtl->IdcHandle); + + vbglR0HGCMHandleFree(hCtl); + + return VINF_SUCCESS; +} + +DECLR0VBGL(int) VbglR0CrCtlConConnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID *pidClient) +{ + VBGLIOCHGCMCONNECT info; + int rc; + + if (!hCtl || !pidClient) + return VERR_INVALID_PARAMETER; + + RT_ZERO(info); + VBGLREQHDR_INIT(&info.Hdr, HGCM_CONNECT); + info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + RTStrCopy(info.u.In.Loc.u.host.achName, sizeof(info.u.In.Loc.u.host.achName), "VBoxSharedCrOpenGL"); + rc = VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &info.Hdr, sizeof(info)); + if (RT_SUCCESS(rc)) + { + Assert(info.u.Out.idClient); + *pidClient = info.u.Out.idClient; + return rc; + } + + AssertRC(rc); + *pidClient = 0; + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlConDisconnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID idClient) +{ + VBGLIOCHGCMDISCONNECT info; + VBGLREQHDR_INIT(&info.Hdr, HGCM_DISCONNECT); + info.u.In.idClient = idClient; + return VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &info.Hdr, sizeof(info)); +} + +DECLR0VBGL(int) VbglR0CrCtlConCallRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); +} + +DECLR0VBGL(int) VbglR0CrCtlConCall(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + int rc = VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); + if (RT_SUCCESS(rc)) + rc = pCallInfo->Hdr.rc; + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlConCallUserDataRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp new file mode 100644 index 00000000..b7718761 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp @@ -0,0 +1,183 @@ +/* $Id: VBoxGuestR0LibGenericRequest.cpp $ */ +/** @file + * VBoxGuestLibR0 - Generic VMMDev request management. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <VBox/err.h> + + +DECLR0VBGL(int) VbglGR0Verify(const VMMDevRequestHeader *pReq, size_t cbReq) +{ + size_t cbReqExpected; + + if (RT_UNLIKELY(!pReq || cbReq < sizeof(VMMDevRequestHeader))) + { + dprintf(("VbglGR0Verify: Invalid parameter: pReq = %p, cbReq = %zu\n", pReq, cbReq)); + return VERR_INVALID_PARAMETER; + } + + if (RT_UNLIKELY(pReq->size > cbReq)) + { + dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq)); + return VERR_INVALID_PARAMETER; + } + + /* The request size must correspond to the request type. */ + cbReqExpected = vmmdevGetRequestSize(pReq->requestType); + if (RT_UNLIKELY(cbReq < cbReqExpected)) + { + dprintf(("VbglGR0Verify: buffer size %zu < expected size %zu\n", cbReq, cbReqExpected)); + return VERR_INVALID_PARAMETER; + } + + if (cbReqExpected == cbReq) + { + /* + * This is most likely a fixed size request, and in this case the + * request size must be also equal to the expected size. + */ + if (RT_UNLIKELY(pReq->size != cbReqExpected)) + { + dprintf(("VbglGR0Verify: request size %u != expected size %zu\n", pReq->size, cbReqExpected)); + return VERR_INVALID_PARAMETER; + } + + return VINF_SUCCESS; + } + + /* + * This can be a variable size request. Check the request type and limit the size + * to VMMDEV_MAX_VMMDEVREQ_SIZE, which is max size supported by the host. + * + * Note: Keep this list sorted for easier human lookup! + */ + if ( pReq->requestType == VMMDevReq_ChangeMemBalloon + || pReq->requestType == VMMDevReq_GetDisplayChangeRequestMulti +#ifdef VBOX_WITH_64_BITS_GUESTS + || pReq->requestType == VMMDevReq_HGCMCall64 +#endif + || pReq->requestType == VMMDevReq_HGCMCall32 + || pReq->requestType == VMMDevReq_RegisterSharedModule + || pReq->requestType == VMMDevReq_ReportGuestUserState + || pReq->requestType == VMMDevReq_LogString + || pReq->requestType == VMMDevReq_SetPointerShape + || pReq->requestType == VMMDevReq_VideoSetVisibleRegion + || pReq->requestType == VMMDevReq_VideoUpdateMonitorPositions) + { + if (RT_UNLIKELY(cbReq > VMMDEV_MAX_VMMDEVREQ_SIZE)) + { + dprintf(("VbglGR0Verify: VMMDevReq_LogString: buffer size %zu too big\n", cbReq)); + return VERR_BUFFER_OVERFLOW; /** @todo is this error code ok? */ + } + } + else + { + dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq)); + return VERR_IO_BAD_LENGTH; /** @todo is this error code ok? */ + } + + return VINF_SUCCESS; +} + +DECLR0VBGL(int) VbglR0GRAlloc(VMMDevRequestHeader **ppReq, size_t cbReq, VMMDevRequestType enmReqType) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + { + if ( ppReq + && cbReq >= sizeof(VMMDevRequestHeader) + && cbReq == (uint32_t)cbReq) + { + VMMDevRequestHeader *pReq = (VMMDevRequestHeader *)VbglR0PhysHeapAlloc((uint32_t)cbReq); + AssertMsgReturn(pReq, ("VbglR0GRAlloc: no memory (cbReq=%u)\n", cbReq), VERR_NO_MEMORY); + memset(pReq, 0xAA, cbReq); + + pReq->size = (uint32_t)cbReq; + pReq->version = VMMDEV_REQUEST_HEADER_VERSION; + pReq->requestType = enmReqType; + pReq->rc = VERR_GENERAL_FAILURE; + pReq->reserved1 = 0; +#ifdef VBGL_VBOXGUEST + pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV +#else + pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV_OTHER +#endif + + | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + *ppReq = pReq; + rc = VINF_SUCCESS; + } + else + { + dprintf(("VbglR0GRAlloc: Invalid parameter: ppReq=%p cbReq=%u\n", ppReq, cbReq)); + rc = VERR_INVALID_PARAMETER; + } + } + return rc; +} + +DECLR0VBGL(int) VbglR0GRPerform(VMMDevRequestHeader *pReq) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + { + if (pReq) + { + RTCCPHYS PhysAddr = VbglR0PhysHeapGetPhysAddr(pReq); + if ( PhysAddr != 0 + && PhysAddr < _4G) /* Port IO is 32 bit. */ + { + ASMOutU32(g_vbgldata.portVMMDev + VMMDEV_PORT_OFF_REQUEST, (uint32_t)PhysAddr); + /* Make the compiler aware that the host has changed memory. */ + ASMCompilerBarrier(); + rc = pReq->rc; + } + else + rc = VERR_VBGL_INVALID_ADDR; + } + else + rc = VERR_INVALID_PARAMETER; + } + return rc; +} + +DECLR0VBGL(void) VbglR0GRFree(VMMDevRequestHeader *pReq) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + VbglR0PhysHeapFree(pReq); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp new file mode 100644 index 00000000..f190d1dc --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp @@ -0,0 +1,240 @@ +/* $Id: VBoxGuestR0LibHGCM.cpp $ */ +/** @file + * VBoxGuestLib - Host-Guest Communication Manager, ring-0 client drivers. + * + * These public functions can be only used by other drivers. They all + * do an IOCTL to VBoxGuest via IDC. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/assert.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <VBox/err.h> + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBGL_HGCM_ASSERT_MSG AssertReleaseMsg + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Fast heap for HGCM handles data. + * @{ + */ +static RTSEMFASTMUTEX g_hMtxHGCMHandleData; +static struct VBGLHGCMHANDLEDATA g_aHGCMHandleData[64]; +/** @} */ + + +/** + * Initializes the HGCM VBGL bits. + * + * @return VBox status code. + */ +DECLR0VBGL(int) VbglR0HGCMInit(void) +{ + AssertReturn(g_hMtxHGCMHandleData == NIL_RTSEMFASTMUTEX, VINF_ALREADY_INITIALIZED); + return RTSemFastMutexCreate(&g_hMtxHGCMHandleData); +} + +/** + * Initializes the HGCM VBGL bits. + * + * @return VBox status code. + */ +DECLR0VBGL(int) VbglR0HGCMTerminate(void) +{ + RTSemFastMutexDestroy(g_hMtxHGCMHandleData); + g_hMtxHGCMHandleData = NIL_RTSEMFASTMUTEX; + + return VINF_SUCCESS; +} + +DECLINLINE(int) vbglR0HandleHeapEnter(void) +{ + int rc = RTSemFastMutexRequest(g_hMtxHGCMHandleData); + + VBGL_HGCM_ASSERT_MSG(RT_SUCCESS(rc), ("Failed to request handle heap mutex, rc = %Rrc\n", rc)); + + return rc; +} + +DECLINLINE(void) vbglR0HandleHeapLeave(void) +{ + RTSemFastMutexRelease(g_hMtxHGCMHandleData); +} + +struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void) +{ + struct VBGLHGCMHANDLEDATA *p = NULL; + int rc = vbglR0HandleHeapEnter(); + if (RT_SUCCESS(rc)) + { + uint32_t i; + + /* Simple linear search in array. This will be called not so often, only connect/disconnect. */ + /** @todo bitmap for faster search and other obvious optimizations. */ + for (i = 0; i < RT_ELEMENTS(g_aHGCMHandleData); i++) + { + if (!g_aHGCMHandleData[i].fAllocated) + { + p = &g_aHGCMHandleData[i]; + p->fAllocated = 1; + break; + } + } + + vbglR0HandleHeapLeave(); + + VBGL_HGCM_ASSERT_MSG(p != NULL, ("Not enough HGCM handles.\n")); + } + return p; +} + +void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle) +{ + if (pHandle) + { + int rc = vbglR0HandleHeapEnter(); + if (RT_SUCCESS(rc)) + { + VBGL_HGCM_ASSERT_MSG(pHandle->fAllocated, ("Freeing not allocated handle.\n")); + + RT_ZERO(*pHandle); + vbglR0HandleHeapLeave(); + } + } +} + +DECLR0VBGL(int) VbglR0HGCMConnect(VBGLHGCMHANDLE *pHandle, const char *pszServiceName, HGCMCLIENTID *pidClient) +{ + int rc; + if (pHandle && pszServiceName && pidClient) + { + struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc(); + if (pHandleData) + { + rc = VbglR0IdcOpen(&pHandleData->IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/); + if (RT_SUCCESS(rc)) + { + VBGLIOCHGCMCONNECT Info; + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT); + Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName); + if (RT_SUCCESS(rc)) + { + rc = VbglR0IdcCall(&pHandleData->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + { + *pidClient = Info.u.Out.idClient; + *pHandle = pHandleData; + return rc; + } + } + + VbglR0IdcClose(&pHandleData->IdcHandle); + } + + vbglR0HGCMHandleFree(pHandleData); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMDisconnect(VBGLHGCMHANDLE handle, HGCMCLIENTID idClient) +{ + int rc; + VBGLIOCHGCMDISCONNECT Info; + + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT); + Info.u.In.idClient = idClient; + rc = VbglR0IdcCall(&handle->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info)); + + VbglR0IdcClose(&handle->IdcHandle); + + vbglR0HGCMHandleFree(handle); + + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMCallRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter), + ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms, + sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL))); + + return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbData), &pData->Hdr, cbData); +} + +DECLR0VBGL(int) VbglR0HGCMCall(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + int rc = VbglR0HGCMCallRaw(handle, pData, cbData); + if (RT_SUCCESS(rc)) + rc = pData->Hdr.rc; + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMCallUserDataRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter), + ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms, + sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL))); + + return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbData), &pData->Hdr, cbData); +} + + +DECLR0VBGL(int) VbglR0HGCMFastCall(VBGLHGCMHANDLE hHandle, PVBGLIOCIDCHGCMFASTCALL pCallReq, uint32_t cbCallReq) +{ + /* pCallReq->Hdr.rc and pCallReq->HgcmCallReq.header.header.rc; are not used by this IDC. */ + return VbglR0IdcCallRaw(&hHandle->IdcHandle, VBGL_IOCTL_IDC_HGCM_FAST_CALL, &pCallReq->Hdr, cbCallReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp new file mode 100644 index 00000000..a21b9f98 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp @@ -0,0 +1,1175 @@ +/* $Id: VBoxGuestR0LibHGCMInternal.cpp $ */ +/** @file + * VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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_HGCM + +#include "VBoxGuestR0LibInternal.h" +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include <VBox/err.h> + +#ifndef VBGL_VBOXGUEST +# error "This file should only be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The max parameter buffer size for a user request. */ +#define VBGLR0_MAX_HGCM_USER_PARM (24*_1M) +/** The max parameter buffer size for a kernel request. */ +#define VBGLR0_MAX_HGCM_KERNEL_PARM (16*_1M) +/** The max embedded buffer size. */ +#define VBGLR0_MAX_HGCM_EMBEDDED_BUFFER _64K + +#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) +/** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted + * side effects. + * Darwin 32bit & 64bit also needs this because of 4GB/4GB user/kernel space. */ +# define USE_BOUNCE_BUFFERS +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Lock info structure used by VbglR0HGCMInternalCall and its helpers. + */ +struct VbglR0ParmInfo +{ + uint32_t cLockBufs; + struct + { + uint32_t iParm; + RTR0MEMOBJ hObj; +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf; +#endif + } aLockBufs[10]; +}; + + + +/* These functions can be only used by VBoxGuest. */ + +DECLR0VBGL(int) VbglR0HGCMInternalConnect(HGCMServiceLocation const *pLoc, uint32_t fRequestor, HGCMCLIENTID *pidClient, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + int rc; + if ( RT_VALID_PTR(pLoc) + && RT_VALID_PTR(pidClient) + && RT_VALID_PTR(pfnAsyncCallback)) + { + /* Allocate request */ + VMMDevHGCMConnect *pHGCMConnect = NULL; + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMConnect, sizeof(VMMDevHGCMConnect), VMMDevReq_HGCMConnect); + if (RT_SUCCESS(rc)) + { + /* Initialize request memory */ + pHGCMConnect->header.header.fRequestor = fRequestor; + + pHGCMConnect->header.fu32Flags = 0; + + memcpy(&pHGCMConnect->loc, pLoc, sizeof(pHGCMConnect->loc)); + pHGCMConnect->u32ClientID = 0; + + /* Issue request */ + rc = VbglR0GRPerform (&pHGCMConnect->header.header); + if (RT_SUCCESS(rc)) + { + /* Check if host decides to process the request asynchronously. */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + /* Wait for request completion interrupt notification from host */ + pfnAsyncCallback(&pHGCMConnect->header, pvAsyncData, u32AsyncData); + } + + rc = pHGCMConnect->header.result; + if (RT_SUCCESS(rc)) + *pidClient = pHGCMConnect->u32ClientID; + } + VbglR0GRFree(&pHGCMConnect->header.header); + } + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + + +DECLR0VBGL(int) VbglR0HGCMInternalDisconnect(HGCMCLIENTID idClient, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + int rc; + if ( idClient != 0 + && pfnAsyncCallback) + { + /* Allocate request */ + VMMDevHGCMDisconnect *pHGCMDisconnect = NULL; + rc = VbglR0GRAlloc ((VMMDevRequestHeader **)&pHGCMDisconnect, sizeof (VMMDevHGCMDisconnect), VMMDevReq_HGCMDisconnect); + if (RT_SUCCESS(rc)) + { + /* Initialize request memory */ + pHGCMDisconnect->header.header.fRequestor = fRequestor; + + pHGCMDisconnect->header.fu32Flags = 0; + + pHGCMDisconnect->u32ClientID = idClient; + + /* Issue request */ + rc = VbglR0GRPerform(&pHGCMDisconnect->header.header); + if (RT_SUCCESS(rc)) + { + /* Check if host decides to process the request asynchronously. */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + /* Wait for request completion interrupt notification from host */ + pfnAsyncCallback(&pHGCMDisconnect->header, pvAsyncData, u32AsyncData); + } + + rc = pHGCMDisconnect->header.result; + } + + VbglR0GRFree(&pHGCMDisconnect->header.header); + } + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + + +/** + * Preprocesses the HGCM call, validating and locking/buffering parameters. + * + * @returns VBox status code. + * + * @param pCallInfo The call info. + * @param cbCallInfo The size of the call info structure. + * @param fIsUser Is it a user request or kernel request. + * @param pcbExtra Where to return the extra request space needed for + * physical page lists. + */ +static int vbglR0HGCMInternalPreprocessCall(PCVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, + bool fIsUser, struct VbglR0ParmInfo *pParmInfo, size_t *pcbExtra) +{ + HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + uint32_t const cParms = pCallInfo->cParms; + uint32_t iParm; + uint32_t cb; + + /* + * Lock down the any linear buffers so we can get their addresses + * and figure out how much extra storage we need for page lists. + * + * Note! With kernel mode users we can be assertive. For user mode users + * we should just (debug) log it and fail without any fanfare. + */ + *pcbExtra = 0; + pParmInfo->cLockBufs = 0; + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++) + { + switch (pSrcParm->type) + { + case VMMDevHGCMParmType_32bit: + Log4(("GstHGCMCall: parm=%u type=32bit: %#010x\n", iParm, pSrcParm->u.value32)); + break; + + case VMMDevHGCMParmType_64bit: + Log4(("GstHGCMCall: parm=%u type=64bit: %#018RX64\n", iParm, pSrcParm->u.value64)); + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + if (fIsUser) + return VERR_INVALID_PARAMETER; + cb = pSrcParm->u.PageList.size; + if (cb) + { + uint32_t off = pSrcParm->u.PageList.offset; + HGCMPageListInfo *pPgLst; + uint32_t cPages; + uint32_t u32; + + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter) + && off <= cbCallInfo - sizeof(HGCMPageListInfo), + ("offset=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + + pPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + off); + cPages = pPgLst->cPages; + u32 = RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]) + off; + AssertMsgReturn(u32 <= cbCallInfo, + ("u32=%#x (cPages=%#x offset=%#x) cbCallInfo=%#x\n", u32, cPages, off, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn(pPgLst->offFirstPage < PAGE_SIZE, ("#x\n", pPgLst->offFirstPage), VERR_INVALID_PARAMETER); + u32 = RT_ALIGN_32(pPgLst->offFirstPage + cb, PAGE_SIZE) >> PAGE_SHIFT; + AssertMsgReturn(cPages == u32, ("cPages=%#x u32=%#x\n", cPages, u32), VERR_INVALID_PARAMETER); + AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pPgLst->flags), ("%#x\n", pPgLst->flags), VERR_INVALID_PARAMETER); + Log4(("GstHGCMCall: parm=%u type=pglst: cb=%#010x cPgs=%u offPg0=%#x flags=%#x\n", + iParm, cb, cPages, pPgLst->offFirstPage, pPgLst->flags)); + u32 = cPages; + while (u32-- > 0) + { + Log4(("GstHGCMCall: pg#%u=%RHp\n", u32, pPgLst->aPages[u32])); + AssertMsgReturn(!(pPgLst->aPages[u32] & (PAGE_OFFSET_MASK | UINT64_C(0xfff0000000000000))), + ("pg#%u=%RHp\n", u32, pPgLst->aPages[u32]), + VERR_INVALID_PARAMETER); + } + + *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[pPgLst->cPages]); + } + else + Log4(("GstHGCMCall: parm=%u type=pglst: cb=0\n", iParm)); + break; + + case VMMDevHGCMParmType_Embedded: + if (fIsUser) /// @todo relax this. + return VERR_INVALID_PARAMETER; + cb = pSrcParm->u.Embedded.cbData; + if (cb) + { + uint32_t off = pSrcParm->u.Embedded.offData; + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_EMBEDDED_BUFFER, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_EMBEDDED_BUFFER), + VERR_INVALID_PARAMETER); + AssertMsgReturn(cb <= cbCallInfo - cParms * sizeof(HGCMFunctionParameter), + ("cb=%#x cParms=%#x cbCallInfo=%3x\n", cb, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter) + && off <= cbCallInfo - cb, + ("offData=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pSrcParm->u.Embedded.fFlags), + ("%#x\n", pSrcParm->u.Embedded.fFlags), VERR_INVALID_PARAMETER); + + *pcbExtra += RT_ALIGN_32(cb, 8); + } + else + Log4(("GstHGCMCall: parm=%u type=embed: cb=0\n", iParm)); + break; + + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (fIsUser) + return VERR_INVALID_PARAMETER; + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + cb = pSrcParm->u.Pointer.size; + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + if (cb != 0) + Log4(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr)); + else + Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type)); + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + cb = pSrcParm->u.Pointer.size; + if (cb != 0) + { +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf = NULL; +#endif + uint32_t iLockBuf = pParmInfo->cLockBufs; + RTR0MEMOBJ hObj; + int rc; + uint32_t fAccess = pSrcParm->type == VMMDevHGCMParmType_LinAddr_In + || pSrcParm->type == VMMDevHGCMParmType_LinAddr_Locked_In + ? RTMEM_PROT_READ + : RTMEM_PROT_READ | RTMEM_PROT_WRITE; + + AssertReturn(iLockBuf < RT_ELEMENTS(pParmInfo->aLockBufs), VERR_INVALID_PARAMETER); + if (!fIsUser) + { + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + rc = RTR0MemObjLockKernel(&hObj, (void *)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess); + if (RT_FAILURE(rc)) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockKernel(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked kernel -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + } + else if (cb > VBGLR0_MAX_HGCM_USER_PARM) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u pv=%p cb=%#x > %#x -> out of range\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, + cb, VBGLR0_MAX_HGCM_USER_PARM)); + return VERR_OUT_OF_RANGE; + } + else + { +#ifndef USE_BOUNCE_BUFFERS + rc = RTR0MemObjLockUser(&hObj, (RTR3PTR)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess, NIL_RTR0PROCESS); + if (RT_FAILURE(rc)) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockUser(,%p,%#x,nil) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked user -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + +#else /* USE_BOUNCE_BUFFERS */ + /* + * This is a bit massive, but we don't want to waste a + * whole page for a 3 byte string buffer (guest props). + * + * The threshold is ASSUMING sizeof(RTMEMHDR) == 16 and + * the system is using some power of two allocator. + */ + /** @todo A more efficient strategy would be to combine buffers. However it + * is probably going to be more massive than the current code, so + * it can wait till later. */ + bool fCopyIn = pSrcParm->type != VMMDevHGCMParmType_LinAddr_Out + && pSrcParm->type != VMMDevHGCMParmType_LinAddr_Locked_Out; + if (cb <= PAGE_SIZE / 2 - 16) + { + pvSmallBuf = fCopyIn ? RTMemTmpAlloc(cb) : RTMemTmpAllocZ(cb); + if (RT_UNLIKELY(!pvSmallBuf)) + return VERR_NO_MEMORY; + if (fCopyIn) + { + rc = RTR0MemUserCopyFrom(pvSmallBuf, pSrcParm->u.Pointer.u.linearAddr, cb); + if (RT_FAILURE(rc)) + { + RTMemTmpFree(pvSmallBuf); + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, + pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + } + rc = RTR0MemObjLockKernel(&hObj, pvSmallBuf, cb, fAccess); + if (RT_FAILURE(rc)) + { + RTMemTmpFree(pvSmallBuf); + Log(("GstHGCMCall: RTR0MemObjLockKernel failed for small buffer: rc=%Rrc pvSmallBuf=%p cb=%#x\n", + rc, pvSmallBuf, cb)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p small buffer %p -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, pvSmallBuf, hObj)); + } + else + { + rc = RTR0MemObjAllocPage(&hObj, cb, false /*fExecutable*/); + if (RT_FAILURE(rc)) + return rc; + if (!fCopyIn) + memset(RTR0MemObjAddress(hObj), '\0', cb); + else + { + rc = RTR0MemUserCopyFrom(RTR0MemObjAddress(hObj), pSrcParm->u.Pointer.u.linearAddr, cb); + if (RT_FAILURE(rc)) + { + RTR0MemObjFree(hObj, false /*fFreeMappings*/); + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, + pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p big buffer -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + } +#endif /* USE_BOUNCE_BUFFERS */ + } + + pParmInfo->aLockBufs[iLockBuf].iParm = iParm; + pParmInfo->aLockBufs[iLockBuf].hObj = hObj; +#ifdef USE_BOUNCE_BUFFERS + pParmInfo->aLockBufs[iLockBuf].pvSmallBuf = pvSmallBuf; +#endif + pParmInfo->cLockBufs = iLockBuf + 1; + + if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false)) + { + size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT; + *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + } + else + Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type)); + break; + + default: + return VERR_INVALID_PARAMETER; + } + } + + return VINF_SUCCESS; +} + + +/** + * Translates locked linear address to the normal type. + * The locked types are only for the guest side and not handled by the host. + * + * @returns normal linear address type. + * @param enmType The type. + */ +static HGCMFunctionParameterType vbglR0HGCMInternalConvertLinAddrType(HGCMFunctionParameterType enmType) +{ + switch (enmType) + { + case VMMDevHGCMParmType_LinAddr_Locked_In: + return VMMDevHGCMParmType_LinAddr_In; + case VMMDevHGCMParmType_LinAddr_Locked_Out: + return VMMDevHGCMParmType_LinAddr_Out; + case VMMDevHGCMParmType_LinAddr_Locked: + return VMMDevHGCMParmType_LinAddr; + default: + return enmType; + } +} + + +/** + * Translates linear address types to page list direction flags. + * + * @returns page list flags. + * @param enmType The type. + */ +static uint32_t vbglR0HGCMInternalLinAddrTypeToPageListFlags(HGCMFunctionParameterType enmType) +{ + switch (enmType) + { + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Locked_In: + return VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + return VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + + default: AssertFailed(); RT_FALL_THRU(); + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_Locked: + return VBOX_HGCM_F_PARM_DIRECTION_BOTH; + } +} + + +/** + * Initializes the call request that we're sending to the host. + * + * @returns VBox status code. + * + * @param pCallInfo The call info. + * @param cbCallInfo The size of the call info structure. + * @param fRequestor VMMDEV_REQUESTOR_XXX. + * @param fIsUser Is it a user request or kernel request. + * @param pcbExtra Where to return the extra request space needed for + * physical page lists. + */ +static void vbglR0HGCMInternalInitCall(VMMDevHGCMCall *pHGCMCall, PCVBGLIOCHGCMCALL pCallInfo, + uint32_t cbCallInfo, uint32_t fRequestor, bool fIsUser, struct VbglR0ParmInfo *pParmInfo) +{ + HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + HGCMFunctionParameter *pDstParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); + uint32_t const cParms = pCallInfo->cParms; + uint32_t offExtra = (uint32_t)((uintptr_t)(pDstParm + cParms) - (uintptr_t)pHGCMCall); + uint32_t iLockBuf = 0; + uint32_t iParm; + RT_NOREF1(cbCallInfo); +#ifndef USE_BOUNCE_BUFFERS + RT_NOREF1(fIsUser); +#endif + + /* + * The call request headers. + */ + pHGCMCall->header.header.fRequestor = !fIsUser || (fRequestor & VMMDEV_REQUESTOR_USERMODE) ? fRequestor + : VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_USR_NOT_GIVEN + | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN | VMMDEV_REQUESTOR_CON_DONT_KNOW; + + pHGCMCall->header.fu32Flags = 0; + pHGCMCall->header.result = VINF_SUCCESS; + + pHGCMCall->u32ClientID = pCallInfo->u32ClientID; + pHGCMCall->u32Function = pCallInfo->u32Function; + pHGCMCall->cParms = cParms; + + /* + * The parameters. + */ + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++) + { + switch (pSrcParm->type) + { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *pDstParm = *pSrcParm; + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + pDstParm->type = pSrcParm->type; + pDstParm->u.PageList.size = pSrcParm->u.PageList.size; + if (pSrcParm->u.PageList.size) + { + HGCMPageListInfo const *pSrcPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + pSrcParm->u.PageList.offset); + HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra); + uint32_t const cPages = pSrcPgLst->cPages; + uint32_t iPage; + + pDstParm->u.PageList.offset = offExtra; + pDstPgLst->flags = pSrcPgLst->flags; + pDstPgLst->offFirstPage = pSrcPgLst->offFirstPage; + pDstPgLst->cPages = (uint16_t)cPages; + for (iPage = 0; iPage < cPages; iPage++) + pDstPgLst->aPages[iPage] = pSrcPgLst->aPages[iPage]; + + offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + else + pDstParm->u.PageList.offset = 0; /** @todo will fail on the host side now */ + break; + + case VMMDevHGCMParmType_Embedded: + { + uint32_t const cb = pSrcParm->u.Embedded.cbData; + pDstParm->type = VMMDevHGCMParmType_Embedded; + pDstParm->u.Embedded.cbData = cb; + pDstParm->u.Embedded.offData = offExtra; + if (cb > 0) + { + uint8_t *pbDst = (uint8_t *)pHGCMCall + offExtra; + if (pSrcParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST) + { + memcpy(pbDst, (uint8_t const *)pCallInfo + pSrcParm->u.Embedded.offData, cb); + if (RT_ALIGN(cb, 8) != cb) + memset(pbDst + cb, 0, RT_ALIGN(cb, 8) - cb); + } + else + RT_BZERO(pbDst, RT_ALIGN(cb, 8)); + offExtra += RT_ALIGN(cb, 8); + } + break; + } + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + *pDstParm = *pSrcParm; + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + if (pSrcParm->u.Pointer.size != 0) + { +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf = pParmInfo->aLockBufs[iLockBuf].pvSmallBuf; +#endif + RTR0MEMOBJ hObj = pParmInfo->aLockBufs[iLockBuf].hObj; + Assert(iParm == pParmInfo->aLockBufs[iLockBuf].iParm); + + if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false)) + { + HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra); + size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT; + size_t iPage; + + pDstParm->type = VMMDevHGCMParmType_PageList; + pDstParm->u.PageList.size = pSrcParm->u.Pointer.size; + pDstParm->u.PageList.offset = offExtra; + pDstPgLst->flags = vbglR0HGCMInternalLinAddrTypeToPageListFlags(pSrcParm->type); +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + pDstPgLst->offFirstPage = (uintptr_t)pvSmallBuf & PAGE_OFFSET_MASK; + else +#endif + pDstPgLst->offFirstPage = (uint16_t)(pSrcParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK); + pDstPgLst->cPages = (uint16_t)cPages; Assert(pDstPgLst->cPages == cPages); + for (iPage = 0; iPage < cPages; iPage++) + { + pDstPgLst->aPages[iPage] = RTR0MemObjGetPagePhysAddr(hObj, iPage); + Assert(pDstPgLst->aPages[iPage] != NIL_RTHCPHYS); + } + + offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + else + { + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + pDstParm->u.Pointer.u.linearAddr = pvSmallBuf + ? (uintptr_t)pvSmallBuf + : (uintptr_t)RTR0MemObjAddress(hObj); + else +#endif + pDstParm->u.Pointer.u.linearAddr = pSrcParm->u.Pointer.u.linearAddr; + } + iLockBuf++; + } + else + { + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + pDstParm->u.Pointer.size = 0; + pDstParm->u.Pointer.u.linearAddr = 0; + } + break; + + default: + AssertFailed(); + pDstParm->type = VMMDevHGCMParmType_Invalid; + break; + } + } +} + + +/** + * Performs the call and completion wait. + * + * @returns VBox status code of this operation, not necessarily the call. + * + * @param pHGCMCall The HGCM call info. + * @param pfnAsyncCallback The async callback that will wait for the call + * to complete. + * @param pvAsyncData Argument for the callback. + * @param u32AsyncData Argument for the callback. + * @param pfLeakIt Where to return the leak it / free it, + * indicator. Cancellation fun. + */ +static int vbglR0HGCMInternalDoCall(VMMDevHGCMCall *pHGCMCall, PFNVBGLHGCMCALLBACK pfnAsyncCallback, + void *pvAsyncData, uint32_t u32AsyncData, bool *pfLeakIt) +{ + int rc; + + Log(("calling VbglR0GRPerform\n")); + rc = VbglR0GRPerform(&pHGCMCall->header.header); + Log(("VbglR0GRPerform rc = %Rrc (header rc=%d)\n", rc, pHGCMCall->header.result)); + + /* + * If the call failed, but as a result of the request itself, then pretend + * success. Upper layers will interpret the result code in the packet. + */ + if ( RT_FAILURE(rc) + && rc == pHGCMCall->header.result) + { + Assert(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE); + rc = VINF_SUCCESS; + } + + /* + * Check if host decides to process the request asynchronously, + * if so, we wait for it to complete using the caller supplied callback. + */ + *pfLeakIt = false; + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + Log(("Processing HGCM call asynchronously\n")); + rc = pfnAsyncCallback(&pHGCMCall->header, pvAsyncData, u32AsyncData); + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + { + Assert(!(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED)); + rc = VINF_SUCCESS; + } + else + { + /* + * The request didn't complete in time or the call was interrupted, + * the RC from the callback indicates which. Try cancel the request. + * + * This is a bit messy because we're racing request completion. Sorry. + */ + /** @todo It would be nice if we could use the waiter callback to do further + * waiting in case of a completion race. If it wasn't for WINNT having its own + * version of all that stuff, I would've done it already. */ + VMMDevHGCMCancel2 *pCancelReq; + int rc2 = VbglR0GRAlloc((VMMDevRequestHeader **)&pCancelReq, sizeof(*pCancelReq), VMMDevReq_HGCMCancel2); + if (RT_SUCCESS(rc2)) + { + pCancelReq->physReqToCancel = VbglR0PhysHeapGetPhysAddr(pHGCMCall); + rc2 = VbglR0GRPerform(&pCancelReq->header); + VbglR0GRFree(&pCancelReq->header); + } +#if 1 /** @todo ADDVER: Remove this on next minor version change. */ + if (rc2 == VERR_NOT_IMPLEMENTED) + { + /* host is too old, or we're out of heap. */ + pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + pHGCMCall->header.header.requestType = VMMDevReq_HGCMCancel; + rc2 = VbglR0GRPerform(&pHGCMCall->header.header); + if (rc2 == VERR_INVALID_PARAMETER) + rc2 = VERR_NOT_FOUND; + else if (RT_SUCCESS(rc)) + RTThreadSleep(1); + } +#endif + if (RT_SUCCESS(rc)) rc = VERR_INTERRUPTED; /** @todo weed this out from the WINNT VBoxGuest code. */ + if (RT_SUCCESS(rc2)) + { + Log(("vbglR0HGCMInternalDoCall: successfully cancelled\n")); + pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + } + else + { + /* + * Wait for a bit while the host (hopefully) completes it. + */ + uint64_t u64Start = RTTimeSystemMilliTS(); + uint32_t cMilliesToWait = rc2 == VERR_NOT_FOUND || rc2 == VERR_SEM_DESTROYED ? 500 : 2000; + uint64_t cElapsed = 0; + if (rc2 != VERR_NOT_FOUND) + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("vbglR0HGCMInternalDoCall: Failed to cancel the HGCM call on %Rrc: rc2=%Rrc\n", rc, rc2)); + } + else + Log(("vbglR0HGCMInternalDoCall: Cancel race rc=%Rrc rc2=%Rrc\n", rc, rc2)); + + do + { + ASMCompilerBarrier(); /* paranoia */ + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + break; + RTThreadSleep(1); + cElapsed = RTTimeSystemMilliTS() - u64Start; + } while (cElapsed < cMilliesToWait); + + ASMCompilerBarrier(); /* paranoia^2 */ + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + rc = VINF_SUCCESS; + else + { + LogRel(("vbglR0HGCMInternalDoCall: Leaking %u bytes. Pending call to %u with %u parms. (rc2=%Rrc)\n", + pHGCMCall->header.header.size, pHGCMCall->u32Function, pHGCMCall->cParms, rc2)); + *pfLeakIt = true; + } + Log(("vbglR0HGCMInternalDoCall: Cancel race ended with rc=%Rrc (rc2=%Rrc) after %llu ms\n", rc, rc2, cElapsed)); + } + } + } + + Log(("GstHGCMCall: rc=%Rrc result=%Rrc fu32Flags=%#x fLeakIt=%d\n", + rc, pHGCMCall->header.result, pHGCMCall->header.fu32Flags, *pfLeakIt)); + return rc; +} + + +/** + * Copies the result of the call back to the caller info structure and user + * buffers (if using bounce buffers). + * + * @returns rc, unless RTR0MemUserCopyTo fails. + * @param pCallInfo Call info structure to update. + * @param cbCallInfo The size of the client request. + * @param pHGCMCall HGCM call request. + * @param cbHGCMCall The size of the HGCM call request. + * @param pParmInfo Parameter locking/buffering info. + * @param fIsUser Is it a user (true) or kernel request. + * @param rc The current result code. Passed along to + * preserve informational status codes. + */ +static int vbglR0HGCMInternalCopyBackResult(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, + VMMDevHGCMCall const *pHGCMCall, uint32_t cbHGCMCall, + struct VbglR0ParmInfo *pParmInfo, bool fIsUser, int rc) +{ + HGCMFunctionParameter const *pSrcParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); + HGCMFunctionParameter *pDstParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + uint32_t const cParms = pCallInfo->cParms; +#ifdef USE_BOUNCE_BUFFERS + uint32_t iLockBuf = 0; +#endif + uint32_t iParm; + RT_NOREF1(pParmInfo); +#ifndef USE_BOUNCE_BUFFERS + RT_NOREF1(fIsUser); +#endif + + /* + * The call result. + */ + pCallInfo->Hdr.rc = pHGCMCall->header.result; + + /* + * Copy back parameters. + */ + /** @todo This is assuming user data (pDstParm) is buffered. Not true + * on OS/2, though I'm not sure we care... */ + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++) + { + switch (pDstParm->type) + { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *pDstParm = *pSrcParm; + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + pDstParm->u.PageList.size = pSrcParm->u.PageList.size; + break; + + case VMMDevHGCMParmType_Embedded: + { + uint32_t const cbDst = pDstParm->u.Embedded.cbData; + uint32_t cbSrc; + pDstParm->u.Embedded.cbData = cbSrc = pSrcParm->u.Embedded.cbData; + if ( cbSrc > 0 + && (pDstParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)) + { + uint32_t const offDst = pDstParm->u.Embedded.offData; + uint32_t const offSrc = pSrcParm->u.Embedded.offData; + + AssertReturn(offDst < cbCallInfo, VERR_INTERNAL_ERROR_2); + AssertReturn(offDst >= sizeof(*pCallInfo) + cParms * sizeof(*pDstParm), VERR_INTERNAL_ERROR_2); + AssertReturn(cbDst <= cbCallInfo - offDst , VERR_INTERNAL_ERROR_2); + + AssertReturn(offSrc < cbCallInfo, VERR_INTERNAL_ERROR_2); + AssertReturn(offSrc >= sizeof(*pHGCMCall) + cParms * sizeof(*pSrcParm), VERR_INTERNAL_ERROR_2); + if (cbSrc <= cbHGCMCall - offSrc) + { /* likely */ } + else + { + /* Special case: Buffer overflow w/ correct size given. */ + AssertReturn(RT_FAILURE_NP(rc), VERR_INTERNAL_ERROR_2); + cbSrc = cbHGCMCall - offSrc; + } + memcpy((uint8_t *)pCallInfo + offDst, (uint8_t const *)pHGCMCall + offSrc, RT_MIN(cbSrc, cbDst)); + } + break; + } + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_In: +#ifdef USE_BOUNCE_BUFFERS + if ( fIsUser + && iLockBuf < pParmInfo->cLockBufs + && iParm == pParmInfo->aLockBufs[iLockBuf].iParm) + iLockBuf++; +#endif + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + { +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + { + size_t cbOut = RT_MIN(pSrcParm->u.Pointer.size, pDstParm->u.Pointer.size); + if (cbOut) + { + int rc2; + Assert(pParmInfo->aLockBufs[iLockBuf].iParm == iParm); + rc2 = RTR0MemUserCopyTo((RTR3PTR)pDstParm->u.Pointer.u.linearAddr, + pParmInfo->aLockBufs[iLockBuf].pvSmallBuf + ? pParmInfo->aLockBufs[iLockBuf].pvSmallBuf + : RTR0MemObjAddress(pParmInfo->aLockBufs[iLockBuf].hObj), + cbOut); + if (RT_FAILURE(rc2)) + return rc2; + iLockBuf++; + } + else if ( iLockBuf < pParmInfo->cLockBufs + && iParm == pParmInfo->aLockBufs[iLockBuf].iParm) + iLockBuf++; + } +#endif + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + } + + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_4; + break; + } + } + +#ifdef USE_BOUNCE_BUFFERS + Assert(!fIsUser || pParmInfo->cLockBufs == iLockBuf); +#endif + return rc; +} + + +DECLR0VBGL(int) VbglR0HGCMInternalCall(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + bool fIsUser = (fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_USER; + struct VbglR0ParmInfo ParmInfo; + size_t cbExtra; + int rc; + + /* + * Basic validation. + */ + AssertMsgReturn( !pCallInfo + || !pfnAsyncCallback + || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS + || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK), + ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags), + VERR_INVALID_PARAMETER); + AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL) + || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter), + VERR_INVALID_PARAMETER); + + Log(("GstHGCMCall: u32ClientID=%#x u32Function=%u cParms=%u cbCallInfo=%#x fFlags=%#x\n", + pCallInfo->u32ClientID, pCallInfo->u32ClientID, pCallInfo->u32Function, pCallInfo->cParms, cbCallInfo, fFlags)); + + /* + * Validate, lock and buffer the parameters for the call. + * This will calculate the amount of extra space for physical page list. + */ + rc = vbglR0HGCMInternalPreprocessCall(pCallInfo, cbCallInfo, fIsUser, &ParmInfo, &cbExtra); + if (RT_SUCCESS(rc)) + { + /* + * Allocate the request buffer and recreate the call request. + */ + VMMDevHGCMCall *pHGCMCall; + uint32_t const cbHGCMCall = sizeof(VMMDevHGCMCall) + pCallInfo->cParms * sizeof(HGCMFunctionParameter) + (uint32_t)cbExtra; + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMCall, cbHGCMCall, VMMDevReq_HGCMCall); + if (RT_SUCCESS(rc)) + { + bool fLeakIt; + vbglR0HGCMInternalInitCall(pHGCMCall, pCallInfo, cbCallInfo, fRequestor, fIsUser, &ParmInfo); + + /* + * Perform the call. + */ + rc = vbglR0HGCMInternalDoCall(pHGCMCall, pfnAsyncCallback, pvAsyncData, u32AsyncData, &fLeakIt); + if (RT_SUCCESS(rc)) + { + /* + * Copy back the result (parameters and buffers that changed). + */ + rc = vbglR0HGCMInternalCopyBackResult(pCallInfo, cbCallInfo, pHGCMCall, cbHGCMCall, &ParmInfo, fIsUser, rc); + } + else + { + if ( rc != VERR_INTERRUPTED + && rc != VERR_TIMEOUT) + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalDoCall failed. rc=%Rrc\n", rc)); + } + } + + if (!fLeakIt) + VbglR0GRFree(&pHGCMCall->header.header); + } + } + else + LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalPreprocessCall failed. rc=%Rrc\n", rc)); + + /* + * Release locks and free bounce buffers. + */ + if (ParmInfo.cLockBufs) + while (ParmInfo.cLockBufs-- > 0) + { + RTR0MemObjFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].hObj, false /*fFreeMappings*/); +#ifdef USE_BOUNCE_BUFFERS + RTMemTmpFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].pvSmallBuf); +#endif + } + + return rc; +} + + +#if ARCH_BITS == 64 +DECLR0VBGL(int) VbglR0HGCMInternalCall32(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + PVBGLIOCHGCMCALL pCallInfo64 = NULL; + HGCMFunctionParameter *pParm64 = NULL; + HGCMFunctionParameter32 *pParm32 = NULL; + uint32_t cParms = 0; + uint32_t iParm = 0; + int rc = VINF_SUCCESS; + + /* + * Input validation. + */ + AssertMsgReturn( !pCallInfo + || !pfnAsyncCallback + || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS + || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK), + ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags), + VERR_INVALID_PARAMETER); + AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL) + || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter32), + VERR_INVALID_PARAMETER); + + /* This Assert does not work on Solaris/Windows 64/32 mixed mode, not sure why, skipping for now */ +#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_WINDOWS) + AssertReturn((fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_KERNEL, VERR_WRONG_ORDER); +#endif + + cParms = pCallInfo->cParms; + Log(("VbglR0HGCMInternalCall32: cParms=%d, u32Function=%d, fFlags=%#x\n", cParms, pCallInfo->u32Function, fFlags)); + + /* + * The simple approach, allocate a temporary request and convert the parameters. + */ + pCallInfo64 = (PVBGLIOCHGCMCALL)RTMemTmpAllocZ(sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter)); + if (!pCallInfo64) + return VERR_NO_TMP_MEMORY; + + *pCallInfo64 = *pCallInfo; + pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo); + pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64); + for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++) + { + switch (pParm32->type) + { + case VMMDevHGCMParmType_32bit: + pParm64->type = VMMDevHGCMParmType_32bit; + pParm64->u.value32 = pParm32->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + pParm64->type = VMMDevHGCMParmType_64bit; + pParm64->u.value64 = pParm32->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + pParm64->type = pParm32->type; + pParm64->u.Pointer.size = pParm32->u.Pointer.size; + pParm64->u.Pointer.u.linearAddr = pParm32->u.Pointer.u.linearAddr; + break; + + default: + rc = VERR_INVALID_PARAMETER; + LogRel(("VbglR0HGCMInternalCall32: pParm32 type %#x invalid.\n", pParm32->type)); + break; + } + if (RT_FAILURE(rc)) + break; + } + if (RT_SUCCESS(rc)) + { + rc = VbglR0HGCMInternalCall(pCallInfo64, sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter), fFlags, + fRequestor, pfnAsyncCallback, pvAsyncData, u32AsyncData); + + if (RT_SUCCESS(rc)) + { + *pCallInfo = *pCallInfo64; + + /* + * Copy back. + */ + pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo); + pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64); + for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++) + { + switch (pParm64->type) + { + case VMMDevHGCMParmType_32bit: + pParm32->u.value32 = pParm64->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + pParm32->u.value64 = pParm64->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + pParm32->u.Pointer.size = pParm64->u.Pointer.size; + break; + + default: + LogRel(("VbglR0HGCMInternalCall32: failed invalid pParm32 type %d\n", pParm32->type)); + rc = VERR_INTERNAL_ERROR_3; + break; + } + } + } + else + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall32: VbglR0HGCMInternalCall failed. rc=%Rrc\n", rc)); + } + } + else + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall32: failed. rc=%Rrc\n", rc)); + } + + RTMemTmpFree(pCallInfo64); + return rc; +} +#endif /* ARCH_BITS == 64 */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp new file mode 100644 index 00000000..c0a2e631 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp @@ -0,0 +1,85 @@ +/* $Id: VBoxGuestR0LibIdc-os2.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, OS/2 specific. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <VBox/err.h> +#include <VBox/log.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +RT_C_DECLS_BEGIN /* for watcom */ +/* This is defined in some assembly file. The AttachDD operation + is done in the driver init code. */ +extern VBGLOS2ATTACHDD g_VBoxGuestIDC; +RT_C_DECLS_END + + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + /* + * Just check whether the connection was made or not. + */ + if ( g_VBoxGuestIDC.u32Version == VBGL_IOC_VERSION + && RT_VALID_PTR(g_VBoxGuestIDC.u32Session) + && RT_VALID_PTR(g_VBoxGuestIDC.pfnServiceEP)) + return g_VBoxGuestIDC.pfnServiceEP(g_VBoxGuestIDC.u32Session, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); + Log(("vbglDriverOpen: failed\n")); + RT_NOREF(pHandle); + return VERR_FILE_NOT_FOUND; +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, uReq, pReqHdr, cbReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp new file mode 100644 index 00000000..66358645 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp @@ -0,0 +1,97 @@ +/* $Id: VBoxGuestR0LibIdc-solaris.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Solaris specific. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include <sys/conf.h> +#include <sys/sunldi.h> +#include <sys/file.h> +#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */ +#include "VBoxGuestR0LibInternal.h" +#include <VBox/err.h> + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + ldi_handle_t hDev = NULL; + ldi_ident_t hIdent = ldi_ident_from_anon(); + int rc = ldi_open_by_name((char *)VBOXGUEST_DEVICE_NAME, FREAD, kcred, &hDev, hIdent); + ldi_ident_release(hIdent); + if (rc == 0) + { + pHandle->s.hDev = hDev; + rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + return VINF_SUCCESS; + ldi_close(hDev, FREAD, kcred); + } + else + rc = VERR_OPEN_FAILED; + pHandle->s.hDev = NULL; + return rc; +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + int rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + { + ldi_close(pHandle->s.hDev, FREAD, kcred); + pHandle->s.hDev = NULL; + } + return rc; +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ +#if 0 + return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq); +#else + int iIgn; + int rc = ldi_ioctl(pHandle->s.hDev, uReq, (intptr_t)pReqHdr, FKIOCTL | FNATIVE, kcred, &iIgn); + if (rc == 0) + return VINF_SUCCESS; + return RTErrConvertFromErrno(rc); +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp new file mode 100644 index 00000000..810d7c80 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp @@ -0,0 +1,64 @@ +/* $Id: VBoxGuestR0LibIdc-unix.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, UNIX-like OSes. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + RT_NOREF(pHandle); + return VBoxGuestIDC(NULL /*pvSession*/, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + return VBoxGuestIDC(pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp new file mode 100644 index 00000000..54db8a4a --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp @@ -0,0 +1,208 @@ +/* $Id: VBoxGuestR0LibIdc-win.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Windows specific. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include <iprt/nt/nt.h> +#include "VBoxGuestR0LibInternal.h" +#include <VBox/VBoxGuest.h> +#include <iprt/errcore.h> +#include <VBox/log.h> + + +/** + * 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 vbglR0IdcNtCallInternal(PDEVICE_OBJECT pDeviceObject, PFILE_OBJECT pFileObject, uint32_t uReq, PVBGLREQHDR pReq) +{ + int rc; + NTSTATUS rcNt; + + /* + * Build the request. + * + * We want to avoid double buffering of the request, therefore we don't + * specify any request pointers or sizes when asking the kernel to build + * the IRP for us, but instead do that part our selves. + * + * See https://www.osr.com/blog/2018/02/14/beware-iobuilddeviceiocontrolrequest/ + * for how fun this is when we're not at IRQL PASSIVE (HACK ALERT futher down). + * Ran into this little issue when LoadLibraryEx on a .NET DLL using the + * LOAD_LIBRARY_AS_DATAFILE and LOAD_LIBRARY_AS_IMAGE_RESOURCE flags. + */ + KEVENT Event; + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + IO_STATUS_BLOCK IoStatusBlock = RTNT_IO_STATUS_BLOCK_INITIALIZER; +#if 0 + PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */ + pDeviceObject, + pReq, /* InputBuffer */ + pReq->cbIn, /* InputBufferLength */ + pReq, /* OutputBuffer */ + pReq->cbOut, /* OutputBufferLength */ + TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */ + &Event, /* Event */ + &IoStatusBlock); /* IoStatusBlock */ +#else + PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */ + pDeviceObject, + NULL, /* InputBuffer */ + 0, /* InputBufferLength */ + NULL, /* OutputBuffer */ + 0, /* OutputBufferLength */ + TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */ + &Event, /* Event */ + &IoStatusBlock); /* IoStatusBlock */ +#endif + if (pIrp) + { +#if 0 + IoGetNextIrpStackLocation(pIrp)->FileObject = pFileObject; +#else + //w2k3sp1+: if (!KeAreAllApcsDisabled()) + //w2k3sp1+: pIrp->Flags |= IRP_SYNCHRONOUS_API; + //w2k3sp1+: else + { + /* HACK ALERT! Causes IoCompleteRequest to update UserIosb and free the IRP w/o any APC happening. */ + pIrp->Flags |= IRP_SYNCHRONOUS_API | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO; + KIRQL bIrqlSaved; + KeRaiseIrql(APC_LEVEL, &bIrqlSaved); + RemoveEntryList(&pIrp->ThreadListEntry); + InitializeListHead(&pIrp->ThreadListEntry); + KeLowerIrql(bIrqlSaved); + } + pIrp->UserBuffer = pReq; + pIrp->AssociatedIrp.SystemBuffer = pReq; + PIO_STACK_LOCATION pStack = IoGetNextIrpStackLocation(pIrp); + pStack->FileObject = pFileObject; + pStack->Parameters.DeviceIoControl.OutputBufferLength = pReq->cbOut; + pStack->Parameters.DeviceIoControl.InputBufferLength = pReq->cbIn; +#endif + + /* + * 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, /* Alertable */ + NULL); /* TimeOut */ + if (NT_SUCCESS(rcNt)) + rcNt = IoStatusBlock.Status; + if (NT_SUCCESS(rcNt)) + rc = pReq->rc; + else + rc = RTErrConvertFromNtStatus(rcNt); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + PDEVICE_OBJECT pDeviceObject = NULL; + PFILE_OBJECT pFileObject = NULL; + UNICODE_STRING wszDeviceName; + NTSTATUS rcNt; + int rc; + + /* + * Get the device object pointer. + */ + RtlInitUnicodeString(&wszDeviceName, VBOXGUEST_DEVICE_NAME_NT); + rcNt = IoGetDeviceObjectPointer(&wszDeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); + if (NT_SUCCESS(rcNt)) + { + /* + * Make the connection call. + */ + rc = vbglR0IdcNtCallInternal(pDeviceObject, pFileObject, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.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 vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + PFILE_OBJECT pFileObject = pHandle->s.pFileObject; + int rc = vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pFileObject, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + { + pHandle->s.pDeviceObject = NULL; + pHandle->s.pFileObject = NULL; + ObDereferenceObject(pFileObject); + } + + return rc; +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + NOREF(cbReq); + return vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pHandle->s.pFileObject, (uint32_t)uReq, pReqHdr); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp new file mode 100644 index 00000000..6e194fcb --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp @@ -0,0 +1,205 @@ +/* $Id: VBoxGuestR0LibIdc.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <iprt/errcore.h> +#include <VBox/VBoxGuest.h> +/*#include <iprt/asm.h>*/ + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/*static PVBGLIDCHANDLE volatile g_pMainHandle = NULL;*/ + + +/** + * Opens the IDC interface of the support driver. + * + * This will perform basic version negotiations and fail if the + * minimum 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. + */ +DECLR0VBGL(int) VbglR0IdcOpen(PVBGLIDCHANDLE pHandle, uint32_t uReqVersion, uint32_t uMinVersion, + uint32_t *puSessionVersion, uint32_t *puDriverVersion, uint32_t *puDriverRevision) +{ + unsigned uDefaultMinVersion; + VBGLIOCIDCCONNECT Req; + int rc; + + /* + * Validate and set failure return values. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + pHandle->s.pvSession = 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)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER); + AssertReturn(!uReqVersion || (uReqVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_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 = VBGL_IOC_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. + */ + VBGLREQHDR_INIT(&Req.Hdr, IDC_CONNECT); + Req.u.In.u32MagicCookie = VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE; + Req.u.In.uMinVersion = uMinVersion; + Req.u.In.uReqVersion = uReqVersion; + Req.u.In.uReserved = 0; + rc = vbglR0IdcNativeOpen(pHandle, &Req); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + { + pHandle->s.pvSession = Req.u.Out.pvSession; + 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 ( RT_VALID_PTR(Req.u.Out.pvSession) + && Req.u.Out.uSessionVersion >= uMinVersion + && (Req.u.Out.uSessionVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000))) + { + /*ASMAtomicCmpXchgPtr(&g_pMainHandle, pHandle, NULL);*/ + return rc; + } + + AssertMsgFailed(("pSession=%p uSessionVersion=0x%x (r%u)\n", Req.u.Out.pvSession, Req.u.Out.uSessionVersion, Req.u.Out.uDriverRevision)); + rc = VERR_VERSION_MISMATCH; + VbglR0IdcClose(pHandle); + } + + return rc; +} + + +/** + * Closes a IDC connection established by VbglR0IdcOpen. + * + * @returns VBox status code. + * @param pHandle The IDC handle. + */ +DECLR0VBGL(int) VbglR0IdcClose(PVBGLIDCHANDLE pHandle) +{ + VBGLIOCIDCDISCONNECT Req; + int rc; + + /* + * Catch closed handles and check that the session is valid. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + if (!pHandle->s.pvSession) + return VERR_INVALID_HANDLE; + AssertPtrReturn(pHandle->s.pvSession, VERR_INVALID_HANDLE); + + /* + * Create the request and hand it to the OS specific code. + */ + VBGLREQHDR_INIT(&Req.Hdr, IDC_DISCONNECT); + Req.u.In.pvSession = pHandle->s.pvSession; + rc = vbglR0IdcNativeClose(pHandle, &Req); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + { + pHandle->s.pvSession = NULL; + /*ASMAtomicCmpXchgPtr(&g_pMainHandle, NULL, pHandle);*/ + } + return rc; +} + + +/** + * Makes an IDC call, returning the request status. + * + * @returns VBox status code. Request status if the I/O control succeeds, + * otherwise the I/O control failure status. + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCall(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + int rc = VbglR0IdcCallRaw(pHandle, uReq, pReqHdr, cbReq); + if (RT_SUCCESS(rc)) + rc = pReqHdr->rc; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp new file mode 100644 index 00000000..c0dba1c9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp @@ -0,0 +1,333 @@ +/* $Id: VBoxGuestR0LibInit.cpp $ */ +/** @file + * VBoxGuestLibR0 - Library initialization. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/semaphore.h> +#include <VBox/err.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The global VBGL instance data. */ +VBGLDATA g_vbgldata; + + +/** + * Used by vbglR0QueryDriverInfo and VbglInit to try get the host feature mask + * and version information (g_vbgldata::hostVersion). + * + * This was first implemented by the host in 3.1 and we quietly ignore failures + * for that reason. + */ +static void vbglR0QueryHostVersion(void) +{ + VMMDevReqHostVersion *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **) &pReq, sizeof (*pReq), VMMDevReq_GetHostVersion); + if (RT_SUCCESS(rc)) + { + rc = VbglR0GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + { + g_vbgldata.hostVersion = *pReq; + Log(("vbglR0QueryHostVersion: %u.%u.%ur%u %#x\n", + pReq->major, pReq->minor, pReq->build, pReq->revision, pReq->features)); + } + + VbglR0GRFree(&pReq->header); + } +} + + +#ifndef VBGL_VBOXGUEST +/** + * The guest library uses lazy initialization for VMMDev port and memory, + * because these values are provided by the VBoxGuest driver and it might + * be loaded later than other drivers. + * + * The VbglEnter checks the current library status, tries to retrieve these + * values and fails if they are unavailable. + */ +static int vbglR0QueryDriverInfo(void) +{ +# ifdef VBGLDATA_USE_FAST_MUTEX + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxIdcSetup); +# else + int rc = RTSemMutexRequest(g_vbgldata.hMtxIdcSetup, RT_INDEFINITE_WAIT); +# endif + if (RT_SUCCESS(rc)) + { + if (g_vbgldata.status == VbglStatusReady) + { /* likely */ } + else + { + rc = VbglR0IdcOpen(&g_vbgldata.IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*puDriverRevision*/); + if (RT_SUCCESS(rc)) + { + /* + * Try query the port info. + */ + VBGLIOCGETVMMDEVIOINFO PortInfo; + RT_ZERO(PortInfo); + VBGLREQHDR_INIT(&PortInfo.Hdr, GET_VMMDEV_IO_INFO); + rc = VbglR0IdcCall(&g_vbgldata.IdcHandle, VBGL_IOCTL_GET_VMMDEV_IO_INFO, &PortInfo.Hdr, sizeof(PortInfo)); + if (RT_SUCCESS(rc)) + { + dprintf(("Port I/O = 0x%04x, MMIO = %p\n", PortInfo.u.Out.IoPort, PortInfo.u.Out.pvVmmDevMapping)); + + g_vbgldata.portVMMDev = PortInfo.u.Out.IoPort; + g_vbgldata.pVMMDevMemory = (VMMDevMemory *)PortInfo.u.Out.pvVmmDevMapping; + g_vbgldata.status = VbglStatusReady; + + vbglR0QueryHostVersion(); + } + } + + dprintf(("vbglQueryDriverInfo rc = %Rrc\n", rc)); + } + +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexRelease(g_vbgldata.hMtxIdcSetup); +# else + RTSemMutexRelease(g_vbgldata.hMtxIdcSetup); +# endif + } + return rc; +} +#endif /* !VBGL_VBOXGUEST */ + +/** + * Checks if VBGL has been initialized. + * + * The client library, this will lazily complete the initialization. + * + * @return VINF_SUCCESS or VERR_VBGL_NOT_INITIALIZED. + */ +int vbglR0Enter(void) +{ + if (g_vbgldata.status == VbglStatusReady) + return VINF_SUCCESS; + +#ifndef VBGL_VBOXGUEST + if (g_vbgldata.status == VbglStatusInitializing) + { + vbglR0QueryDriverInfo(); + if (g_vbgldata.status == VbglStatusReady) + return VINF_SUCCESS; + } +#endif + return VERR_VBGL_NOT_INITIALIZED; +} + + +static int vbglR0InitCommon(void) +{ + int rc; + + RT_ZERO(g_vbgldata); + g_vbgldata.status = VbglStatusInitializing; + + rc = VbglR0PhysHeapInit(); + if (RT_SUCCESS(rc)) + { + dprintf(("vbglR0InitCommon: returns rc = %d\n", rc)); + return rc; + } + + LogRel(("vbglR0InitCommon: VbglR0PhysHeapInit failed: rc=%Rrc\n", rc)); + g_vbgldata.status = VbglStatusNotInitialized; + return rc; +} + + +static void vbglR0TerminateCommon(void) +{ + VbglR0PhysHeapTerminate(); + g_vbgldata.status = VbglStatusNotInitialized; +} + +#ifdef VBGL_VBOXGUEST + +DECLR0VBGL(int) VbglR0InitPrimary(RTIOPORT portVMMDev, VMMDevMemory *pVMMDevMemory, uint32_t *pfFeatures) +{ + int rc; + +# ifdef RT_OS_WINDOWS /** @todo r=bird: this doesn't make sense. Is there something special going on on windows? */ + dprintf(("vbglInit: starts g_vbgldata.status %d\n", g_vbgldata.status)); + + if ( g_vbgldata.status == VbglStatusInitializing + || g_vbgldata.status == VbglStatusReady) + { + /* Initialization is already in process. */ + return VINF_SUCCESS; + } +# else + dprintf(("vbglInit: starts\n")); +# endif + + rc = vbglR0InitCommon(); + if (RT_SUCCESS(rc)) + { + g_vbgldata.portVMMDev = portVMMDev; + g_vbgldata.pVMMDevMemory = pVMMDevMemory; + g_vbgldata.status = VbglStatusReady; + + vbglR0QueryHostVersion(); + *pfFeatures = g_vbgldata.hostVersion.features; + return VINF_SUCCESS; + } + + g_vbgldata.status = VbglStatusNotInitialized; + return rc; +} + +DECLR0VBGL(void) VbglR0TerminatePrimary(void) +{ + vbglR0TerminateCommon(); +} + + +#else /* !VBGL_VBOXGUEST */ + +DECLR0VBGL(int) VbglR0InitClient(void) +{ + int rc; + + /** @todo r=bird: explain why we need to be doing this, please... */ + if ( g_vbgldata.status == VbglStatusInitializing + || g_vbgldata.status == VbglStatusReady) + { + /* Initialization is already in process. */ + return VINF_SUCCESS; + } + + rc = vbglR0InitCommon(); + if (RT_SUCCESS(rc)) + { +# ifdef VBGLDATA_USE_FAST_MUTEX + rc = RTSemFastMutexCreate(&g_vbgldata.hMtxIdcSetup); +# else + rc = RTSemMutexCreate(&g_vbgldata.hMtxIdcSetup); +# endif + if (RT_SUCCESS(rc)) + { + /* Try to obtain VMMDev port via IOCTL to VBoxGuest main driver. */ + vbglR0QueryDriverInfo(); + +# ifdef VBOX_WITH_HGCM + rc = VbglR0HGCMInit(); +# endif + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX; +# else + RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX; +# endif + } + vbglR0TerminateCommon(); + } + + return rc; +} + +DECLR0VBGL(void) VbglR0TerminateClient(void) +{ +# ifdef VBOX_WITH_HGCM + VbglR0HGCMTerminate(); +# endif + + /* driver open could fail, which does not prevent VbglInit from succeeding, + * close the driver only if it is opened */ + VbglR0IdcClose(&g_vbgldata.IdcHandle); +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX; +# else + RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX; +# endif + + /* note: do vbglR0TerminateCommon as a last step since it zeroez up the g_vbgldata + * conceptually, doing vbglR0TerminateCommon last is correct + * since this is the reverse order to how init is done */ + vbglR0TerminateCommon(); +} + + +int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle) +{ + if (g_vbgldata.status == VbglStatusReady) + { /* likely */ } + else + { + vbglR0QueryDriverInfo(); + if (g_vbgldata.status != VbglStatusReady) + { + *ppIdcHandle = NULL; + return VERR_TRY_AGAIN; + } + } + + *ppIdcHandle = &g_vbgldata.IdcHandle; + return VINF_SUCCESS; +} + + +DECLR0VBGL(int) VbglR0QueryHostFeatures(uint32_t *pfHostFeatures) +{ + if (g_vbgldata.status == VbglStatusReady) + *pfHostFeatures = g_vbgldata.hostVersion.features; + else + { + int rc = vbglR0QueryDriverInfo(); + if (g_vbgldata.status != VbglStatusReady) + return rc; + *pfHostFeatures = g_vbgldata.hostVersion.features; + } + + return VINF_SUCCESS; +} + +#endif /* !VBGL_VBOXGUEST */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h new file mode 100644 index 00000000..6942a7e5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h @@ -0,0 +1,212 @@ +/* $Id: VBoxGuestR0LibInternal.h $ */ +/** @file + * VBoxGuestLibR0 - Internal header. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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. + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h +#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* + * Define the private IDC handle structure before we include the VBoxGuestLib.h header. + */ +#include <iprt/types.h> +#include <iprt/assert.h> +RT_C_DECLS_BEGIN + +# ifndef VBGL_VBOXGUEST +/** + * The hidden part of VBGLIDCHANDLE. + */ +struct VBGLIDCHANDLEPRIVATE +{ + /** Pointer to the session handle. */ + void *pvSession; +# if defined(RT_OS_WINDOWS) && (defined(IPRT_INCLUDED_nt_ntddk_h) || defined(IPRT_INCLUDED_nt_nt_h)) + /** Pointer to the NT device object. */ + PDEVICE_OBJECT pDeviceObject; + /** Pointer to the NT file object. */ + PFILE_OBJECT pFileObject; +# elif defined(RT_OS_SOLARIS) && defined(_SYS_SUNLDI_H) + /** LDI device handle to keep the device attached. */ + ldi_handle_t hDev; +# endif +}; +/** Indicate that the VBGLIDCHANDLEPRIVATE structure is present. */ +# define VBGLIDCHANDLEPRIVATE_DECLARED 1 +#endif + +#include <VBox/VMMDev.h> +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> + +#ifdef VBGLIDCHANDLEPRIVATE_DECLARED +AssertCompile(RT_SIZEOFMEMB(VBGLIDCHANDLE, apvPadding) >= sizeof(struct VBGLIDCHANDLEPRIVATE)); +#endif + + +/* + * Native IDC functions. + */ +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq); +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq); + + +/* + * Deprecated logging macro + */ +#include <VBox/log.h> +#ifdef RT_OS_WINDOWS /** @todo dprintf() -> Log() */ +# if (defined(DEBUG) && !defined(NO_LOGGING)) || defined(LOG_ENABLED) +# define dprintf(a) RTLogBackdoorPrintf a +# else +# define dprintf(a) do {} while (0) +# endif +#else +# define dprintf(a) Log(a) +#endif + +/* + * Lazy bird: OS/2 doesn't currently implement the RTSemMutex API in ring-0, so + * use a fast mutex instead. Unlike Windows, the OS/2 implementation + * doesn't have any nasty side effects on IRQL-like context properties, so the + * fast mutexes on OS/2 are identical to normal mutexes except for the missing + * timeout aspec. Fortunately we don't need timeouts here. + */ +#ifdef RT_OS_OS2 +# define VBGLDATA_USE_FAST_MUTEX +#endif + +struct VBGLPHYSHEAPBLOCK; +typedef struct VBGLPHYSHEAPBLOCK VBGLPHYSHEAPBLOCK; +struct VBGLPHYSHEAPFREEBLOCK; +typedef struct VBGLPHYSHEAPFREEBLOCK VBGLPHYSHEAPFREEBLOCK; +struct VBGLPHYSHEAPCHUNK; +typedef struct VBGLPHYSHEAPCHUNK VBGLPHYSHEAPCHUNK; + +enum VbglLibStatus +{ + VbglStatusNotInitialized = 0, + VbglStatusInitializing, + VbglStatusReady +}; + +/** + * Global VBGL ring-0 data. + * Lives in VBoxGuestR0LibInit.cpp. + */ +typedef struct VBGLDATA +{ + enum VbglLibStatus status; + + RTIOPORT portVMMDev; + + VMMDevMemory *pVMMDevMemory; + + /** Physical memory heap data. + * @{ + */ + RTSEMFASTMUTEX hMtxHeap; + /** Head of the block list (both free and allocated blocks). + * This runs parallel to the chunk list and is sorted by address within each + * chunk. This allows merging with blocks both after and before when + * freeing. */ + VBGLPHYSHEAPBLOCK *pBlockHead; + /** Head of the free list. + * This is not sorted and more on the most recently freed approach. */ + VBGLPHYSHEAPFREEBLOCK *pFreeHead; + /** Number of block of any kind. */ + int32_t cBlocks; + /** Number of free blocks. */ + int32_t cFreeBlocks; + /** Head of the chunk list. */ + VBGLPHYSHEAPCHUNK *pChunkHead; + /** @} */ + + /** + * The host version data. + */ + VMMDevReqHostVersion hostVersion; + + +#ifndef VBGL_VBOXGUEST + /** The IDC handle. This is used for talking to the main driver. */ + VBGLIDCHANDLE IdcHandle; + /** Mutex used to serialize IDC setup. */ +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSEMFASTMUTEX hMtxIdcSetup; +# else + RTSEMMUTEX hMtxIdcSetup; +# endif +#endif +} VBGLDATA; + + +extern VBGLDATA g_vbgldata; + +/** + * Internal macro for checking whether we can pass physical page lists to the + * host. + * + * ASSUMES that vbglR0Enter has been called already. + * + * @param a_fLocked For the windows shared folders workarounds. + * + * @remarks Disabled the PageList feature for locked memory on Windows, + * because a new MDL is created by VBGL to get the page addresses + * and the pages from the MDL are marked as dirty when they should not. + */ +#if defined(RT_OS_WINDOWS) +# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ + ( !(a_fLocked) && (g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) +#else +# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ + ( !!(g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) +#endif + +int vbglR0Enter (void); + +#ifdef VBOX_WITH_HGCM +struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void); +void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle); +#endif /* VBOX_WITH_HGCM */ + +#ifndef VBGL_VBOXGUEST +/** + * Get the IDC handle to the main VBoxGuest driver. + * @returns VERR_TRY_AGAIN if the main driver has not yet been loaded. + */ +int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle); +#endif + +RT_C_DECLS_END + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp new file mode 100644 index 00000000..7ce27a8f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp @@ -0,0 +1,130 @@ +/* $Id: VBoxGuestR0LibMouse.cpp $ */ +/** @file + * VBoxGuestLibR0 - Mouse Integration. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/** + * Sets the function which is called back on each mouse pointer event. Only + * one callback can be active at once, so if you need several for any reason + * you must multiplex yourself. Call backs can be disabled by passing NULL + * as the function pointer. + * + * @remarks Ring-0. + * @returns iprt status code. + * @returns VERR_TRY_AGAIN if the main guest driver hasn't finished + * initialising. + * + * @param pfnNotify the function to call back. NULL to disable call backs. + * @param pvUser user supplied data/cookie to be passed to the function. + */ +DECLR0VBGL(int) VbglR0SetMouseNotifyCallback(PFNVBOXGUESTMOUSENOTIFY pfnNotify, void *pvUser) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VBGLIOCSETMOUSENOTIFYCALLBACK NotifyCallback; + VBGLREQHDR_INIT(&NotifyCallback.Hdr, SET_MOUSE_NOTIFY_CALLBACK); + NotifyCallback.u.In.pfnNotify = pfnNotify; + NotifyCallback.u.In.pvUser = pvUser; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK, &NotifyCallback.Hdr, sizeof(NotifyCallback)); + } + return rc; +} + + +/** + * Retrieve mouse coordinates and features from the host. + * + * @remarks Ring-0. + * @returns VBox status code. + * + * @param pfFeatures Where to store the mouse features. + * @param px Where to store the X co-ordinate. + * @param py Where to store the Y co-ordinate. + */ +DECLR0VBGL(int) VbglR0GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VMMDevReqMouseStatus Req; + VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetMouseStatus); + Req.mouseFeatures = 0; + Req.pointerXPos = 0; + Req.pointerYPos = 0; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_VMMDEV_REQUEST(sizeof(Req)), (PVBGLREQHDR)&Req.header, sizeof(Req)); + if (RT_SUCCESS(rc)) + { + if (pfFeatures) + *pfFeatures = Req.mouseFeatures; + if (px) + *px = Req.pointerXPos; + if (py) + *py = Req.pointerYPos; + } + } + return rc; +} + + +/** + * Send mouse features to the host. + * + * @remarks Ring-0. + * @returns VBox status code. + * + * @param fFeatures Supported mouse pointer features. The main guest driver + * will mediate different callers and show the host any + * feature enabled by any guest caller. + */ +DECLR0VBGL(int) VbglR0SetMouseStatus(uint32_t fFeatures) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VBGLIOCSETMOUSESTATUS Req; + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fFeatures; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req)); + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp new file mode 100644 index 00000000..176124e3 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp @@ -0,0 +1,1197 @@ +/* $Id: VBoxGuestR0LibPhysHeap.cpp $ */ +/** @file + * VBoxGuestLibR0 - Physical memory heap. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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. + */ + +/** @page pg_vbglr0_phys_heap VBoxGuestLibR0 - Physical memory heap. + * + * Traditional heap implementation keeping all blocks in a ordered list and + * keeping free blocks on additional list via pointers in the user area. This + * is similar to @ref grp_rt_heap_simple "RTHeapSimple" and + * @ref grp_rt_heap_offset "RTHeapOffset" in IPRT, except that this code handles + * mutiple chunks and has a physical address associated with each chunk and + * block. The alignment is fixed (VBGL_PH_ALLOC_ALIGN). + * + * When allocating memory, a free block is found that satisfies the request, + * extending the heap with another chunk if needed. The block is split if it's + * too large, and the tail end is put on the free list. + * + * When freeing memory, the block being freed is put back on the free list and + * we use the block list to check whether it can be merged with adjacent blocks. + * + * @note The original code managed the blocks in two separate lists for free and + * allocated blocks, which had the disadvantage only allowing merging with + * the block after the block being freed. On the plus side, it had the + * potential for slightly better locality when examining the free list, + * since the next pointer and block size members were closer to one + * another. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/semaphore.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Enables heap dumping. */ +#if defined(DOXYGEN_RUNNING) || 0 +# define VBGL_PH_DUMPHEAP +#endif + +#ifdef VBGL_PH_DUMPHEAP +# define VBGL_PH_DPRINTF(a) RTAssertMsg2Weak a +#else +# define VBGL_PH_DPRINTF(a) do { } while (0) +#endif + +/** Heap chunk signature */ +#define VBGL_PH_CHUNKSIGNATURE UINT32_C(0xADDCCCCC) +/** Heap chunk allocation unit */ +#define VBGL_PH_CHUNKSIZE (0x10000) + +/** Heap block signature */ +#define VBGL_PH_BLOCKSIGNATURE UINT32_C(0xADDBBBBB) + +/** The allocation block alignment. + * + * This cannot be larger than VBGLPHYSHEAPBLOCK. + */ +#define VBGL_PH_ALLOC_ALIGN (sizeof(void *)) + +/** Max number of free nodes to search before just using the best fit. + * + * This is used to limit the free list walking during allocation and just get + * on with the job. A low number should reduce the cache trashing at the + * possible cost of heap fragmentation. + * + * Picked 16 after comparing the tstVbglR0PhysHeap-1 results w/ uRandSeed=42 for + * different max values. + */ +#define VBGL_PH_MAX_FREE_SEARCH 16 + +/** Threshold to stop the block search if a free block is at least this much too big. + * + * May cause more fragmation (depending on usage pattern), but should speed up + * allocation and hopefully reduce cache trashing. + * + * Since we merge adjacent free blocks when we can, free blocks should typically + * be a lot larger that what's requested. So, it is probably a good idea to + * just chop up a large block rather than keep searching for a perfect-ish + * match. + * + * Undefine this to disable this trick. + */ +#if defined(DOXYGEN_RUNNING) || 1 +# define VBGL_PH_STOP_SEARCH_AT_EXCESS _4K +#endif + +/** Threshold at which to split out a tail free block when allocating. + * + * The value gives the amount of user space, i.e. excluding the header. + * + * Using 32 bytes based on VMMDev.h request sizes. The smallest requests are 24 + * bytes, i.e. only the header, at least 4 of these. There are at least 10 with + * size 28 bytes and at least 11 with size 32 bytes. So, 32 bytes would fit + * some 25 requests out of about 60, which is reasonable. + */ +#define VBGL_PH_MIN_SPLIT_FREE_BLOCK 32 + + +/** The smallest amount of user data that can be allocated. + * + * This is to ensure that the block can be converted into a + * VBGLPHYSHEAPFREEBLOCK structure when freed. This must be smaller or equal + * to VBGL_PH_MIN_SPLIT_FREE_BLOCK. + */ +#define VBGL_PH_SMALLEST_ALLOC_SIZE 16 + +/** The maximum allocation request size. */ +#define VBGL_PH_LARGEST_ALLOC_SIZE RT_ALIGN_32( _128M \ + - sizeof(VBGLPHYSHEAPBLOCK) \ + - sizeof(VBGLPHYSHEAPCHUNK) \ + - VBGL_PH_ALLOC_ALIGN, \ + VBGL_PH_ALLOC_ALIGN) + +/** + * Whether to use the RTR0MemObjAllocCont API or RTMemContAlloc for + * allocating chunks. + * + * This can be enabled on hosts where RTMemContAlloc is more complicated than + * RTR0MemObjAllocCont. This can also be done if we wish to save code space, as + * the latter is typically always dragged into the link on guests where the + * linker cannot eliminiate functions within objects. Only drawback is that + * RTR0MemObjAllocCont requires another heap allocation for the handle. + */ +#if defined(DOXYGEN_RUNNING) || (!defined(IN_TESTCASE) && 0) +# define VBGL_PH_USE_MEMOBJ +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * A heap block (within a chunk). + * + * This is used to track a part of a heap chunk that's either free or + * allocated. The VBGLPHYSHEAPBLOCK::fAllocated member indicates which it is. + */ +struct VBGLPHYSHEAPBLOCK +{ + /** Magic value (VBGL_PH_BLOCKSIGNATURE). */ + uint32_t u32Signature; + + /** Size of user data in the block. Does not include this block header. */ + uint32_t cbUser : 31; + /** The top bit indicates whether it's allocated or free. */ + uint32_t fAllocated : 1; + + /** Pointer to the next block on the list. */ + VBGLPHYSHEAPBLOCK *pNext; + /** Pointer to the previous block on the list. */ + VBGLPHYSHEAPBLOCK *pPrev; + /** Pointer back to the chunk. */ + VBGLPHYSHEAPCHUNK *pChunk; +}; + +/** + * A free block. + */ +struct VBGLPHYSHEAPFREEBLOCK +{ + /** Core block data. */ + VBGLPHYSHEAPBLOCK Core; + /** Pointer to the next free list entry. */ + VBGLPHYSHEAPFREEBLOCK *pNextFree; + /** Pointer to the previous free list entry. */ + VBGLPHYSHEAPFREEBLOCK *pPrevFree; +}; +AssertCompile(VBGL_PH_SMALLEST_ALLOC_SIZE >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK)); +AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK)); +AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= VBGL_PH_SMALLEST_ALLOC_SIZE); + +/** + * A chunk of memory used by the heap for sub-allocations. + * + * There is a list of these. + */ +struct VBGLPHYSHEAPCHUNK +{ + /** Magic value (VBGL_PH_CHUNKSIGNATURE). */ + uint32_t u32Signature; + + /** Size of the chunk. Includes the chunk header. */ + uint32_t cbChunk; + + /** Physical address of the chunk (contiguous). */ + uint32_t physAddr; + +#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32 + uint32_t uPadding1; +#endif + + /** Number of block of any kind. */ + int32_t cBlocks; + /** Number of free blocks. */ + int32_t cFreeBlocks; + + /** Pointer to the next chunk. */ + VBGLPHYSHEAPCHUNK *pNext; + /** Pointer to the previous chunk. */ + VBGLPHYSHEAPCHUNK *pPrev; + +#if defined(VBGL_PH_USE_MEMOBJ) + /** The allocation handle. */ + RTR0MEMOBJ hMemObj; +#endif + +#if ARCH_BITS == 64 + /** Pad the size up to 64 bytes. */ +# ifdef VBGL_PH_USE_MEMOBJ + uintptr_t auPadding2[2]; +# else + uintptr_t auPadding2[3]; +# endif +#endif +}; +#if ARCH_BITS == 64 +AssertCompileSize(VBGLPHYSHEAPCHUNK, 64); +#endif + + +/** + * Debug function that dumps the heap. + */ +#ifndef VBGL_PH_DUMPHEAP +# define dumpheap(pszWhere) do { } while (0) +#else +static void dumpheap(const char *pszWhere) +{ + VBGL_PH_DPRINTF(("VBGL_PH dump at '%s'\n", pszWhere)); + + VBGL_PH_DPRINTF(("Chunks:\n")); + for (VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead; pChunk; pChunk = pChunk->pNext) + VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, cBlocks = %8d, cFreeBlocks=%8d, phys = %08X\n", + pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbChunk, + pChunk->cBlocks, pChunk->cFreeBlocks, pChunk->physAddr)); + + VBGL_PH_DPRINTF(("Allocated blocks:\n")); + for (VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pBlockHead; pBlock; pBlock = pBlock->pNext) + VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, size = %05x, sign = %08X, %s, pChunk = %p\n", + pBlock, pBlock->pNext, pBlock->pPrev, pBlock->cbUser, + pBlock->u32Signature, pBlock->fAllocated ? "allocated" : " free", pBlock->pChunk)); + + VBGL_PH_DPRINTF(("Free blocks:\n")); + for (VBGLPHYSHEAPFREEBLOCK *pBlock = g_vbgldata.pFreeHead; pBlock; pBlock = pBlock->pNextFree) + VBGL_PH_DPRINTF(("%p: pNextFree = %p, pPrevFree = %p, size = %05x, sign = %08X, pChunk = %p%s\n", + pBlock, pBlock->pNextFree, pBlock->pPrevFree, pBlock->Core.cbUser, + pBlock->Core.u32Signature, pBlock->Core.pChunk, + !pBlock->Core.fAllocated ? "" : " !!allocated-block-on-freelist!!")); + + VBGL_PH_DPRINTF(("VBGL_PH dump at '%s' done\n", pszWhere)); +} +#endif + + +/** + * Initialize a free block + */ +static void vbglPhysHeapInitFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbUser) +{ + Assert(pBlock != NULL); + Assert(pChunk != NULL); + + pBlock->Core.u32Signature = VBGL_PH_BLOCKSIGNATURE; + pBlock->Core.cbUser = cbUser; + pBlock->Core.fAllocated = false; + pBlock->Core.pNext = NULL; + pBlock->Core.pPrev = NULL; + pBlock->Core.pChunk = pChunk; + pBlock->pNextFree = NULL; + pBlock->pPrevFree = NULL; +} + + +/** + * Updates block statistics when a block is added. + */ +DECLINLINE(void) vbglPhysHeapStatsBlockAdded(VBGLPHYSHEAPBLOCK *pBlock) +{ + g_vbgldata.cBlocks += 1; + pBlock->pChunk->cBlocks += 1; + AssertMsg((uint32_t)pBlock->pChunk->cBlocks <= pBlock->pChunk->cbChunk / sizeof(VBGLPHYSHEAPFREEBLOCK), + ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks)); +} + + +/** + * Links @a pBlock onto the head of block list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertBlock(VBGLPHYSHEAPBLOCK *pBlock) +{ + AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext)); + AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev)); + + /* inserting to head of list */ + VBGLPHYSHEAPBLOCK *pOldHead = g_vbgldata.pBlockHead; + + pBlock->pNext = pOldHead; + pBlock->pPrev = NULL; + + if (pOldHead) + pOldHead->pPrev = pBlock; + g_vbgldata.pBlockHead = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsBlockAdded(pBlock); +} + + +/** + * Links @a pBlock onto the block list after @a pInsertAfter. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertBlockAfter(VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPBLOCK *pInsertAfter) +{ + AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext)); + AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev)); + + pBlock->pNext = pInsertAfter->pNext; + pBlock->pPrev = pInsertAfter; + + if (pInsertAfter->pNext) + pInsertAfter->pNext->pPrev = pBlock; + + pInsertAfter->pNext = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsBlockAdded(pBlock); +} + + +/** + * Unlinks @a pBlock from the block list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapUnlinkBlock(VBGLPHYSHEAPBLOCK *pBlock) +{ + VBGLPHYSHEAPBLOCK *pOtherBlock = pBlock->pNext; + if (pOtherBlock) + pOtherBlock->pPrev = pBlock->pPrev; + /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */ + + pOtherBlock = pBlock->pPrev; + if (pOtherBlock) + pOtherBlock->pNext = pBlock->pNext; + else + { + Assert(g_vbgldata.pBlockHead == pBlock); + g_vbgldata.pBlockHead = pBlock->pNext; + } + + pBlock->pNext = NULL; + pBlock->pPrev = NULL; + + /* Update the stats: */ + g_vbgldata.cBlocks -= 1; + pBlock->pChunk->cBlocks -= 1; + AssertMsg(pBlock->pChunk->cBlocks >= 0, + ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks)); + Assert(g_vbgldata.cBlocks >= 0); +} + + + +/** + * Updates statistics after adding a free block. + */ +DECLINLINE(void) vbglPhysHeapStatsFreeBlockAdded(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + g_vbgldata.cFreeBlocks += 1; + pBlock->Core.pChunk->cFreeBlocks += 1; +} + + +/** + * Links @a pBlock onto head of the free chain. + * + * This is used during block freeing and when adding a new chunk. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + Assert(!pBlock->Core.fAllocated); + AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree)); + AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree)); + + /* inserting to head of list */ + VBGLPHYSHEAPFREEBLOCK *pOldHead = g_vbgldata.pFreeHead; + + pBlock->pNextFree = pOldHead; + pBlock->pPrevFree = NULL; + + if (pOldHead) + pOldHead->pPrevFree = pBlock; + g_vbgldata.pFreeHead = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsFreeBlockAdded(pBlock); +} + + +/** + * Links @a pBlock after @a pInsertAfter. + * + * This is used when splitting a free block during allocation to preserve the + * place in the free list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertFreeBlockAfter(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPFREEBLOCK *pInsertAfter) +{ + Assert(!pBlock->Core.fAllocated); + AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree)); + AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree)); + + /* inserting after the tiven node */ + pBlock->pNextFree = pInsertAfter->pNextFree; + pBlock->pPrevFree = pInsertAfter; + + if (pInsertAfter->pNextFree) + pInsertAfter->pNextFree->pPrevFree = pBlock; + + pInsertAfter->pNextFree = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsFreeBlockAdded(pBlock); +} + + +/** + * Unlinks @a pBlock from the free list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapUnlinkFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + Assert(!pBlock->Core.fAllocated); + + VBGLPHYSHEAPFREEBLOCK *pOtherBlock = pBlock->pNextFree; + if (pOtherBlock) + pOtherBlock->pPrevFree = pBlock->pPrevFree; + /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */ + + pOtherBlock = pBlock->pPrevFree; + if (pOtherBlock) + pOtherBlock->pNextFree = pBlock->pNextFree; + else + { + Assert(g_vbgldata.pFreeHead == pBlock); + g_vbgldata.pFreeHead = pBlock->pNextFree; + } + + pBlock->pNextFree = NULL; + pBlock->pPrevFree = NULL; + + /* Update the stats: */ + g_vbgldata.cFreeBlocks -= 1; + pBlock->Core.pChunk->cFreeBlocks -= 1; + AssertMsg(pBlock->Core.pChunk->cFreeBlocks >= 0, + ("pChunk=%p: cbChunk=%#x cFreeBlocks=%d\n", + pBlock->Core.pChunk, pBlock->Core.pChunk->cbChunk, pBlock->Core.pChunk->cFreeBlocks)); + Assert(g_vbgldata.cFreeBlocks >= 0); +} + + +/** + * Allocate another chunk and add it to the heap. + * + * @returns Pointer to the free block in the new chunk on success, NULL on + * allocation failure. + * @param cbMinBlock The size of the user block we need this chunk for. + */ +static VBGLPHYSHEAPFREEBLOCK *vbglPhysHeapChunkAlloc(uint32_t cbMinBlock) +{ + RTCCPHYS PhysAddr = NIL_RTHCPHYS; + VBGLPHYSHEAPCHUNK *pChunk; + uint32_t cbChunk; +#ifdef VBGL_PH_USE_MEMOBJ + RTR0MEMOBJ hMemObj = NIL_RTR0MEMOBJ; + int rc; +#endif + + VBGL_PH_DPRINTF(("Allocating new chunk for %#x byte allocation\n", cbMinBlock)); + AssertReturn(cbMinBlock <= VBGL_PH_LARGEST_ALLOC_SIZE, NULL); /* paranoia */ + + /* + * Compute the size of the new chunk, rounding up to next chunk size, + * which must be power of 2. + * + * Note! Using VBGLPHYSHEAPFREEBLOCK here means the minimum block size is + * 8 or 16 bytes too high, but safer this way since cbMinBlock is + * zero during the init code call. + */ + Assert(RT_IS_POWER_OF_TWO(VBGL_PH_CHUNKSIZE)); + cbChunk = cbMinBlock + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK); + cbChunk = RT_ALIGN_32(cbChunk, VBGL_PH_CHUNKSIZE); + + /* + * This function allocates physical contiguous memory below 4 GB. This 4GB + * limitation stems from using a 32-bit OUT instruction to pass a block + * physical address to the host. + */ +#ifdef VBGL_PH_USE_MEMOBJ + rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/); + pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL); +#else + pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk); +#endif + if (!pChunk) + { + /* If the allocation fail, halv the size till and try again. */ + uint32_t cbMinChunk = RT_MAX(cbMinBlock, PAGE_SIZE / 2) + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK); + cbMinChunk = RT_ALIGN_32(cbMinChunk, PAGE_SIZE); + if (cbChunk > cbMinChunk) + do + { + cbChunk >>= 2; + cbChunk = RT_ALIGN_32(cbChunk, PAGE_SIZE); +#ifdef VBGL_PH_USE_MEMOBJ + rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/); + pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL); +#else + pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk); +#endif + } while (!pChunk && cbChunk > cbMinChunk); + } + if (pChunk) + { + VBGLPHYSHEAPCHUNK *pOldHeadChunk; + VBGLPHYSHEAPFREEBLOCK *pBlock; + AssertRelease(PhysAddr < _4G && PhysAddr + cbChunk <= _4G); + + /* + * Init the new chunk. + */ + pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE; + pChunk->cbChunk = cbChunk; + pChunk->physAddr = (uint32_t)PhysAddr; + pChunk->cBlocks = 0; + pChunk->cFreeBlocks = 0; + pChunk->pNext = NULL; + pChunk->pPrev = NULL; +#ifdef VBGL_PH_USE_MEMOBJ + pChunk->hMemObj = hMemObj; +#endif + + /* Initialize the padding too: */ +#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32 + pChunk->uPadding1 = UINT32_C(0xADDCAAA1); +#endif +#if ARCH_BITS == 64 + pChunk->auPadding2[0] = UINT64_C(0xADDCAAA3ADDCAAA2); + pChunk->auPadding2[1] = UINT64_C(0xADDCAAA5ADDCAAA4); +# ifndef VBGL_PH_USE_MEMOBJ + pChunk->auPadding2[2] = UINT64_C(0xADDCAAA7ADDCAAA6); +# endif +#endif + + /* + * Initialize the free block, which now occupies entire chunk. + */ + pBlock = (VBGLPHYSHEAPFREEBLOCK *)(pChunk + 1); + vbglPhysHeapInitFreeBlock(pBlock, pChunk, cbChunk - sizeof(VBGLPHYSHEAPCHUNK) - sizeof(VBGLPHYSHEAPBLOCK)); + vbglPhysHeapInsertBlock(&pBlock->Core); + vbglPhysHeapInsertFreeBlock(pBlock); + + /* + * Add the chunk to the list. + */ + pOldHeadChunk = g_vbgldata.pChunkHead; + pChunk->pNext = pOldHeadChunk; + if (pOldHeadChunk) + pOldHeadChunk->pPrev = pChunk; + g_vbgldata.pChunkHead = pChunk; + + VBGL_PH_DPRINTF(("Allocated chunk %p LB %#x, block %p LB %#x\n", pChunk, cbChunk, pBlock, pBlock->Core.cbUser)); + return pBlock; + } + LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u (%#x) contiguous bytes.\n", cbChunk, cbChunk)); + return NULL; +} + + +/** + * Deletes a chunk: Unlinking all its blocks and freeing its memory. + */ +static void vbglPhysHeapChunkDelete(VBGLPHYSHEAPCHUNK *pChunk) +{ + uintptr_t uEnd, uCur; + Assert(pChunk != NULL); + AssertMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, ("pChunk->u32Signature = %08X\n", pChunk->u32Signature)); + + VBGL_PH_DPRINTF(("Deleting chunk %p size %x\n", pChunk, pChunk->cbChunk)); + + /* + * First scan the chunk and unlink all blocks from the lists. + * + * Note! We could do this by finding the first and last block list entries + * and just drop the whole chain relating to this chunk, rather than + * doing it one by one. But doing it one by one is simpler and will + * continue to work if the block list ends in an unsorted state. + */ + uEnd = (uintptr_t)pChunk + pChunk->cbChunk; + uCur = (uintptr_t)(pChunk + 1); + + while (uCur < uEnd) + { + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)uCur; + Assert(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE); + Assert(pBlock->pChunk == pChunk); + + uCur += pBlock->cbUser + sizeof(VBGLPHYSHEAPBLOCK); + Assert(uCur == (uintptr_t)pBlock->pNext || uCur >= uEnd); + + if (!pBlock->fAllocated) + vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pBlock); + vbglPhysHeapUnlinkBlock(pBlock); + } + + AssertMsg(uCur == uEnd, ("uCur = %p, uEnd = %p, pChunk->cbChunk = %08X\n", uCur, uEnd, pChunk->cbChunk)); + + /* + * Unlink the chunk from the chunk list. + */ + if (pChunk->pNext) + pChunk->pNext->pPrev = pChunk->pPrev; + /* else: we do not maintain tail pointer. */ + + if (pChunk->pPrev) + pChunk->pPrev->pNext = pChunk->pNext; + else + { + Assert(g_vbgldata.pChunkHead == pChunk); + g_vbgldata.pChunkHead = pChunk->pNext; + } + + /* + * Finally, free the chunk memory. + */ +#ifdef VBGL_PH_USE_MEMOBJ + RTR0MemObjFree(pChunk->hMemObj, true /*fFreeMappings*/); +#else + RTMemContFree(pChunk, pChunk->cbChunk); +#endif +} + + +DECLR0VBGL(void *) VbglR0PhysHeapAlloc(uint32_t cb) +{ + VBGLPHYSHEAPFREEBLOCK *pBlock; + VBGLPHYSHEAPFREEBLOCK *pIter; + int32_t cLeft; +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + uint32_t cbAlwaysSplit; +#endif + int rc; + + /* + * Make sure we don't allocate anything too small to turn into a free node + * and align the size to prevent pointer misalignment and whatnot. + */ + cb = RT_MAX(cb, VBGL_PH_SMALLEST_ALLOC_SIZE); + cb = RT_ALIGN_32(cb, VBGL_PH_ALLOC_ALIGN); + AssertCompile(VBGL_PH_ALLOC_ALIGN <= sizeof(pBlock->Core)); + + rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, NULL); + + dumpheap("pre alloc"); + + /* + * Search the free list. We do this in linear fashion as we don't expect + * there to be many blocks in the heap. + */ +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + cbAlwaysSplit = cb + VBGL_PH_STOP_SEARCH_AT_EXCESS; +#endif + cLeft = VBGL_PH_MAX_FREE_SEARCH; + pBlock = NULL; + if (cb <= PAGE_SIZE / 4 * 3) + { + /* Smaller than 3/4 page: Prefer a free block that can keep the request within a single page, + so HGCM processing in VMMDev can use page locks instead of several reads and writes. */ + VBGLPHYSHEAPFREEBLOCK *pFallback = NULL; + for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--) + { + AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + if (pIter->Core.cbUser >= cb) + { + if (pIter->Core.cbUser == cb) + { + if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb) + { + pBlock = pIter; + break; + } + pFallback = pIter; + } + else + { + if (!pFallback || pIter->Core.cbUser < pFallback->Core.cbUser) + pFallback = pIter; + if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb) + { + if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser) + pBlock = pIter; +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + else if (pIter->Core.cbUser >= cbAlwaysSplit) + { + pBlock = pIter; + break; + } +#endif + } + } + + if (cLeft > 0) + { /* likely */ } + else + break; + } + } + + if (!pBlock) + pBlock = pFallback; + } + else + { + /* Large than 3/4 page: Find closest free list match. */ + for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--) + { + AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + if (pIter->Core.cbUser >= cb) + { + if (pIter->Core.cbUser == cb) + { + /* Exact match - we're done! */ + pBlock = pIter; + break; + } + +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + if (pIter->Core.cbUser >= cbAlwaysSplit) + { + /* Really big block - no point continue searching! */ + pBlock = pIter; + break; + } +#endif + /* Looking for a free block with nearest size. */ + if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser) + pBlock = pIter; + + if (cLeft > 0) + { /* likely */ } + else + break; + } + } + } + + if (!pBlock) + { + /* No free blocks, allocate a new chunk, the only free block of the + chunk will be returned. */ + pBlock = vbglPhysHeapChunkAlloc(cb); + } + + if (pBlock) + { + /* We have a free block, either found or allocated. */ + AssertMsg(pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE, + ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->Core.u32Signature)); + AssertMsg(!pBlock->Core.fAllocated, ("pBlock = %p\n", pBlock)); + + /* + * If the block is too large, split off a free block with the unused space. + * + * We do this before unlinking the block so we can preserve the location + * in the free list. + * + * Note! We cannot split off and return the tail end here, because that may + * violate the same page requirements for requests smaller than 3/4 page. + */ + AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(*pBlock) - sizeof(pBlock->Core)); + if (pBlock->Core.cbUser >= sizeof(VBGLPHYSHEAPBLOCK) * 2 + VBGL_PH_MIN_SPLIT_FREE_BLOCK + cb) + { + pIter = (VBGLPHYSHEAPFREEBLOCK *)((uintptr_t)(&pBlock->Core + 1) + cb); + vbglPhysHeapInitFreeBlock(pIter, pBlock->Core.pChunk, pBlock->Core.cbUser - cb - sizeof(VBGLPHYSHEAPBLOCK)); + + pBlock->Core.cbUser = cb; + + /* Insert the new 'pIter' block after the 'pBlock' in the block list + and in the free list. */ + vbglPhysHeapInsertBlockAfter(&pIter->Core, &pBlock->Core); + vbglPhysHeapInsertFreeBlockAfter(pIter, pBlock); + } + + /* + * Unlink the block from the free list and mark it as allocated. + */ + vbglPhysHeapUnlinkFreeBlock(pBlock); + pBlock->Core.fAllocated = true; + + dumpheap("post alloc"); + + /* + * Return success. + */ + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + + VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns %p size %x\n", pBlock + 1, pBlock->Core.cbUser)); + return &pBlock->Core + 1; + } + + /* + * Return failure. + */ + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + AssertRC(rc); + + VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns NULL (requested %#x bytes)\n", cb)); + return NULL; +} + + +DECLR0VBGL(uint32_t) VbglR0PhysHeapGetPhysAddr(void *pv) +{ + /* + * Validate the incoming pointer. + */ + if (pv != NULL) + { + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)pv - 1; + if ( pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE + && pBlock->fAllocated) + { + /* + * Calculate and return its physical address. + */ + VBGLPHYSHEAPCHUNK *pChunk = pBlock->pChunk; + return pChunk->physAddr + (uint32_t)((uintptr_t)pv - (uintptr_t)pChunk); + } + + AssertMsgFailed(("Use after free or corrupt pointer variable: pv=%p pBlock=%p: u32Signature=%#x cb=%#x fAllocated=%d\n", + pv, pBlock, pBlock->u32Signature, pBlock->cbUser, pBlock->fAllocated)); + } + else + AssertMsgFailed(("Unexpected NULL pointer\n")); + return 0; +} + + +DECLR0VBGL(void) VbglR0PhysHeapFree(void *pv) +{ + if (pv != NULL) + { + VBGLPHYSHEAPFREEBLOCK *pBlock; + + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturnVoid(rc); + + dumpheap("pre free"); + + /* + * Validate the block header. + */ + pBlock = (VBGLPHYSHEAPFREEBLOCK *)((VBGLPHYSHEAPBLOCK *)pv - 1); + if ( pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE + && pBlock->Core.fAllocated + && pBlock->Core.cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE) + { + VBGLPHYSHEAPCHUNK *pChunk; + VBGLPHYSHEAPBLOCK *pNeighbour; + + /* + * Change the block status to freeed. + */ + VBGL_PH_DPRINTF(("VbglR0PhysHeapFree: %p size %#x\n", pv, pBlock->Core.cbUser)); + + pBlock->Core.fAllocated = false; + pBlock->pNextFree = pBlock->pPrevFree = NULL; + vbglPhysHeapInsertFreeBlock(pBlock); + + dumpheap("post insert"); + + /* + * Check if the block after this one is also free and we can merge it into this one. + */ + pChunk = pBlock->Core.pChunk; + + pNeighbour = pBlock->Core.pNext; + if ( pNeighbour + && !pNeighbour->fAllocated + && pNeighbour->pChunk == pChunk) + { + Assert((uintptr_t)pBlock + sizeof(pBlock->Core) + pBlock->Core.cbUser == (uintptr_t)pNeighbour); + + /* Adjust size of current memory block */ + pBlock->Core.cbUser += pNeighbour->cbUser + sizeof(VBGLPHYSHEAPBLOCK); + + /* Unlink the following node and invalid it. */ + vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pNeighbour); + vbglPhysHeapUnlinkBlock(pNeighbour); + + pNeighbour->u32Signature = ~VBGL_PH_BLOCKSIGNATURE; + pNeighbour->cbUser = UINT32_MAX / 4; + + dumpheap("post merge after"); + } + + /* + * Same check for the block before us. This invalidates pBlock. + */ + pNeighbour = pBlock->Core.pPrev; + if ( pNeighbour + && !pNeighbour->fAllocated + && pNeighbour->pChunk == pChunk) + { + Assert((uintptr_t)pNeighbour + sizeof(*pNeighbour) + pNeighbour->cbUser == (uintptr_t)pBlock); + + /* Adjust size of the block before us */ + pNeighbour->cbUser += pBlock->Core.cbUser + sizeof(VBGLPHYSHEAPBLOCK); + + /* Unlink this node and invalid it. */ + vbglPhysHeapUnlinkFreeBlock(pBlock); + vbglPhysHeapUnlinkBlock(&pBlock->Core); + + pBlock->Core.u32Signature = ~VBGL_PH_BLOCKSIGNATURE; + pBlock->Core.cbUser = UINT32_MAX / 8; + + pBlock = NULL; /* invalid */ + + dumpheap("post merge before"); + } + + /* + * If this chunk is now completely unused, delete it if there are + * more completely free ones. + */ + if ( pChunk->cFreeBlocks == pChunk->cBlocks + && (pChunk->pPrev || pChunk->pNext)) + { + VBGLPHYSHEAPCHUNK *pCurChunk; + uint32_t cUnusedChunks = 0; + for (pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext) + { + AssertBreak(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE); + if (pCurChunk->cFreeBlocks == pCurChunk->cBlocks) + { + cUnusedChunks++; + if (cUnusedChunks > 1) + { + /* Delete current chunk, it will also unlink all free blocks + * remaining in the chunk from the free list, so the pBlock + * will also be invalid after this. + */ + vbglPhysHeapChunkDelete(pChunk); + pBlock = NULL; /* invalid */ + pChunk = NULL; + pNeighbour = NULL; + break; + } + } + } + } + + dumpheap("post free"); + } + else + AssertMsgFailed(("pBlock: %p: u32Signature=%#x cb=%#x fAllocated=%d - double free?\n", + pBlock, pBlock->Core.u32Signature, pBlock->Core.cbUser, pBlock->Core.fAllocated)); + + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + AssertRC(rc); + } +} + +#ifdef IN_TESTCASE /* For the testcase only */ + +/** + * Returns the sum of all free heap blocks. + * + * This is the amount of memory you can theoretically allocate if you do + * allocations exactly matching the free blocks. + * + * @returns The size of the free blocks. + * @returns 0 if heap was safely detected as being bad. + */ +DECLVBGL(size_t) VbglR0PhysHeapGetFreeSize(void) +{ + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, 0); + + size_t cbTotal = 0; + for (VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead; pCurBlock; pCurBlock = pCurBlock->pNextFree) + { + Assert(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + Assert(!pCurBlock->Core.fAllocated); + cbTotal += pCurBlock->Core.cbUser; + } + + RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + return cbTotal; +} + + +/** + * Checks the heap, caller responsible for locking. + * + * @returns VINF_SUCCESS if okay, error status if not. + * @param pErrInfo Where to return more error details, optional. + */ +static int vbglR0PhysHeapCheckLocked(PRTERRINFO pErrInfo) +{ + /* + * Scan the blocks in each chunk, walking the block list in parallel. + */ + const VBGLPHYSHEAPBLOCK *pPrevBlockListEntry = NULL; + const VBGLPHYSHEAPBLOCK *pCurBlockListEntry = g_vbgldata.pBlockHead; + unsigned acTotalBlocks[2] = { 0, 0 }; + for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead, *pPrevChunk = NULL; pCurChunk; pCurChunk = pCurChunk->pNext) + { + AssertReturn(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurChunk=%p: magic=%#x", pCurChunk, pCurChunk->u32Signature)); + AssertReturn(pCurChunk->pPrev == pPrevChunk, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurChunk=%p: pPrev=%p, expected %p", pCurChunk, pCurChunk->pPrev, pPrevChunk)); + + const VBGLPHYSHEAPBLOCK *pCurBlock = (const VBGLPHYSHEAPBLOCK *)(pCurChunk + 1); + uintptr_t const uEnd = (uintptr_t)pCurChunk + pCurChunk->cbChunk; + unsigned acBlocks[2] = { 0, 0 }; + while ((uintptr_t)pCurBlock < uEnd) + { + AssertReturn(pCurBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, + "pCurBlock=%p: magic=%#x", pCurBlock, pCurBlock->u32Signature)); + AssertReturn(pCurBlock->pChunk == pCurChunk, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurBlock=%p: pChunk=%p, expected %p", pCurBlock, pCurBlock->pChunk, pCurChunk)); + AssertReturn( pCurBlock->cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE + && pCurBlock->cbUser <= VBGL_PH_LARGEST_ALLOC_SIZE + && RT_ALIGN_32(pCurBlock->cbUser, VBGL_PH_ALLOC_ALIGN) == pCurBlock->cbUser, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, + "pCurBlock=%p: cbUser=%#x", pCurBlock, pCurBlock->cbUser)); + AssertReturn(pCurBlock == pCurBlockListEntry, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurChunk=%p: pCurBlock=%p, pCurBlockListEntry=%p\n", + pCurChunk, pCurBlock, pCurBlockListEntry)); + AssertReturn(pCurBlock->pPrev == pPrevBlockListEntry, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5, + "pCurChunk=%p: pCurBlock->pPrev=%p, pPrevBlockListEntry=%p\n", + pCurChunk, pCurBlock->pPrev, pPrevBlockListEntry)); + + acBlocks[pCurBlock->fAllocated] += 1; + + /* advance */ + pPrevBlockListEntry = pCurBlock; + pCurBlockListEntry = pCurBlock->pNext; + pCurBlock = (const VBGLPHYSHEAPBLOCK *)((uintptr_t)(pCurBlock + 1) + pCurBlock->cbUser); + } + AssertReturn((uintptr_t)pCurBlock == uEnd, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurBlock=%p uEnd=%p", pCurBlock, uEnd)); + + acTotalBlocks[1] += acBlocks[1]; + AssertReturn(acBlocks[0] + acBlocks[1] == (uint32_t)pCurChunk->cBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurChunk=%p: cBlocks=%u, expected %u", + pCurChunk, pCurChunk->cBlocks, acBlocks[0] + acBlocks[1])); + + acTotalBlocks[0] += acBlocks[0]; + AssertReturn(acBlocks[0] == (uint32_t)pCurChunk->cFreeBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5, + "pCurChunk=%p: cFreeBlocks=%u, expected %u", + pCurChunk, pCurChunk->cFreeBlocks, acBlocks[0])); + + pPrevChunk = pCurChunk; + } + + AssertReturn(acTotalBlocks[0] == (uint32_t)g_vbgldata.cFreeBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR, + "g_vbgldata.cFreeBlocks=%u, expected %u", g_vbgldata.cFreeBlocks, acTotalBlocks[0])); + AssertReturn(acTotalBlocks[0] + acTotalBlocks[1] == (uint32_t)g_vbgldata.cBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR, + "g_vbgldata.cBlocks=%u, expected %u", g_vbgldata.cBlocks, acTotalBlocks[0] + acTotalBlocks[1])); + + /* + * Check that the free list contains the same number of blocks as we + * encountered during the above scan. + */ + { + unsigned cFreeListBlocks = 0; + for (const VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead, *pPrevBlock = NULL; + pCurBlock; + pCurBlock = pCurBlock->pNextFree) + { + AssertReturn(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, + "pCurBlock=%p/free: magic=%#x", pCurBlock, pCurBlock->Core.u32Signature)); + AssertReturn(pCurBlock->pPrevFree == pPrevBlock, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurBlock=%p/free: pPrev=%p, expected %p", pCurBlock, pCurBlock->pPrevFree, pPrevBlock)); + AssertReturn(pCurBlock->Core.pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurBlock=%p/free: chunk (%p) magic=%#x", + pCurBlock, pCurBlock->Core.pChunk, pCurBlock->Core.pChunk->u32Signature)); + cFreeListBlocks++; + pPrevBlock = pCurBlock; + } + + AssertReturn(cFreeListBlocks == acTotalBlocks[0], + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, + "Found %u in free list, expected %u", cFreeListBlocks, acTotalBlocks[0])); + } + return VINF_SUCCESS; +} + + +/** + * Performs a heap check. + * + * @returns Problem description on failure, NULL on success. + * @param pErrInfo Where to return more error details, optional. + */ +DECLVBGL(int) VbglR0PhysHeapCheck(PRTERRINFO pErrInfo) +{ + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, 0); + + rc = vbglR0PhysHeapCheckLocked(pErrInfo); + + RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + return rc; +} + +#endif /* IN_TESTCASE */ + +DECLR0VBGL(int) VbglR0PhysHeapInit(void) +{ + g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX; + + /* Allocate the first chunk of the heap. */ + VBGLPHYSHEAPFREEBLOCK *pBlock = vbglPhysHeapChunkAlloc(0); + if (pBlock) + return RTSemFastMutexCreate(&g_vbgldata.hMtxHeap); + return VERR_NO_CONT_MEMORY; +} + +DECLR0VBGL(void) VbglR0PhysHeapTerminate(void) +{ + while (g_vbgldata.pChunkHead) + vbglPhysHeapChunkDelete(g_vbgldata.pChunkHead); + + RTSemFastMutexDestroy(g_vbgldata.hMtxHeap); + g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c new file mode 100644 index 00000000..0350b194 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c @@ -0,0 +1,716 @@ +/* $Id: VBoxGuestR0LibSharedFolders.c $ */ +/** @file + * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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_SHARED_FOLDERS +#include "VBoxGuestR0LibInternal.h" +#include <VBox/VBoxGuestLibSharedFolders.h> +#include <VBox/log.h> +#include <iprt/err.h> +#include <iprt/time.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBOX_INIT_CALL(a, b, c) \ + LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \ + VBGL_HGCM_HDR_INIT(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b); \ + (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */ + +#define VBOX_INIT_CALL_EX(a, b, c, a_cbReq) \ + LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \ + VBGL_HGCM_HDR_INIT_EX(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b, a_cbReq); \ + (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */ + + + +DECLVBGL(int) VbglR0SfInit(void) +{ + return VbglR0InitClient(); +} + +DECLVBGL(void) VbglR0SfTerm(void) +{ + VbglR0TerminateClient(); +} + +DECLVBGL(int) VbglR0SfConnect(PVBGLSFCLIENT pClient) +{ + int rc = VbglR0HGCMConnect(&pClient->handle, "VBoxSharedFolders", &pClient->idClient); + if (RT_SUCCESS(rc)) + LogFunc(("idClient=%d\n", pClient->idClient)); + else + LogFunc(("VbglR0HGCMConnect failed -> rc=%Rrc\n", rc)); + return rc; +} + +DECLVBGL(void) VbglR0SfDisconnect(PVBGLSFCLIENT pClient) +{ + int rc; + LogFunc(("u32ClientID=%d\n", pClient->idClient)); + if (pClient->handle == NULL) + return; /* not connected */ + + rc = VbglR0HGCMDisconnect(pClient->handle, pClient->idClient); + NOREF(rc); +/* Log(("VBOXSF: VbglR0SfDisconnect: VbglR0HGCMDisconnect -> %#x\n", rc)); */ + pClient->idClient = 0; + pClient->handle = NULL; + return; +} + +#if !defined(RT_OS_LINUX) + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfSetUtf8(PVBGLSFCLIENT pClient) +{ + int rc; + VBGLIOCHGCMCALL callInfo; + + VBOX_INIT_CALL(&callInfo, SET_UTF8, pClient); + rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo)); +/* Log(("VBOXSF: VbglR0SfSetUtf8: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +/** @name Deprecated VBGL shared folder helpers. + * + * @deprecated These are all use the slow VbglR0HGCMCall interface, that + * basically treat ring-0 and user land callers much the same. + * Since 6.0 there is VbglR0HGCMFastCall() that does not bother with + * repacking the request and locking/duplicating parameter buffers, + * but just passes it along to the host and handles the waiting. + * Also new in 6.0 is embedded buffers which saves a bit time on + * guest and host by embedding parameter buffers into the request. + * + * @{ + */ + +DECLVBGL(int) VbglR0SfQueryMappings(PVBGLSFCLIENT pClient, SHFLMAPPING paMappings[], uint32_t *pcMappings) +{ + int rc; + VBoxSFQueryMappings data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAPPINGS, pClient); + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = SHFL_MF_UCS2; + + data.numberOfMappings.type = VMMDevHGCMParmType_32bit; + data.numberOfMappings.u.value32 = *pcMappings; + + data.mappings.type = VMMDevHGCMParmType_LinAddr; + data.mappings.u.Pointer.size = sizeof(SHFLMAPPING) * *pcMappings; + data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&paMappings[0]; + +/* Log(("VBOXSF: in ifs difference %d\n", (char *)&data.flags.type - (char *)&data.callInfo.cParms)); */ + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfQueryMappings: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.result)); */ + if (RT_SUCCESS(rc)) + *pcMappings = data.numberOfMappings.u.value32; + + return rc; +} + +DECLVBGL(int) VbglR0SfQueryMapName(PVBGLSFCLIENT pClient, SHFLROOT root, SHFLSTRING *pString, uint32_t size) +{ + int rc; + VBoxSFQueryMapName data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAP_NAME, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.name.type = VMMDevHGCMParmType_LinAddr; + data.name.u.Pointer.size = size; + data.name.u.Pointer.u.linearAddr = (uintptr_t)pString; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfQueryMapName: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfMapFolder(PVBGLSFCLIENT pClient, PSHFLSTRING szFolderName, PVBGLSFMAP pMap) +{ + int rc; + VBoxSFMapFolder data; + + VBOX_INIT_CALL(&data.callInfo, MAP_FOLDER, pClient); + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(szFolderName); + data.path.u.Pointer.u.linearAddr = (uintptr_t)szFolderName; + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = 0; + + data.delimiter.type = VMMDevHGCMParmType_32bit; + data.delimiter.u.value32 = RTPATH_DELIMITER; + + data.fCaseSensitive.type = VMMDevHGCMParmType_32bit; +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + data.fCaseSensitive.u.value32 = 0; +#else + data.fCaseSensitive.u.value32 = 1; +#endif + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfMapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + pMap->root = data.root.u.value32; + rc = data.callInfo.Hdr.rc; + } + return rc; +} + +DECLVBGL(int) VbglR0SfUnmapFolder(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap) +{ + int rc; + VBoxSFUnmapFolder data; + + VBOX_INIT_CALL(&data.callInfo, UNMAP_FOLDER, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfUnmapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# if !defined(RT_OS_WINDOWS) + +DECLVBGL(int) VbglR0SfCreate(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, PSHFLCREATEPARMS pCreateParms) +{ + /** @todo copy buffers to physical or mapped memory. */ + int rc; + VBoxSFCreate data; + + VBOX_INIT_CALL(&data.callInfo, CREATE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.parms.type = VMMDevHGCMParmType_LinAddr; + data.parms.u.Pointer.size = sizeof(SHFLCREATEPARMS); + data.parms.u.Pointer.u.linearAddr = (uintptr_t)pCreateParms; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfCreate: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfClose(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE Handle) +{ + int rc; + VBoxSFClose data; + + VBOX_INIT_CALL(&data.callInfo, CLOSE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = Handle; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfClose: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + + +DECLVBGL(int) VbglR0SfRemove(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t flags) +{ + int rc = VINF_SUCCESS; + + VBoxSFRemove data; + + VBOX_INIT_CALL(&data.callInfo, REMOVE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRemove: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfRename(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, uint32_t flags) +{ + int rc; + VBoxSFRename data; + + VBOX_INIT_CALL(&data.callInfo, RENAME, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.src.type = VMMDevHGCMParmType_LinAddr_In; + data.src.u.Pointer.size = ShflStringSizeOfBuffer(pSrcPath); + data.src.u.Pointer.u.linearAddr = (uintptr_t)pSrcPath; + + data.dest.type = VMMDevHGCMParmType_LinAddr_In; + data.dest.u.Pointer.size = ShflStringSizeOfBuffer(pDestPath); + data.dest.u.Pointer.u.linearAddr = (uintptr_t)pDestPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRename: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfRead(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked) +{ + int rc; + VBoxSFRead data; + + VBOX_INIT_CALL(&data.callInfo, READ, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = (fLocked) ? VMMDevHGCMParmType_LinAddr_Locked_Out : VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRead: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +DECLVBGL(int) VbglR0SfReadPageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToRead = *pcbBuffer; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFRead) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFRead *pData = (VBoxSFRead *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, READ, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToRead; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToRead; + pData->buffer.u.PageList.offset = sizeof(VBoxSFRead); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfReadPageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + +DECLVBGL(int) VbglR0SfWrite(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked) +{ + int rc; + VBoxSFWrite data; + + VBOX_INIT_CALL(&data.callInfo, WRITE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = fLocked ? VMMDevHGCMParmType_LinAddr_Locked_In : VMMDevHGCMParmType_LinAddr_In; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfWrite: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +DECLVBGL(int) VbglR0SfWritePhysCont(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, + uint32_t *pcbBuffer, RTCCPHYS PhysBuffer) +{ + uint32_t cbToWrite = *pcbBuffer; + uint32_t cPages = RT_ALIGN_32((PhysBuffer & PAGE_OFFSET_MASK) + cbToWrite, PAGE_SIZE) >> PAGE_SHIFT; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint32_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = (uint16_t)(PhysBuffer & PAGE_OFFSET_MASK); + pPgLst->cPages = cPages; + PhysBuffer &= ~(RTCCPHYS)PAGE_OFFSET_MASK; + for (iPage = 0; iPage < cPages; iPage++, PhysBuffer += PAGE_SIZE) + pPgLst->aPages[iPage] = PhysBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfWritePhysCont: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; + +} + +DECLVBGL(int) VbglR0SfWritePageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToWrite = *pcbBuffer; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfWritePageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +DECLVBGL(int) VbglR0SfFlush(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile) +{ + int rc; + VBoxSFFlush data; + + VBOX_INIT_CALL(&data.callInfo, FLUSH, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfFlush: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfDirInfo( + PVBGLSFCLIENT pClient, + PVBGLSFMAP pMap, + SHFLHANDLE hFile, + PSHFLSTRING ParsedPath, + uint32_t flags, + uint32_t index, + uint32_t *pcbBuffer, + PSHFLDIRINFO pBuffer, + uint32_t *pcFiles) +{ + int rc; + VBoxSFList data; + + VBOX_INIT_CALL(&data.callInfo, LIST, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ParsedPath ? ShflStringSizeOfBuffer(ParsedPath) : 0; + data.path.u.Pointer.u.linearAddr = (uintptr_t) ParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + data.resumePoint.type = VMMDevHGCMParmType_32bit; + data.resumePoint.u.value32 = index; + data.cFiles.type = VMMDevHGCMParmType_32bit; + data.cFiles.u.value32 = 0; /* out parameters only */ + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfDirInfo: rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + *pcbBuffer = data.cb.u.value32; + *pcFiles = data.cFiles.u.value32; + return rc; +} + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfFsInfo(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint32_t flags, uint32_t *pcbBuffer, PSHFLDIRINFO pBuffer) +{ + int rc; + VBoxSFInformation data; + + VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.info.type = VMMDevHGCMParmType_LinAddr; + data.info.u.Pointer.size = *pcbBuffer; + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfFsInfo: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +DECLVBGL(int) VbglR0SfLock(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint64_t cbSize, uint32_t fLock) +{ + int rc; + VBoxSFLock data; + + VBOX_INIT_CALL(&data.callInfo, LOCK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.length.type = VMMDevHGCMParmType_64bit; + data.length.u.value64 = cbSize; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = fLock; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfLock: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfReadLink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t cbBuffer, uint8_t *pBuffer) +{ + int rc; + VBoxSFReadLink data; + + VBOX_INIT_CALL(&data.callInfo, READLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = cbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfReadLink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfSymlink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath, + PSHFLFSOBJINFO pBuffer) +{ + int rc; + VBoxSFSymlink data; + + VBOX_INIT_CALL(&data.callInfo, SYMLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.newPath.type = VMMDevHGCMParmType_LinAddr_In; + data.newPath.u.Pointer.size = ShflStringSizeOfBuffer (pNewPath); + data.newPath.u.Pointer.u.linearAddr = (uintptr_t)pNewPath; + + data.oldPath.type = VMMDevHGCMParmType_LinAddr_In; + data.oldPath.u.Pointer.size = ShflStringSizeOfBuffer (pOldPath); + data.oldPath.u.Pointer.u.linearAddr = (uintptr_t)pOldPath; + + data.info.type = VMMDevHGCMParmType_LinAddr_Out; + data.info.u.Pointer.size = sizeof(SHFLFSOBJINFO); + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfSymlink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfSetSymlinks(PVBGLSFCLIENT pClient) +{ + int rc; + VBGLIOCHGCMCALL callInfo; + + VBOX_INIT_CALL(&callInfo, SET_SYMLINKS, pClient); + rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo)); +/* Log(("VBOXSF: VbglR0SfSetSymlinks: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +#endif /* !RT_OS_LINUX */ + +/** @} */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp new file mode 100644 index 00000000..f8340152 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp @@ -0,0 +1,51 @@ +/* $Id: VBoxGuestR0LibVMMDev.cpp $ */ +/** @file + * VBoxGuestLibR0 - VMMDev device related functions. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +DECLVBGL(int) VbglR0QueryVMMDevMemory(VMMDevMemory **ppVMMDevMemory) +{ + int rc = vbglR0Enter(); + if (RT_FAILURE(rc)) + return rc; + + /* If the memory was not found, return an error. */ + if (!g_vbgldata.pVMMDevMemory) + return VERR_NOT_SUPPORTED; + + *ppVMMDevMemory = g_vbgldata.pVMMDevMemory; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp new file mode 100644 index 00000000..239b942d --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp @@ -0,0 +1,485 @@ +/* $Id: VBoxGuestR3Lib.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(RT_OS_WINDOWS) +# include <iprt/nt/nt-and-windows.h> + +#elif defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> + +#elif defined(RT_OS_DARWIN) \ + || defined(RT_OS_FREEBSD) \ + || defined(RT_OS_HAIKU) \ + || defined(RT_OS_LINUX) \ + || defined(RT_OS_NETBSD) \ + || defined(RT_OS_SOLARIS) +# include <sys/types.h> +# include <sys/stat.h> +# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_NETBSD) + /** @todo check this on solaris+freebsd as well. */ +# include <sys/ioctl.h> +# endif +# if defined(RT_OS_DARWIN) +# include <mach/mach_port.h> +# include <IOKit/IOKitLib.h> +# endif +# include <errno.h> +# include <unistd.h> +#endif + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/time.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <VBox/log.h> +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +# define XF86_O_RDWR 0x0002 +typedef void *pointer; +extern "C" int xf86open(const char *, int, ...); +extern "C" int xf86close(int); +extern "C" int xf86ioctl(int, unsigned long, pointer); +# define VBOX_VBGLR3_XSERVER +#elif defined(VBOX_VBGLR3_XORG) +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +# include <sys/ioctl.h> +# define xf86open open +# define xf86close close +# define xf86ioctl ioctl +# define XF86_O_RDWR O_RDWR +# define VBOX_VBGLR3_XSERVER +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The VBoxGuest device handle. */ +#ifdef VBOX_VBGLR3_XSERVER +static int g_File = -1; +#elif defined(RT_OS_WINDOWS) +static HANDLE g_hFile = INVALID_HANDLE_VALUE; +#else +static RTFILE g_File = NIL_RTFILE; +#endif +/** User counter. + * A counter of the number of times the library has been initialised, for use with + * X.org drivers, where the library may be shared by multiple independent modules + * inside a single process space. + */ +static uint32_t volatile g_cInits = 0; +#ifdef RT_OS_DARWIN +/** I/O Kit connection handle. */ +static io_connect_t g_uConnection = 0; +#endif + + + +/** + * Implementation of VbglR3Init and VbglR3InitUser + */ +static int vbglR3Init(const char *pszDeviceName) +{ + int rc2; + uint32_t cInits = ASMAtomicIncU32(&g_cInits); + Assert(cInits > 0); + if (cInits > 1) + { + /* + * This will fail if two (or more) threads race each other calling VbglR3Init. + * However it will work fine for single threaded or otherwise serialized + * processed calling us more than once. + */ +#ifdef RT_OS_WINDOWS + if (g_hFile == INVALID_HANDLE_VALUE) +#elif !defined (VBOX_VBGLR3_XSERVER) + if (g_File == NIL_RTFILE) +#else + if (g_File == -1) +#endif + return VERR_INTERNAL_ERROR; + return VINF_SUCCESS; + } +#if defined(RT_OS_WINDOWS) + if (g_hFile != INVALID_HANDLE_VALUE) +#elif !defined(VBOX_VBGLR3_XSERVER) + if (g_File != NIL_RTFILE) +#else + if (g_File != -1) +#endif + return VERR_INTERNAL_ERROR; + +#if defined(RT_OS_WINDOWS) + /* + * Have to use CreateFile here as we want to specify FILE_FLAG_OVERLAPPED + * and possible some other bits not available thru iprt/file.h. + */ + HANDLE hFile = CreateFile(pszDeviceName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return VERR_OPEN_FAILED; + g_hFile = hFile; + +#elif defined(RT_OS_OS2) + /* + * We might wish to compile this with Watcom, so stick to + * the OS/2 APIs all the way. And in any case we have to use + * DosDevIOCtl for the requests, why not use Dos* for everything. + */ + HFILE hf = NULLHANDLE; + ULONG ulAction = 0; + APIRET rc = DosOpen((PCSZ)pszDeviceName, &hf, &ulAction, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, + NULL); + if (rc) + return RTErrConvertFromOS2(rc); + + if (hf < 16) + { + HFILE ahfs[16]; + unsigned i; + for (i = 0; i < RT_ELEMENTS(ahfs); i++) + { + ahfs[i] = 0xffffffff; + rc = DosDupHandle(hf, &ahfs[i]); + if (rc) + break; + } + + if (i-- > 1) + { + ULONG fulState = 0; + rc = DosQueryFHState(ahfs[i], &fulState); + if (!rc) + { + fulState |= OPEN_FLAGS_NOINHERIT; + fulState &= OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT; /* Turn off non-participating bits. */ + rc = DosSetFHState(ahfs[i], fulState); + } + if (!rc) + { + rc = DosClose(hf); + AssertMsg(!rc, ("%ld\n", rc)); + hf = ahfs[i]; + } + else + i++; + while (i-- > 0) + DosClose(ahfs[i]); + } + } + g_File = (RTFILE)hf; + +#elif defined(RT_OS_DARWIN) + /* + * Darwin is kind of special we need to engage the device via I/O first + * before we open it via the BSD device node. + */ + /* IOKit */ + 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("org_virtualbox_VBoxGuest"); + if (!ClassToMatch) + { + LogRel(("IOServiceMatching(\"org_virtualbox_VBoxGuest\") failed.\n")); + return VERR_GENERAL_FAILURE; + } + + io_service_t ServiceObject = IOServiceGetMatchingService(kIOMasterPortDefault, ClassToMatch); + if (!ServiceObject) + { + LogRel(("IOServiceGetMatchingService returned NULL\n")); + return VERR_NOT_FOUND; + } + + io_connect_t uConnection; + kr = IOServiceOpen(ServiceObject, mach_task_self(), VBOXGUEST_DARWIN_IOSERVICE_COOKIE, &uConnection); + IOObjectRelease(ServiceObject); + if (kr != kIOReturnSuccess) + { + LogRel(("IOServiceOpen returned %d. Driver open failed.\n", kr)); + return VERR_OPEN_FAILED; + } + + /* Regular unix FD. */ + RTFILE hFile; + int rc = RTFileOpen(&hFile, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + { + LogRel(("RTFileOpen(%s) returned %Rrc. Driver open failed.\n", pszDeviceName, rc)); + IOServiceClose(uConnection); + return rc; + } + g_File = hFile; + g_uConnection = uConnection; + +#elif defined(VBOX_VBGLR3_XSERVER) + int File = xf86open(pszDeviceName, XF86_O_RDWR); + if (File == -1) + return VERR_OPEN_FAILED; + g_File = File; + +#else + + /* The default implementation. (linux, solaris, freebsd, netbsd, haiku) */ + RTFILE File; + int rc = RTFileOpen(&File, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + return rc; + g_File = File; + +#endif + + /* + * Adjust the I/O control interface version. + */ + { + VBGLIOCDRIVERVERSIONINFO VerInfo; + VBGLREQHDR_INIT(&VerInfo.Hdr, DRIVER_VERSION_INFO); + VerInfo.u.In.uMinVersion = VBGL_IOC_VERSION & UINT32_C(0xffff0000); + VerInfo.u.In.uReqVersion = VBGL_IOC_VERSION; + VerInfo.u.In.uReserved1 = 0; + VerInfo.u.In.uReserved2 = 0; + rc2 = vbglR3DoIOCtl(VBGL_IOCTL_DRIVER_VERSION_INFO, &VerInfo.Hdr, sizeof(VerInfo)); +#ifndef VBOX_VBGLR3_XSERVER + AssertRC(rc2); /* otherwise ignored for now*/ +#endif + } + + +#ifndef VBOX_VBGLR3_XSERVER + /* + * Create release logger + */ + PRTLOGGER pReleaseLogger; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + rc2 = RTLogCreate(&pReleaseLogger, 0, "all", "VBOX_RELEASE_LOG", + RT_ELEMENTS(s_apszGroups), &s_apszGroups[0], RTLOGDEST_USER, NULL); + /* This may legitimately fail if we are using the mini-runtime. */ + if (RT_SUCCESS(rc2)) + RTLogRelSetDefaultInstance(pReleaseLogger); +#endif + + return VINF_SUCCESS; +} + + +/** + * Open the VBox R3 Guest Library. This should be called by system daemons + * and processes. + */ +VBGLR3DECL(int) VbglR3Init(void) +{ + return vbglR3Init(VBOXGUEST_DEVICE_NAME); +} + + +/** + * Open the VBox R3 Guest Library. Equivalent to VbglR3Init, but for user + * session processes. + */ +VBGLR3DECL(int) VbglR3InitUser(void) +{ + return vbglR3Init(VBOXGUEST_USER_DEVICE_NAME); +} + + +VBGLR3DECL(void) VbglR3Term(void) +{ + /* + * Decrement the reference count and see if we're the last one out. + */ + uint32_t cInits = ASMAtomicDecU32(&g_cInits); + if (cInits > 0) + return; +#if !defined(VBOX_VBGLR3_XSERVER) + AssertReturnVoid(!cInits); + +# if defined(RT_OS_WINDOWS) + HANDLE hFile = g_hFile; + g_hFile = INVALID_HANDLE_VALUE; + AssertReturnVoid(hFile != INVALID_HANDLE_VALUE); + BOOL fRc = CloseHandle(hFile); + Assert(fRc); NOREF(fRc); + +# elif defined(RT_OS_OS2) + RTFILE File = g_File; + g_File = NIL_RTFILE; + AssertReturnVoid(File != NIL_RTFILE); + APIRET rc = DosClose((uintptr_t)File); + AssertMsg(!rc, ("%ld\n", rc)); + +#elif defined(RT_OS_DARWIN) + io_connect_t uConnection = g_uConnection; + RTFILE hFile = g_File; + g_uConnection = 0; + g_File = NIL_RTFILE; + kern_return_t kr = IOServiceClose(uConnection); + AssertMsg(kr == kIOReturnSuccess, ("%#x (%d)\n", kr, kr)); NOREF(kr); + int rc = RTFileClose(hFile); + AssertRC(rc); + +# else /* The IPRT case. */ + RTFILE File = g_File; + g_File = NIL_RTFILE; + AssertReturnVoid(File != NIL_RTFILE); + int rc = RTFileClose(File); + AssertRC(rc); +# endif + +#else /* VBOX_VBGLR3_XSERVER */ + int File = g_File; + g_File = -1; + if (File == -1) + return; + xf86close(File); +#endif /* VBOX_VBGLR3_XSERVER */ +} + + +/** + * Internal wrapper around various OS specific ioctl implementations. + * + * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or + * an failure returned by the OS specific ioctl APIs. + * + * @param uFunction The requested function. + * @param pHdr The input and output request buffer. + * @param cbReq The size of the request buffer. + */ +int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq) +{ + Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut)); RT_NOREF1(cbReq); + Assert(pHdr->cbOut != 0); + +#if defined(RT_OS_WINDOWS) +# if 0 /*def USE_NT_DEVICE_IO_CONTROL_FILE*/ + IO_STATUS_BLOCK Ios; + Ios.Status = -1; + Ios.Information = 0; + NTSTATUS rcNt = NtDeviceIoControlFile(g_hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios, + (ULONG)uFunction, + pHdr /*pvInput */, pHdr->cbIn /* cbInput */, + pHdr /*pvOutput*/, pHdr->cbOut /* cbOutput */); + if (NT_SUCCESS(rcNt)) + { + if (NT_SUCCESS(Ios.Status)) + return VINF_SUCCESS; + rcNt = Ios.Status; + } + return RTErrConvertFromNtStatus(rcNt); + +# else + DWORD cbReturned = (ULONG)pHdr->cbOut; + if (DeviceIoControl(g_hFile, uFunction, pHdr, pHdr->cbIn, pHdr, cbReturned, &cbReturned, NULL)) + return 0; + return RTErrConvertFromWin32(GetLastError()); +# endif + +#elif defined(RT_OS_OS2) + ULONG cbOS2Parm = cbReq; + APIRET rc = DosDevIOCtl((uintptr_t)g_File, VBGL_IOCTL_CATEGORY, uFunction, pHdr, cbReq, &cbOS2Parm, NULL, 0, NULL); + if (RT_LIKELY(rc == NO_ERROR)) + return VINF_SUCCESS; + return RTErrConvertFromOS2(rc); + +#elif defined(VBOX_VBGLR3_XSERVER) + if (g_File != -1) + { + if (RT_LIKELY(xf86ioctl((int)g_File, uFunction, pHdr) >= 0)) + return VINF_SUCCESS; + return VERR_FILE_IO_ERROR; + } + return VERR_INVALID_HANDLE; + +#else + if (g_File != NIL_RTFILE) + { + if (RT_LIKELY(ioctl((int)(intptr_t)g_File, uFunction, pHdr) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); + } + return VERR_INVALID_HANDLE; +#endif +} + + +/** + * Internal wrapper around various OS specific ioctl implementations, that + * returns the status from the header. + * + * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or + * an failure returned by the OS specific ioctl APIs. + * + * @param uFunction The requested function. + * @param pHdr The input and output request buffer. + * @param cbReq The size of the request buffer. + */ +int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq) +{ + int rc = vbglR3DoIOCtlRaw(uFunction, pHdr, cbReq); + if (RT_SUCCESS(rc)) + rc = pHdr->rc; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp new file mode 100644 index 00000000..e45f25dc --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp @@ -0,0 +1,363 @@ +/* $Id: VBoxGuestR3LibAdditions.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Additions Info. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> +#ifdef RT_OS_WINDOWS +# include <iprt/utf16.h> +#endif +#include <VBox/log.h> +#include <VBox/version.h> +#include "VBoxGuestR3LibInternal.h" + + + +#ifdef RT_OS_WINDOWS + +/** + * Opens the "VirtualBox Guest Additions" registry key. + * + * @returns IPRT status code + * @param phKey Receives key handle on success. The returned handle must + * be closed by calling vbglR3WinCloseRegKey. + */ +static int vbglR3WinOpenAdditionRegisterKey(PHKEY phKey) +{ + /* + * Current vendor first. We keep the older ones just for the case that + * the caller isn't actually installed yet (no real use case AFAIK). + */ + static PCRTUTF16 s_apwszKeys[] = + { + L"SOFTWARE\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions", +#endif + L"SOFTWARE\\Sun\\VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\Sun\\VirtualBox Guest Additions", +#endif + L"SOFTWARE\\Sun\\xVM VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\Sun\\xVM VirtualBox Guest Additions", +#endif + }; + int rc = VERR_NOT_FOUND; + for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszKeys); i++) + { + LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_apwszKeys[i], 0 /* ulOptions*/, KEY_READ, phKey); + if (lrc == ERROR_SUCCESS) + return VINF_SUCCESS; + if (i == 0) + rc = RTErrConvertFromWin32(lrc); + } + return rc; +} + + +/** + * Closes the registry handle returned by vbglR3WinOpenAdditionRegisterKey(). + * + * @returns @a rc or IPRT failure status. + * @param hKey Handle to close. + * @param rc The current IPRT status of the operation. Error + * condition takes precedence over errors from this call. + */ +static int vbglR3WinCloseRegKey(HKEY hKey, int rc) +{ + LSTATUS lrc = RegCloseKey(hKey); + if ( lrc == ERROR_SUCCESS + || RT_FAILURE(rc)) + return rc; + return RTErrConvertFromWin32(lrc); +} + + +/** + * Queries a string value from a specified registry key. + * + * @return IPRT status code. + * @param hKey Handle of registry key to use. + * @param pwszValueName The name of the value to query. + * @param cbHint Size hint. + * @param ppszValue Where to return value string on success. Free + * with RTStrFree. + */ +static int vbglR3QueryRegistryString(HKEY hKey, PCRTUTF16 pwszValueName, uint32_t cbHint, char **ppszValue) +{ + AssertPtr(pwszValueName); + AssertPtrReturn(ppszValue, VERR_INVALID_POINTER); + + /* + * First try. + */ + int rc; + DWORD dwType; + DWORD cbTmp = cbHint; + PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16)); + if (pwszTmp) + { + LSTATUS lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp); + if (lrc == ERROR_MORE_DATA) + { + /* + * Allocate larger buffer and try again. + */ + RTMemTmpFree(pwszTmp); + cbTmp += 16; + pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16)); + if (!pwszTmp) + { + *ppszValue = NULL; + return VERR_NO_TMP_MEMORY; + } + lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp); + } + if (lrc == ERROR_SUCCESS) + { + /* + * Check the type and convert to UTF-8. + */ + if (dwType == REG_SZ) + rc = RTUtf16ToUtf8(pwszTmp, ppszValue); + else + rc = VERR_WRONG_TYPE; + } + else + rc = RTErrConvertFromWin32(lrc); + RTMemTmpFree(pwszTmp); + } + else + rc = VERR_NO_TMP_MEMORY; + if (RT_SUCCESS(rc)) + return rc; + *ppszValue = NULL; + return rc; +} + +#endif /* RT_OS_WINDOWS */ + + +/** + * Fallback for VbglR3GetAdditionsVersion. + * + * @copydoc VbglR3GetAdditionsVersion + */ +static int vbglR3GetAdditionsCompileTimeVersion(char **ppszVer, char **ppszVerExt, char **ppszRev) +{ + int rc = VINF_SUCCESS; + if (ppszVer) + rc = RTStrDupEx(ppszVer, VBOX_VERSION_STRING_RAW); + if (RT_SUCCESS(rc)) + { + if (ppszVerExt) + rc = RTStrDupEx(ppszVerExt, VBOX_VERSION_STRING); + if (RT_SUCCESS(rc)) + { + if (ppszRev) + rc = RTStrDupEx(ppszRev, RT_XSTR(VBOX_SVN_REV)); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + /* bail out: */ + } + if (ppszVerExt) + { + RTStrFree(*ppszVerExt); + *ppszVerExt = NULL; + } + } + if (ppszVer) + { + RTStrFree(*ppszVer); + *ppszVer = NULL; + } + return rc; +} + + +/** + * Retrieves the installed Guest Additions version and/or revision. + * + * @returns IPRT status code + * @param ppszVer Receives pointer of allocated raw version string + * (major.minor.build). NULL is accepted. The returned + * pointer must be freed using RTStrFree(). + * @param ppszVerExt Receives pointer of allocated full version string + * (raw version + vendor suffix(es)). NULL is + * accepted. The returned pointer must be freed using + * RTStrFree(). + * @param ppszRev Receives pointer of allocated revision string. NULL is + * accepted. The returned pointer must be freed using + * RTStrFree(). + */ +VBGLR3DECL(int) VbglR3GetAdditionsVersion(char **ppszVer, char **ppszVerExt, char **ppszRev) +{ + /* + * Zap the return value up front. + */ + if (ppszVer) + *ppszVer = NULL; + if (ppszVerExt) + *ppszVerExt = NULL; + if (ppszRev) + *ppszRev = NULL; + +#ifdef RT_OS_WINDOWS + HKEY hKey; + int rc = vbglR3WinOpenAdditionRegisterKey(&hKey); + if (RT_SUCCESS(rc)) + { + /* + * Version. + */ + if (ppszVer) + rc = vbglR3QueryRegistryString(hKey, L"Version", 64, ppszVer); + + if ( RT_SUCCESS(rc) + && ppszVerExt) + rc = vbglR3QueryRegistryString(hKey, L"VersionExt", 128, ppszVerExt); + + /* + * Revision. + */ + if ( RT_SUCCESS(rc) + && ppszRev) + rc = vbglR3QueryRegistryString(hKey, L"Revision", 64, ppszRev); + + rc = vbglR3WinCloseRegKey(hKey, rc); + + /* Clean up allocated strings on error. */ + if (RT_FAILURE(rc)) + { + if (ppszVer) + { + RTStrFree(*ppszVer); + *ppszVer = NULL; + } + if (ppszVerExt) + { + RTStrFree(*ppszVerExt); + *ppszVerExt = NULL; + } + if (ppszRev) + { + RTStrFree(*ppszRev); + *ppszRev = NULL; + } + } + } + /* + * No registry entries found, return the version string compiled into this binary. + */ + else + rc = vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev); + return rc; + +#else /* !RT_OS_WINDOWS */ + /* + * On non-Windows platforms just return the compile-time version string. + */ + return vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev); +#endif /* !RT_OS_WINDOWS */ +} + + +/** + * Retrieves the installation path of Guest Additions. + * + * @returns IPRT status code + * @param ppszPath Receives pointer of allocated installation path string. + * The returned pointer must be freed using + * RTStrFree(). + */ +VBGLR3DECL(int) VbglR3GetAdditionsInstallationPath(char **ppszPath) +{ + int rc; + +#ifdef RT_OS_WINDOWS + /* + * Get it from the registry. + */ + HKEY hKey; + rc = vbglR3WinOpenAdditionRegisterKey(&hKey); + if (RT_SUCCESS(rc)) + { + rc = vbglR3QueryRegistryString(hKey, L"InstallDir", MAX_PATH * sizeof(RTUTF16), ppszPath); + if (RT_SUCCESS(rc)) + RTPathChangeToUnixSlashes(*ppszPath, true /*fForce*/); + rc = vbglR3WinCloseRegKey(hKey, rc); + } +#else + /** @todo implement me */ + rc = VERR_NOT_IMPLEMENTED; + RT_NOREF1(ppszPath); +#endif + return rc; +} + + +/** + * Reports the Guest Additions status of a certain facility to the host. + * + * @returns IPRT status code + * @param enmFacility The facility to report the status on. + * @param enmStatus The new status of the facility. + * @param fReserved Flags reserved for future hacks. + */ +VBGLR3DECL(int) VbglR3ReportAdditionsStatus(VBoxGuestFacilityType enmFacility, VBoxGuestFacilityStatus enmStatus, + uint32_t fReserved) +{ + VMMDevReportGuestStatus Report; + RT_ZERO(Report); + int rc = vmmdevInitRequest(&Report.header, VMMDevReq_ReportGuestStatus); + if (RT_SUCCESS(rc)) + { + Report.guestStatus.facility = enmFacility; + Report.guestStatus.status = enmStatus; + Report.guestStatus.flags = fReserved; + + rc = vbglR3GRPerform(&Report.header); + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp new file mode 100644 index 00000000..f40b2947 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp @@ -0,0 +1,130 @@ +/* $Id: VBoxGuestR3LibAutoLogon.cpp $ */ +/** @file + * VBoxGuestR3LibAutoLogon - Ring-3 utility functions for auto-logon modules + * (VBoxGINA / VBoxCredProv / pam_vbox). + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef RT_OS_WINDOWS +# include <iprt/win/windows.h> +#endif + +#include "VBoxGuestR3LibInternal.h" +#include <iprt/errcore.h> + + +/** + * Reports the current auto-logon status to the host. + * + * This makes sure that the Failed state is sticky. + * + * @return IPRT status code. + * @param enmStatus Status to report to the host. + */ +VBGLR3DECL(int) VbglR3AutoLogonReportStatus(VBoxGuestFacilityStatus enmStatus) +{ + /* + * VBoxGuestFacilityStatus_Failed is sticky. + */ + static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive; + if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed) + { + int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_AutoLogon, enmStatus, 0 /* Flags */); + if (rc == VERR_NOT_SUPPORTED) + { + /* + * To maintain backwards compatibility to older hosts which don't have + * VMMDevReportGuestStatus implemented we set the appropriate status via + * guest property to have at least something. + */ +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClient = 0; + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + const char *pszStatus; + switch (enmStatus) + { + case VBoxGuestFacilityStatus_Inactive: pszStatus = "Inactive"; break; + case VBoxGuestFacilityStatus_Paused: pszStatus = "Disabled"; break; + case VBoxGuestFacilityStatus_PreInit: pszStatus = "PreInit"; break; + case VBoxGuestFacilityStatus_Init: pszStatus = "Init"; break; + case VBoxGuestFacilityStatus_Active: pszStatus = "Active"; break; + case VBoxGuestFacilityStatus_Terminating: pszStatus = "Terminating"; break; + case VBoxGuestFacilityStatus_Terminated: pszStatus = "Terminated"; break; + case VBoxGuestFacilityStatus_Failed: pszStatus = "Failed"; break; + default: pszStatus = NULL; + } + if (pszStatus) + { + /* + * Use TRANSRESET when possible, fall back to TRANSIENT + * (generally sufficient unless the guest misbehaves). + */ + static const char s_szPath[] = "/VirtualBox/GuestInfo/OS/AutoLogonStatus"; + rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSRESET"); + if (rc == VERR_PARSE_ERROR) + rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSIENT"); + } + else + rc = VERR_INVALID_PARAMETER; + + VbglR3GuestPropDisconnect(idClient); + } +#endif + } + + s_enmLastStatus = enmStatus; + } + return VINF_SUCCESS; +} + + +/** + * Detects whether our process is running in a remote session or not. + * + * @return bool true if running in a remote session, false if not. + */ +VBGLR3DECL(bool) VbglR3AutoLogonIsRemoteSession(void) +{ +#ifdef RT_OS_WINDOWS + return GetSystemMetrics(SM_REMOTESESSION) != 0 ? true : false; +#else + return false; /* Not implemented. */ +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp new file mode 100644 index 00000000..2a37dea9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp @@ -0,0 +1,83 @@ +/* $Id: VBoxGuestR3LibBalloon.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Ballooning. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" +#include <iprt/string.h> + + +/** + * Refresh the memory balloon after a change. + * + * @returns IPRT status code. + * @param pcChunks The size of the balloon in chunks of 1MB (out). + * @param pfHandleInR3 Allocating of memory in R3 required (out). + */ +VBGLR3DECL(int) VbglR3MemBalloonRefresh(uint32_t *pcChunks, bool *pfHandleInR3) +{ + VBGLIOCCHECKBALLOON Info; + VBGLREQHDR_INIT(&Info.Hdr, CHECK_BALLOON); + int rc = vbglR3DoIOCtl(VBGL_IOCTL_CHECK_BALLOON, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + { + *pcChunks = Info.u.Out.cBalloonChunks; + *pfHandleInR3 = Info.u.Out.fHandleInR3 != false; + } + return rc; +} + + +/** + * Change the memory by granting/reclaiming memory to/from R0. + * + * @returns IPRT status code. + * @param pv Memory chunk (1MB). + * @param fInflate true = inflate balloon (grant memory). + * false = deflate balloon (reclaim memory). + */ +VBGLR3DECL(int) VbglR3MemBalloonChange(void *pv, bool fInflate) +{ + VBGLIOCCHANGEBALLOON Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_BALLOON); + Info.u.In.pvChunk = pv; + Info.u.In.fInflate = fInflate; + RT_ZERO(Info.u.In.abPadding); + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_BALLOON, &Info.Hdr, sizeof(Info)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp new file mode 100644 index 00000000..35aaa70e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp @@ -0,0 +1,2609 @@ +/* $Id: VBoxGuestR3LibClipboard.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared Clipboard. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/GuestHost/SharedClipboard.h> +#include <VBox/GuestHost/clipboard-helper.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <VBox/GuestHost/SharedClipboard-transfers.h> +#endif +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/err.h> +#include <iprt/assert.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <iprt/dir.h> +# include <iprt/file.h> +# include <iprt/path.h> +#endif +#include <iprt/string.h> +#include <iprt/cpp/ministring.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Function naming convention: + * + * FunctionNameRecv = Receives a host message (request). + * FunctionNameReply = Replies to a host message (request). + * FunctionNameSend = Sends a guest message to the host. + */ + + +/********************************************************************************************************************************* +* Prototypes * +*********************************************************************************************************************************/ + + +/** + * Connects to the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code + * @param pidClient Where to put the client id on success. The client id + * must be passed to all the other clipboard calls. + */ +VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient); + if (RT_FAILURE(rc)) + { + if (rc == VERR_HGCM_SERVICE_NOT_FOUND) + LogRel(("Shared Clipboard: Unabled to connect, as host service was not found, skipping\n")); + else + LogRel(("Shared Clipboard: Unabled to connect to host service, rc=%Rrc\n", rc)); + } + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Connects to the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Command context. This will be initialized by this + * call. + * @param fGuestFeatures The guest features supported by this client, + * VBOX_SHCL_GF_0_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardConnectEx(PVBGLR3SHCLCMDCTX pCtx, uint64_t fGuestFeatures) +{ + /* + * Intialize the context structure. + */ + pCtx->idClient = 0; + pCtx->fGuestFeatures = fGuestFeatures; + pCtx->fHostFeatures = 0; + pCtx->fUseLegacyProtocol = true; + pCtx->cParmsRecived = 0; + pCtx->idContext = 0; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + /* Init callback table. */ + RT_ZERO(pCtx->Transfers.Callbacks); + /* Indicate that this guest supports Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS; +# ifdef RT_OS_WINDOWS + /* Indicate that on Windows guest OSes we have our own IDataObject implementation which + * integrates nicely into the guest's Windows Explorer showing / handling the Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS_FRONTEND; +# endif + pCtx->Transfers.cbChunkSize = VBOX_SHCL_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */ + pCtx->Transfers.cbMaxChunkSize = VBOX_SHCL_MAX_CHUNK_SIZE; /** @todo Ditto. */ +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + + /* + * First step is connecting to the HGCM service. + */ + int rc = VbglR3ClipboardConnect(&pCtx->idClient); + if (RT_SUCCESS(rc)) + { + /* + * Next is reporting our features. If this fails, assume older host. + */ + rc = VbglR3ClipboardReportFeatures(pCtx->idClient, pCtx->fGuestFeatures, &pCtx->fHostFeatures); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Guest features: %#RX64 - Host features: %#RX64\n", + pCtx->fGuestFeatures, pCtx->fHostFeatures)); + + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_CONTEXT_ID) ) + { + pCtx->fUseLegacyProtocol = false; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_TRANSFERS) ) + { + VBoxShClParmNegotiateChunkSize MsgChunkSize; + do + { + VBGL_HGCM_HDR_INIT(&MsgChunkSize.hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE, + VBOX_SHCL_CPARMS_NEGOTIATE_CHUNK_SIZE); + MsgChunkSize.cb32MaxChunkSize.SetUInt32(pCtx->Transfers.cbMaxChunkSize); + MsgChunkSize.cb32ChunkSize.SetUInt32(0); /* If set to 0, let the host choose. */ + rc = VbglR3HGCMCall(&MsgChunkSize.hdr, sizeof(MsgChunkSize)); + } while (rc == VERR_INTERRUPTED); + if (RT_SUCCESS(rc)) + { + Assert(MsgChunkSize.cb32ChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbChunkSize = RT_MIN(MsgChunkSize.cb32ChunkSize.u.value32, pCtx->Transfers.cbChunkSize); + Assert(MsgChunkSize.cb32MaxChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbMaxChunkSize = RT_MIN(MsgChunkSize.cb32MaxChunkSize.u.value32, pCtx->Transfers.cbMaxChunkSize); + + LogRel2(("Shared Clipboard: Using chunk size %RU32 (maximum is %RU32)\n", + pCtx->Transfers.cbChunkSize, pCtx->Transfers.cbMaxChunkSize)); + } + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS)) + LogRel2(("Shared Clipboard: Host does not support transfers\n")); + } +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID)) + LogRel(("Shared Clipboard: Host does not support context IDs, using legacy protocol\n")); + + pCtx->fUseLegacyProtocol = true; + } + } + else + { + AssertLogRelMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, + ("Reporting features failed: %Rrc\n", rc)); + pCtx->fUseLegacyProtocol = true; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param fGuestFeatures Features to report, VBOX_SHCL_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_SHCL_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_SHCL_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Disconnects from the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Disconnects from the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnectEx(PVBGLR3SHCLCMDCTX pCtx) +{ + int rc = VbglR3ClipboardDisconnect(pCtx->idClient); + if (RT_SUCCESS(rc)) + { + pCtx->idClient = 0; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Receives reported formats from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the + * connection. + * @param pfFormats Where to store the received formats from the host. + */ +static int vbglR3ClipboardFormatsReportRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMATS pfFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormats, VERR_INVALID_POINTER); + + *pfFormats = 0; + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Formats; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt32(VBOX_SHCL_HOST_MSG_FORMATS_REPORT); + Msg.f32Formats.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Formats.GetUInt32(pfFormats); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA_CID message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadDataCid(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt64(VBOX_SHCL_HOST_MSG_READ_DATA_CID); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.id64Context.GetUInt64(&pCtx->idContext); + AssertRC(rc); + int rc2 = Msg.f32Format.GetUInt32(pfFormat); + AssertRCStmt(rc2, rc = rc2); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadData(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id32Msg; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id32Msg.SetUInt32(VBOX_SHCL_HOST_MSG_READ_DATA); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Format.GetUInt32(pfFormat); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Get a host message, legacy version (which does not have VBOX_SHCL_GUEST_FN_MSG_GET). Do not use anymore. + * + * Note: This is the old message which still is being used for the non-URI Shared Clipboard transfers, + * to not break compatibility with older additions / VBox versions. + * + * This will block until a message becomes available. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param pidMsg Where to store the message id. + * @param pfFormats Where to store the format(s) the message applies to. + */ +VBGLR3DECL(int) VbglR3ClipboardGetHostMsgOld(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats) +{ + VBoxShClGetHostMsgOld Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD); + VbglHGCMParmUInt32Set(&Msg.msg, 0); + VbglHGCMParmUInt32Set(&Msg.formats, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg); + if (RT_SUCCESS(rc)) + { + rc2 = VbglHGCMParmUInt32Get(&Msg.formats, pfFormats); + if (RT_SUCCESS(rc2)) + return rc; + } + rc = rc2; + } + *pidMsg = UINT32_MAX - 1; + *pfFormats = UINT32_MAX; + return rc; +} + + +/** + * Reads data from the host clipboard. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format we're requesting the data in. + * @param pvData Where to store the data. + * @param cbData The size of the buffer pointed to by \a pvData. + * @param pcbRead The actual size of the host clipboard data. May be larger than \a cbData. + */ +VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pvData, uint32_t cbData, + uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataRead Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_READ, VBOX_SHCL_CPARMS_DATA_READ); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet( &Msg.Parms.pData, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.Parms.cb32Needed, 0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbRead; + rc = VbglHGCMParmUInt32Get(&Msg.Parms.cb32Needed, &cbRead); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("cbRead=%RU32\n", cbRead)); + + if (cbRead > cbData) + rc = VINF_BUFFER_OVERFLOW; + + *pcbRead = cbRead; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reads clipboard data from the host clipboard. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param uFormat Clipboard format of clipboard data to be read. + * @param pvData Buffer where to store the read data. + * @param cbData Size (in bytes) of data buffer where to store the read data. + * @param pcbRead The actual size of the host clipboard data. + */ +VBGLR3DECL(int) VbglR3ClipboardReadDataEx(PVBGLR3SHCLCMDCTX pCtx, + SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + return VbglR3ClipboardReadData(pCtx->idClient, uFormat, pvData, cbData, pcbRead); +} + + +/** + * Query the host features. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param pfHostFeatures Where to store the host feature, VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_QUERY_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, 0); + VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & RT_BIT_64(63)) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + +/** + * Peeks at the next host message, waiting for one to turn up. + * + * This glosses over the difference between new (6.1) and old (1.3.2) host + * service versions, however it does so by abusing @a pcParameters, so don't use + * it directly when in legacy mode, always pass it on to + * VbglR3ClipboardEventGetNext() or VbglR3ClipboardEventGetNextEx(). + * + * @returns VBox status code. + * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so + * caller just have to repeat this call. + * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck). + * + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use + * for the VM restore check. Optional. + * + * @note Restore check is only performed optimally with a 6.0 host. + */ +VBGLR3DECL(int) VbglR3ClipboardMsgPeekWait(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pidMsg, + uint32_t *pcParameters, uint64_t *pidRestoreCheck) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */ + HGCMFunctionParameter cParameters; + } Msg; + int rc; + if (!pCtx->fUseLegacyProtocol) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT, 2); + VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0); + VbglHGCMParmUInt32Set(&Msg.cParameters, 0); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + LogFlowFunc(("VbglR3HGCMCall -> %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit + && Msg.cParameters.type == VMMDevHGCMParmType_32bit, + ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type), + VERR_INTERNAL_ERROR_3); + + *pidMsg = (uint32_t)Msg.idMsg.u.value64; + *pcParameters = Msg.cParameters.u.value32; + return rc; + } + + /* + * If restored, update pidRestoreCheck. + */ + if (rc == VERR_VM_RESTORED && pidRestoreCheck) + *pidRestoreCheck = Msg.idMsg.u.value64; + } + else + { + /* + * We do some crude stuff here by putting the 2nd parameter (foramts) in the parameter count, + * however it's supposed to be passed directly to VbglR3ClipboardEventGetNext or + * VbglR3ClipboardEventGetNextEx, so that's fine... + */ + rc = VbglR3ClipboardGetHostMsgOld(pCtx->idClient, pidMsg, pcParameters); + if (RT_SUCCESS(rc)) + return rc; + } + + /* + * If interrupted we must cancel the call so it doesn't prevent us from making another one. + */ + if (rc == VERR_INTERRUPTED) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_CANCEL, 0); + int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr)); + AssertRC(rc2); + } + + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; +} + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + +/** + * Reads a root list header from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Where to store the received root list header. + */ +static int vbglR3ClipboardRootListHdrRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(0); + + Msg.cRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(&pRootListHdr->fRoots); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.cRoots.GetUInt32(&pRootListHdr->cRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads a root list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to read. + * @param pRootListEntry Where to store the root list entry read from the host. + */ +static int vbglR3ClipboardRootListEntryRead(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pRootListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(pRootListEntry->fInfo); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pRootListEntry->pszName, pRootListEntry->cbName); + Msg.cbInfo.SetUInt32(pRootListEntry->cbInfo); + Msg.pvInfo.SetPtr(pRootListEntry->pvInfo, pRootListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(&pRootListEntry->fInfo); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + uint32_t cbInfo = 0; + rc = Msg.cbInfo.GetUInt32(&cbInfo); AssertRC(rc); + if (pRootListEntry->cbInfo != cbInfo) + rc = VERR_INVALID_PARAMETER; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads the root list from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param ppRootList Where to store the (allocated) root list. Must be free'd by the caller with + * SharedClipboardTransferRootListFree(). + */ +VBGLR3DECL(int) VbglR3ClipboardRootListRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppRootList, VERR_INVALID_POINTER); + + int rc; + + PSHCLROOTLIST pRootList = ShClTransferRootListAlloc(); + if (pRootList) + { + SHCLROOTLISTHDR srcRootListHdr; + rc = vbglR3ClipboardRootListHdrRead(pCtx, &srcRootListHdr); + if (RT_SUCCESS(rc)) + { + pRootList->Hdr.cRoots = srcRootListHdr.cRoots; + pRootList->Hdr.fRoots = 0; /** @todo Implement this. */ + + if (srcRootListHdr.cRoots) + { + pRootList->paEntries = + (PSHCLROOTLISTENTRY)RTMemAllocZ(srcRootListHdr.cRoots * sizeof(SHCLROOTLISTENTRY)); + if (pRootList->paEntries) + { + for (uint32_t i = 0; i < srcRootListHdr.cRoots; i++) + { + SHCLROOTLISTENTRY *pEntry = &pRootList->paEntries[i]; + AssertPtr(pEntry); + + rc = ShClTransferRootListEntryInit(pEntry); + if (RT_SUCCESS(rc)) + rc = vbglR3ClipboardRootListEntryRead(pCtx, i, pEntry); + + if (RT_FAILURE(rc)) + break; + } + } + else + rc = VERR_NO_MEMORY; + } + } + + if (RT_SUCCESS(rc)) + { + *ppRootList = pRootList; + } + else + ShClTransferRootListFree(pRootList); + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a transfer status from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pEnmDir Where to store the transfer direction for the reported transfer. + * @param pReport Where to store the transfer (status) report. + */ +VBGLR3DECL(int) VbglR3ClipboarTransferStatusRecv(PVBGLR3SHCLCMDCTX pCtx, + PSHCLTRANSFERDIR pEnmDir, PSHCLTRANSFERREPORT pReport) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pReport, VERR_INVALID_POINTER); + AssertPtrReturn(pEnmDir, VERR_INVALID_POINTER); + + VBoxShClTransferStatusMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_TRANSFER_STATUS); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS); + Msg.enmDir.SetUInt32(0); + Msg.enmStatus.SetUInt32(0); + Msg.rc.SetUInt32(0); + Msg.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.enmDir.GetUInt32((uint32_t *)pEnmDir); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.enmStatus.GetUInt32(&pReport->uStatus); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.rc.GetUInt32((uint32_t *)&pReport->rc); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.fFlags.GetUInt32(&pReport->fFlags); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a transfer report from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pTransfer Transfer of report to reply to. + * @param uStatus Tranfer status to reply. + * @param rcTransfer Result code (rc) to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardTransferStatusReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLTRANSFER pTransfer, + SHCLTRANSFERSTATUS uStatus, int rcTransfer) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransfer, VERR_INVALID_POINTER); + + RT_NOREF(pTransfer); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS); + Msg.rc.SetUInt32((uint32_t )rcTransfer); /* int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.TransferStatus.enmStatus.SetUInt32((uint32_t)uStatus); + + LogFlowFunc(("%s\n", ShClTransferStatusToStr(uStatus))); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list header from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfRoots Where to store the root list header flags to use, requested by the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pfRoots) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfRoots, VERR_INVALID_POINTER); + + VBoxShClRootListReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ); + Msg.ReqParms.fRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(pfRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list header request. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Root lsit header to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(pRootListHdr->fRoots); + + Msg.cRoots.SetUInt32(pRootListHdr->cRoots); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param puIndex Where to return the index of the root list entry the host wants to read. + * @param pfInfo Where to return the read flags the host wants to use. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *puIndex, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(puIndex, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClRootListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ); + + Msg.Parms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uIndex.GetUInt32(puIndex); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list entry read request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to reply. + * @param pEntry Actual root list entry to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReply(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pEntry->pszName, pEntry->cbName); + Msg.cbInfo.SetUInt32(pEntry->cbInfo); + Msg.pvInfo.SetPtr(pEntry->pvInfo, pEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to open a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms List open parameters to use for the open request. + * @param phList Where to return the list handle received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_OPEN, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.fList.SetUInt32(0); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms Where to store the open parameters the host wants to use for opening the list handle. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN); + Msg.fList.SetUInt32(0); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fList.GetUInt32(&pOpenParms->fList); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle of (guest) list to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to store the list handle to close, received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list handle close request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle the send the close reply for. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request for closing on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_CLOSE, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to read list header for. + * @param fFlags List header read flags to use. + * @param pListHdr Where to return the list header received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, uint32_t fFlags, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_READ, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(fFlags); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(0); + Msg.cTotalObjects.SetUInt64(0); + Msg.cbTotalSize.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.fFeatures.GetUInt32(&pListHdr->fFeatures); + if (RT_SUCCESS(rc)) + rc = Msg.cTotalObjects.GetUInt64(&pListHdr->cTotalObjects); + if (RT_SUCCESS(rc)) + rc = Msg.cbTotalSize.GetUInt64(&pListHdr->cbTotalSize); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list header on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read list header for. + * @param pfFlags Where to return the List header read flags to use. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClListHdrReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fFlags.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write list header for. + * @param pListHdr List header to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(0); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(pListHdr->fFeatures); + Msg.cTotalObjects.SetUInt64(pListHdr->cTotalObjects); + Msg.cbTotalSize.SetUInt64(pListHdr->cbTotalSize); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request to read a list entry for. + * @param pListEntry Where to return the list entry read from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(0); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.cbInfo.GetUInt32(&pListEntry->cbInfo); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read a list entry for. + * @param pfInfo Where to return the list read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_ENTRY_READ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fInfo.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list entry to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write a list etnry for. + * @param pListEntry List entry to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(pListEntry->fInfo); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Where to store the object open/create parameters received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr(pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fCreate.GetUInt32(&pCreateParms->fCreate); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies a host request to open an object. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle of opened object to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjOpen.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends an object open request to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Object open/create parameters to use for opening the object on the host. + * @param phObj Where to return the object handle from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, + PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_OPEN, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr((void *)pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(pCreateParms->fCreate); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to close from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to an object open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjClose.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle to close on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_CLOSE, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read from an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to read from. + * @param pcbToRead Where to return the amount (in bytes) to read. + * @param pfFlags Where to return the read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj, uint32_t *pcbToRead, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + AssertPtrReturn(pcbToRead, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClObjReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.cbToRead.SetUInt32(0); + Msg.ReqParms.fRead.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phObj); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.cbToRead.GetUInt32(pcbToRead); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fRead.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read from an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to read from. + * @param pvData Buffer where to store the read object data. + * @param cbData Size (in bytes) of buffer. + * @param pcbRead Where to store the amount (in bytes) read from the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + /* pcbRead is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_READ, VBOX_SHCL_CPARMS_OBJ_READ); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.cbData.SetUInt32(cbData); + Msg.pvData.SetPtr(pvData, cbData); + Msg.cbChecksum.SetUInt32(0); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbRead) + { + rc = Msg.cbData.GetUInt32(pcbRead); AssertRC(rc); + AssertReturn(cbData >= *pcbRead, VERR_TOO_MUCH_DATA); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to write to an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to write to. + * @param pvData Buffer of data to write to object. + * @param cbData Size (in bytes) of buffer. + * @param pcbWritten Where to store the amount (in bytes) written to the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjWriteSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + /* cbData can be 0. */ + /* pcbWritten is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_WRITE, VBOX_SHCL_CPARMS_OBJ_WRITE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.pvData.SetPtr(pvData, cbData); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbWritten) + *pcbWritten = cbData; /** @todo For now return all as being written. */ + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/********************************************************************************************************************************* +* Transfer interface implementations * +*********************************************************************************************************************************/ + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceGetRoots(PSHCLTXPROVIDERCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardRootListRead(pCmdCtx, ppRootList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListOpenSend(pCmdCtx, pOpenParms, phList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListCloseSend(pCmdCtx, hList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = ShClTransferListHdrInit(pListHdr); + if (RT_SUCCESS(rc)) + { + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrRead(pCmdCtx, hList, 0 /* fFlags */, pListHdr); + } + else + ShClTransferListHdrDestroy(pListHdr); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTENTRY pEntry) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListEntryRead(pCmdCtx, hList, pEntry); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, + PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjOpenSend(pCmdCtx, pCreateParms, phObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjCloseSend(pCmdCtx, hObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, + SHCLOBJHANDLE hObj, void *pvData, uint32_t cbData, + uint32_t fFlags, uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + RT_NOREF(fFlags); /* Not used yet. */ + + int rc = VbglR3ClipboardObjReadSend(pCmdCtx, hObj, pvData, cbData, pcbRead); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Starts a transfer on the guest side. + * + * @returns VBox status code. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to create transfer for. + * @param uTransferID ID to use for transfer to start. + * @param enmDir Direction of transfer to start. + * @param enmSource Source of transfer to start. + * @param ppTransfer Where to return the transfer object on success. Optional. + */ +static int vbglR3ClipboardTransferStart(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, + PSHCLTRANSFER *ppTransfer) +{ + PSHCLTRANSFER pTransfer; + int rc = ShClTransferCreate(&pTransfer); + if (RT_SUCCESS(rc)) + { + ShClTransferSetCallbacks(pTransfer, &pCmdCtx->Transfers.Callbacks); + + rc = ShClTransferInit(pTransfer, enmDir, enmSource); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferCtxTransferRegisterById(pTransferCtx, pTransfer, uTransferID); + if (RT_SUCCESS(rc)) + { + /* If this is a read transfer (reading data from host), set the interface to use + * our VbglR3 routines here. */ + if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + SHCLTXPROVIDERCREATIONCTX creationCtx; + RT_ZERO(creationCtx); + + creationCtx.Interface.pfnRootsGet = vbglR3ClipboardTransferIfaceGetRoots; + + creationCtx.Interface.pfnListOpen = vbglR3ClipboardTransferIfaceListOpen; + creationCtx.Interface.pfnListClose = vbglR3ClipboardTransferIfaceListClose; + creationCtx.Interface.pfnListHdrRead = vbglR3ClipboardTransferIfaceListHdrRead; + creationCtx.Interface.pfnListEntryRead = vbglR3ClipboardTransferIfaceListEntryRead; + + creationCtx.Interface.pfnObjOpen = vbglR3ClipboardTransferIfaceObjOpen; + creationCtx.Interface.pfnObjClose = vbglR3ClipboardTransferIfaceObjClose; + creationCtx.Interface.pfnObjRead = vbglR3ClipboardTransferIfaceObjRead; + + creationCtx.pvUser = pCmdCtx; + + rc = ShClTransferSetProviderIface(pTransfer, &creationCtx); + } + + if (RT_SUCCESS(rc)) + rc = ShClTransferStart(pTransfer); + } + + if (RT_FAILURE(rc)) + ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + } + } + + if (RT_SUCCESS(rc)) + { + if (ppTransfer) + *ppTransfer = pTransfer; + + LogRel2(("Shared Clipboard: Transfer ID=%RU32 (%s %s) successfully started\n", + uTransferID, + enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "reading from" : "writing to", + enmSource == SHCLSOURCE_LOCAL ? "local" : "remote")); + } + else + LogRel(("Shared Clipboard: Unable to start transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc); + if (RT_SUCCESS(rc)) + rc = rc2; + + if (RT_FAILURE(rc)) + { + ShClTransferDestroy(pTransfer); + pTransfer = NULL; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Stops a transfer on the guest side. + * + * @returns VBox status code, or VERR_NOT_FOUND if transfer has not been found. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to stop transfer for. + * @param uTransferID ID of transfer to stop. + */ +static int vbglR3ClipboardTransferStop(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID) +{ + int rc; + + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, uTransferID); + if (pTransfer) + { + rc = ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Transfer ID=%RU32 successfully stopped\n", uTransferID)); + } + else + LogRel(("Shared Clipboard: Unable to stop transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STOPPED : SHCLTRANSFERSTATUS_ERROR, rc); + AssertRC(rc2); + } + else + rc = VERR_NOT_FOUND; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets transfer callbacks of a Shared Clipboard command context. + * + * @param pCmdCtx Command context to set callbacks for. + * @param pCallbacks Pointer to callback table to set. + */ +VBGLR3DECL(void) VbglR3ClipboardTransferSetCallbacks(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCALLBACKTABLE pCallbacks) +{ + AssertPtrReturnVoid(pCmdCtx); + AssertPtrReturnVoid(pCallbacks); + + ShClTransferCopyCallbacks(&pCmdCtx->Transfers.Callbacks, pCallbacks); +} + +VBGLR3DECL(int) VbglR3ClipboardEventGetNextEx(uint32_t idMsg, uint32_t cParms, + PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + LogFunc(("Handling idMsg=%RU32 (%s), cParms=%RU32\n", idMsg, ShClHostMsgToStr(idMsg), cParms)); + + int rc; + if (!pCmdCtx->fUseLegacyProtocol) + { + bool fErrorSent = false; /* Whether an error has been reported back to the host already. */ + + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_TRANSFER_STATUS: + { + SHCLTRANSFERDIR enmDir; + SHCLTRANSFERREPORT transferReport; + rc = VbglR3ClipboarTransferStatusRecv(pCmdCtx, &enmDir, &transferReport); + if (RT_SUCCESS(rc)) + { + const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + LogFlowFunc(("[Transfer %RU32] enmDir=%RU32, status=%s\n", + uTransferID, enmDir, ShClTransferStatusToStr(transferReport.uStatus))); + + switch (transferReport.uStatus) + { + case SHCLTRANSFERSTATUS_INITIALIZED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_STARTED: + { + SHCLSOURCE enmSource = SHCLSOURCE_INVALID; + + /* The host announces the transfer direction from its point of view, so inverse the direction here. */ + if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) + { + enmDir = SHCLTRANSFERDIR_FROM_REMOTE; + enmSource = SHCLSOURCE_REMOTE; + } + else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + enmDir = SHCLTRANSFERDIR_TO_REMOTE; + enmSource = SHCLSOURCE_LOCAL; + } + else + AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER); + + rc = vbglR3ClipboardTransferStart(pCmdCtx, pTransferCtx, uTransferID, + enmDir, enmSource, NULL /* ppTransfer */); + break; + } + + case SHCLTRANSFERSTATUS_STOPPED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_CANCELED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_KILLED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_ERROR: + { + rc = vbglR3ClipboardTransferStop(pCmdCtx, pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + break; + } + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(rc)) + { + pEvent->u.TransferStatus.enmDir = enmDir; + pEvent->u.TransferStatus.Report = transferReport; + pEvent->u.TransferStatus.uID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS; + + LogRel2(("Shared Clipboard: Received status %s (rc=%Rrc) for transfer ID=%RU32\n", + ShClTransferStatusToStr(pEvent->u.TransferStatus.Report.uStatus), pEvent->u.TransferStatus.Report.rc, + pEvent->u.TransferStatus.uID)); + } + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ: + { + uint32_t fRoots; + rc = VbglR3ClipboardRootListHdrReadReq(pCmdCtx, &fRoots); + + /** @todo Validate / handle fRoots. */ + + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTHDR rootListHdr; + RT_ZERO(rootListHdr); + + rootListHdr.cRoots = ShClTransferRootsCount(pTransfer); + + LogFlowFunc(("cRoots=%RU32\n", rootListHdr.cRoots)); + + rc = VbglR3ClipboardRootListHdrReadReply(pCmdCtx, &rootListHdr); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ: + { + uint32_t uIndex; + uint32_t fInfo; + rc = VbglR3ClipboardRootListEntryReadReq(pCmdCtx, &uIndex, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTENTRY rootListEntry; + rc = ShClTransferRootsEntry(pTransfer, uIndex, &rootListEntry); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardRootListEntryReadReply(pCmdCtx, uIndex, &rootListEntry); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN: + { + SHCLLISTOPENPARMS openParmsList; + rc = ShClTransferListOpenParmsInit(&openParmsList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListOpenRecv(pCmdCtx, &openParmsList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + LogFlowFunc(("pszPath=%s\n", openParmsList.pszPath)); + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListOpenReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + ShClTransferListOpenParmsDestroy(&openParmsList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE: + { + SHCLLISTHANDLE hList; + rc = VbglR3ClipboardListCloseRecv(pCmdCtx, &hList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListClose(pTransfer, hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListCloseReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ: + { + /** @todo Handle filter + list features. */ + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + uint32_t fFlags = 0; + rc = VbglR3ClipboardListHdrReadRecvReq(pCmdCtx, &hList, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLLISTHDR hdrList; + rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrWrite(pCmdCtx, hList, &hdrList); + + ShClTransferListHdrDestroy(&hdrList); + } + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ: + { + LogFlowFunc(("VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ\n")); + + SHCLLISTENTRY entryList; + rc = ShClTransferListEntryInit(&entryList); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + uint32_t fInfo; + rc = VbglR3ClipboardListEntryReadRecvReq(pCmdCtx, &hList, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListRead(pTransfer, hList, &entryList); + if (RT_SUCCESS(rc)) + { + PSHCLFSOBJINFO pObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo; + Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO)); + + RT_NOREF(pObjInfo); + + LogFlowFunc(("\t%s (%RU64 bytes)\n", entryList.pszName, pObjInfo->cbObject)); + + rc = VbglR3ClipboardListEntryWrite(pCmdCtx, hList, &entryList); + } + } + + ShClTransferListEntryDestroy(&entryList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN: + { + SHCLOBJOPENCREATEPARMS openParms; + rc = ShClTransferObjOpenParmsInit(&openParms); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardObjOpenRecv(pCmdCtx, &openParms); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLOBJHANDLE hObj; + rc = ShClTransferObjOpen(pTransfer, &openParms, &hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjOpenReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + ShClTransferObjOpenParmsDestroy(&openParms); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE: + { + SHCLOBJHANDLE hObj; + rc = VbglR3ClipboardObjCloseRecv(pCmdCtx, &hObj); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferObjClose(pTransfer, hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjCloseReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ: + { + SHCLOBJHANDLE hObj; + uint32_t cbBuf; + uint32_t fFlags; + rc = VbglR3ClipboardObjReadRecv(pCmdCtx, &hObj, &cbBuf, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + AssertBreakStmt(pCmdCtx->Transfers.cbChunkSize, rc = VERR_INVALID_PARAMETER); + + const uint32_t cbToRead = RT_MIN(cbBuf, pCmdCtx->Transfers.cbChunkSize); + + LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, fFlags=0x%x -> cbChunkSize=%RU32, cbToRead=%RU32\n", + hObj, cbBuf, fFlags, pCmdCtx->Transfers.cbChunkSize, cbToRead)); + + void *pvBuf = RTMemAlloc(cbToRead); + if (pvBuf) + { + uint32_t cbRead; + rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, fFlags, &cbRead); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardObjWriteSend(pCmdCtx, hObj, pvBuf, cbRead, NULL /* pcbWritten */); + + RTMemFree(pvBuf); + } + else + rc = VERR_NO_MEMORY; + } + + break; + } + + default: + { + rc = VbglR3ClipboardEventGetNext(idMsg, cParms, pCmdCtx, pEvent); + if (RT_FAILURE(rc)) + fErrorSent = true; + break; + } + } + + if ( !fErrorSent + && RT_FAILURE(rc)) + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCmdCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + +VBGLR3DECL(int) VbglR3ClipboardEventGetNext(uint32_t idMsg, uint32_t cParms, PVBGLR3SHCLCMDCTX pCtx, PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + RT_NOREF(cParms); + + int rc; + if (!pCtx->fUseLegacyProtocol) + { + LogFunc(("Handling idMsg=%RU32 (%s)\n", idMsg, ShClHostMsgToStr(idMsg))); + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + { + rc = vbglR3ClipboardFormatsReportRecv(pCtx, &pEvent->u.fReportedFormats); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA_CID: + { + rc = vbglR3ClipboardFetchReadDataCid(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA: + { + rc = vbglR3ClipboardFetchReadData(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_QUIT: + { + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + rc = VINF_SUCCESS; + break; + } + + default: + { + /** @todo r=bird: BUGBUG - need a skip command here! */ + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if (RT_SUCCESS(rc)) + { + /* Copy over our command context to the event. */ + pEvent->cmdCtx = *pCtx; + } + else + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + pEvent->cmdCtx = *pCtx; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Frees (destroys) a formerly allocated Shared Clipboard event. + * + * @returns IPRT status code. + * @param pEvent Event to free (destroy). + */ +VBGLR3DECL(void) VbglR3ClipboardEventFree(PVBGLR3CLIPBOARDEVENT pEvent) +{ + if (!pEvent) + return; + + /* Some messages require additional cleanup. */ + switch (pEvent->enmType) + { + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +/** + * Reports (advertises) guest clipboard formats to the host. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormats The formats to report. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats) +{ + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmReportFormats Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FORMATS, VBOX_SHCL_CPARMS_REPORT_FORMATS); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Formats, fFormats); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * Legacy function kept for compatibility, do not use anymore. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format of the data. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWriteOld Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE_OLD); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet(&Msg.Parms.pData, pv, cb); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param fFormat Clipboard format to send. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteDataEx(PVBGLR3SHCLCMDCTX pCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData) +{ + LogFlowFunc(("ENTER: fFormat=%#x pvData=%p cbData=%#x\n", fFormat, pvData, cbData)); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + if (cbData > 0) + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + + int rc; + if (pCtx->fUseLegacyProtocol) + rc = VbglR3ClipboardWriteData(pCtx->idClient, fFormat, pvData, cbData); + else + { + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWrite Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE); + Msg.Parms.id64Context.SetUInt64(pCtx->idContext); + Msg.Parms.f32Format.SetUInt32(fFormat); + Msg.Parms.pData.SetPtr(pvData, cbData); + + LogFlowFunc(("CID=%RU32\n", pCtx->idContext)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Writes an error to the host. + * + * @returns IPRT status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteError(HGCMCLIENTID idClient, int rcErr) +{ + AssertReturn(idClient, VERR_INVALID_PARAMETER); + + VBoxShClWriteErrorMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_ERROR, VBOX_SHCL_CPARMS_ERROR); + + /** @todo Context ID not used yet. */ + Msg.uContext.SetUInt64(0); + Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + if (RT_FAILURE(rc)) + LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc)); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + + if (RT_FAILURE(rc)) + LogRel(("Shared Clipboard: Reporting error %Rrc to the host failed with %Rrc\n", rcErr, rc)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp new file mode 100644 index 00000000..29f73d0b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp @@ -0,0 +1,56 @@ +/* $Id: VBoxGuestR3LibCoreDump.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core Dumps. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + + +/** + * Write guest core dump. + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3WriteCoreDump(void) +{ + VBGLIOCWRITECOREDUMP Req; + VBGLREQHDR_INIT(&Req.Hdr, WRITE_CORE_DUMP); + Req.u.In.fFlags = 0; + return vbglR3DoIOCtl(VBGL_IOCTL_WRITE_CORE_DUMP, &Req.Hdr, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp new file mode 100644 index 00000000..a5da33e4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp @@ -0,0 +1,133 @@ +/* $Id: VBoxGuestR3LibCpuHotPlug.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, CPU Hot Plugging. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" +#include <iprt/assert.h> +#include <iprt/errcore.h> + + +/** + * Initialize CPU hot plugging. + * + * This will enable the CPU hot plugging events. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugInit(void) +{ + int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_CPU_HOTPLUG, 0); + if (RT_FAILURE(rc)) + return rc; + + VMMDevCpuHotPlugStatusRequest Req; + vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus); + Req.enmStatusType = VMMDevCpuStatusType_Enable; + rc = vbglR3GRPerform(&Req.header); + if (RT_FAILURE(rc)) + VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG); + + return rc; +} + + +/** + * Terminate CPU hot plugging. + * + * This will disable the CPU hot plugging events. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugTerm(void) +{ + /* Clear the events. */ + VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG); + + VMMDevCpuHotPlugStatusRequest Req; + vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus); + Req.enmStatusType = VMMDevCpuStatusType_Disable; + return vbglR3GRPerform(&Req.header); +} + + +/** + * Waits for a CPU hot plugging event and retrieve the data associated with it. + * + * @returns VBox status code. + * @param penmEventType Where to store the event type on success. + * @param pidCpuCore Where to store the CPU core ID on success. + * @param pidCpuPackage Where to store the CPU package ID on success. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugWaitForEvent(VMMDevCpuEventType *penmEventType, uint32_t *pidCpuCore, uint32_t *pidCpuPackage) +{ + AssertPtrReturn(penmEventType, VERR_INVALID_POINTER); + AssertPtrReturn(pidCpuCore, VERR_INVALID_POINTER); + AssertPtrReturn(pidCpuPackage, VERR_INVALID_POINTER); + + uint32_t fEvents = 0; + int rc = VbglR3WaitEvent(VMMDEV_EVENT_CPU_HOTPLUG, RT_INDEFINITE_WAIT, &fEvents); + if (RT_SUCCESS(rc)) + { + /* did we get the right event? */ + if (fEvents & VMMDEV_EVENT_CPU_HOTPLUG) + { + VMMDevGetCpuHotPlugRequest Req; + + /* get the CPU hot plugging request */ + vmmdevInitRequest(&Req.header, VMMDevReq_GetCpuHotPlugRequest); + Req.idCpuCore = UINT32_MAX; + Req.idCpuPackage = UINT32_MAX; + Req.enmEventType = VMMDevCpuEventType_None; + rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *penmEventType = Req.enmEventType; + *pidCpuCore = Req.idCpuCore; + *pidCpuPackage = Req.idCpuPackage; + return VINF_SUCCESS; + } + } + else + rc = VERR_TRY_AGAIN; + } + else if (rc == VERR_TIMEOUT) /* just in case */ + rc = VERR_TRY_AGAIN; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp new file mode 100644 index 00000000..6427e2dd --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp @@ -0,0 +1,222 @@ +/* $Id: VBoxGuestR3LibCredentials.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, user credentials. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include <VBox/log.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Checks whether user credentials are available to the guest or not. + * + * @returns IPRT status value; VINF_SUCCESS if credentials are available, + * VERR_NOT_FOUND if not. Otherwise an error is occurred. + */ +VBGLR3DECL(int) VbglR3CredentialsQueryAvailability(void) +{ + VMMDevCredentials Req; + RT_ZERO(Req); + vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials); + Req.u32Flags |= VMMDEV_CREDENTIALS_QUERYPRESENCE; + + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + if ((Req.u32Flags & VMMDEV_CREDENTIALS_PRESENT) == 0) + rc = VERR_NOT_FOUND; + } + return rc; +} + + +/** + * Retrieves and clears the user credentials for logging into the guest OS. + * + * @returns IPRT status value + * @param ppszUser Receives pointer of allocated user name string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + * @param ppszPassword Receives pointer of allocated user password string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + * @param ppszDomain Receives pointer of allocated domain name string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + */ +VBGLR3DECL(int) VbglR3CredentialsRetrieve(char **ppszUser, char **ppszPassword, char **ppszDomain) +{ + AssertPtrReturn(ppszUser, VERR_INVALID_POINTER); + AssertPtrReturn(ppszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(ppszDomain, VERR_INVALID_POINTER); + + VMMDevCredentials Req; + RT_ZERO(Req); + vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials); + Req.u32Flags |= VMMDEV_CREDENTIALS_READ | VMMDEV_CREDENTIALS_CLEAR; + + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszUser, Req.szUserName); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszPassword, Req.szPassword); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszDomain, Req.szDomain); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTStrFree(*ppszPassword); + } + RTStrFree(*ppszUser); + } + } + return rc; +} + + +/** + * Retrieves and clears the user credentials for logging into the guest OS. + * UTF-16 version. + * + * @returns IPRT status value + * @param ppwszUser Receives pointer of allocated user name string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + * @param ppwszPassword Receives pointer of allocated user password string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + * @param ppwszDomain Receives pointer of allocated domain name string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + */ +VBGLR3DECL(int) VbglR3CredentialsRetrieveUtf16(PRTUTF16 *ppwszUser, PRTUTF16 *ppwszPassword, PRTUTF16 *ppwszDomain) +{ + AssertPtrReturn(ppwszUser, VERR_INVALID_POINTER); + AssertPtrReturn(ppwszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(ppwszDomain, VERR_INVALID_POINTER); + + char *pszUser, *pszPassword, *pszDomain; + int rc = VbglR3CredentialsRetrieve(&pszUser, &pszPassword, &pszDomain); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszUser = NULL; + PRTUTF16 pwszPassword = NULL; + PRTUTF16 pwszDomain = NULL; + + rc = RTStrToUtf16(pszUser, &pwszUser); + if (RT_SUCCESS(rc)) + { + rc = RTStrToUtf16(pszPassword, &pwszPassword); + if (RT_SUCCESS(rc)) + rc = RTStrToUtf16(pszDomain, &pwszDomain); + } + + if (RT_SUCCESS(rc)) + { + *ppwszUser = pwszUser; + *ppwszPassword = pwszPassword; + *ppwszDomain = pwszDomain; + } + else + VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* Passes */); + VbglR3CredentialsDestroy(pszUser, pszPassword, pszDomain, 3 /* Passes */); + } + + return rc; +} + + +/** + * Clears and frees the three strings. + * + * @param pszUser Receives pointer of the user name string to destroy. + * Optional. + * @param pszPassword Receives pointer of the password string to destroy. + * Optional. + * @param pszDomain Receives pointer of allocated domain name string. + * Optional. + * @param cPasses Number of wipe passes. The more the better + slower. + */ +VBGLR3DECL(void) VbglR3CredentialsDestroy(char *pszUser, char *pszPassword, char *pszDomain, uint32_t cPasses) +{ + /* wipe first */ + if (pszUser) + RTMemWipeThoroughly(pszUser, strlen(pszUser) + 1, cPasses); + if (pszPassword) + RTMemWipeThoroughly(pszPassword, strlen(pszPassword) + 1, cPasses); + if (pszDomain) + RTMemWipeThoroughly(pszDomain, strlen(pszDomain) + 1, cPasses); + + /* then free. */ + RTStrFree(pszUser); + RTStrFree(pszPassword); + RTStrFree(pszDomain); +} + + +/** + * Clears and frees the three strings. UTF-16 version. + * + * @param pwszUser Receives pointer of the user name string to destroy. + * Optional. + * @param pwszPassword Receives pointer of the password string to destroy. + * Optional. + * @param pwszDomain Receives pointer of allocated domain name string. + * Optional. + * @param cPasses Number of wipe passes. The more the better + slower. + */ +VBGLR3DECL(void) VbglR3CredentialsDestroyUtf16(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, + uint32_t cPasses) +{ + /* wipe first */ + if (pwszUser) + RTMemWipeThoroughly(pwszUser, (RTUtf16Len(pwszUser) + 1) * sizeof(RTUTF16), cPasses); + if (pwszPassword) + RTMemWipeThoroughly(pwszPassword, (RTUtf16Len(pwszPassword) + 1) * sizeof(RTUTF16), cPasses); + if (pwszDomain) + RTMemWipeThoroughly(pwszDomain, (RTUtf16Len(pwszDomain) + 1) * sizeof(RTUTF16), cPasses); + + /* then free. */ + RTUtf16Free(pwszUser); + RTUtf16Free(pwszPassword); + RTUtf16Free(pwszDomain); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp new file mode 100644 index 00000000..77e1a7d4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp @@ -0,0 +1,264 @@ +/** $Id: VBoxGuestR3LibDaemonize.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> + +# include <iprt/alloca.h> +# include <iprt/string.h> + +#elif defined(RT_OS_WINDOWS) +# error "PORTME" + +#else /* the unices */ +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/wait.h> +# include <stdio.h> +# include <fcntl.h> +# include <stdlib.h> +# include <unistd.h> +# include <signal.h> +# include <errno.h> +#endif + +#include <iprt/process.h> +#include <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Daemonize the process for running in the background. + * + * This is supposed to do the same job as the BSD daemon() call. + * + * @returns 0 on success + * + * @param fNoChDir Pass false to change working directory to root. + * @param fNoClose Pass false to redirect standard file streams to /dev/null. + * @param fRespawn Restart the daemonised process after five seconds if it + * terminates abnormally. + * @param pcRespawn Where to store a count of how often we have respawned, + * intended for avoiding error spamming. Optional. + * + * @todo Use RTProcDaemonize instead of this. + * @todo Implement fRespawn on OS/2. + * @todo Make the respawn interval configurable. But not until someone + * actually needs that. + */ +VBGLR3DECL(int) VbglR3Daemonize(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn) +{ +#if defined(RT_OS_OS2) + PPIB pPib; + PTIB pTib; + DosGetInfoBlocks(&pTib, &pPib); + + AssertRelease(!fRespawn); + /* Get the full path to the executable. */ + char szExe[CCHMAXPATH]; + APIRET rc = DosQueryModuleName(pPib->pib_hmte, sizeof(szExe), szExe); + if (rc) + return RTErrConvertFromOS2(rc); + + /* calc the length of the command line. */ + char *pch = pPib->pib_pchcmd; + size_t cch0 = strlen(pch); + pch += cch0 + 1; + size_t cch1 = strlen(pch); + pch += cch1 + 1; + char *pchArgs; + if (cch1 && *pch) + { + do pch = strchr(pch, '\0') + 1; + while (*pch); + + size_t cchTotal = pch - pPib->pib_pchcmd; + pchArgs = (char *)alloca(cchTotal + sizeof("--daemonized\0\0")); + memcpy(pchArgs, pPib->pib_pchcmd, cchTotal - 1); + memcpy(pchArgs + cchTotal - 1, "--daemonized\0\0", sizeof("--daemonized\0\0")); + } + else + { + size_t cchTotal = pch - pPib->pib_pchcmd + 1; + pchArgs = (char *)alloca(cchTotal + sizeof(" --daemonized ")); + memcpy(pchArgs, pPib->pib_pchcmd, cch0 + 1); + pch = pchArgs + cch0 + 1; + memcpy(pch, " --daemonized ", sizeof(" --daemonized ") - 1); + pch += sizeof(" --daemonized ") - 1; + if (cch1) + memcpy(pch, pPib->pib_pchcmd + cch0 + 1, cch1 + 2); + else + pch[0] = pch[1] = '\0'; + } + + /* spawn a detach process */ + char szObj[128]; + RESULTCODES ResCodes = { 0, 0 }; + szObj[0] = '\0'; + rc = DosExecPgm(szObj, sizeof(szObj), EXEC_BACKGROUND, (PCSZ)pchArgs, NULL, &ResCodes, (PCSZ)szExe); + if (rc) + { + /** @todo Change this to some standard log/print error?? */ + /* VBoxServiceError("DosExecPgm failed with rc=%d and szObj='%s'\n", rc, szObj); */ + return RTErrConvertFromOS2(rc); + } + DosExit(EXIT_PROCESS, 0); + return VERR_GENERAL_FAILURE; + +#elif defined(RT_OS_WINDOWS) +# error "PORTME" + +#else /* the unices */ + /* + * Fork the child process in a new session and quit the parent. + * + * - fork once and create a new session (setsid). This will detach us + * from the controlling tty meaning that we won't receive the SIGHUP + * (or any other signal) sent to that session. + * - The SIGHUP signal is ignored because the session/parent may throw + * us one before we get to the setsid. + * - When the parent exit(0) we will become an orphan and re-parented to + * the init process. + * - Because of the Linux / System V semantics of assigning the controlling + * tty automagically when a session leader first opens a tty, we will + * fork() once more on Linux to get rid of the session leadership role. + */ + + struct sigaction OldSigAct; + struct sigaction SigAct; + RT_ZERO(SigAct); + SigAct.sa_handler = SIG_IGN; + int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct); + + pid_t pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid != 0) + exit(0); + + /* + * The orphaned child becomes is reparented to the init process. + * We create a new session for it (setsid), point the standard + * file descriptors to /dev/null, and change to the root directory. + */ + pid_t newpgid = setsid(); + int SavedErrno = errno; + if (rcSigAct != -1) + sigaction(SIGHUP, &OldSigAct, NULL); + if (newpgid == -1) + return RTErrConvertFromErrno(SavedErrno); + + if (!fNoClose) + { + /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */ + int fd = open("/dev/null", O_RDWR); + if (fd == -1) /* paranoia */ + { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + fd = open("/dev/null", O_RDWR); + } + if (fd != -1) + { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); + } + } + + if (!fNoChDir) + { + int rcShutUpGcc = chdir("/"); + RT_NOREF_PV(rcShutUpGcc); + } + + /* + * Change the umask - this is non-standard daemon() behavior. + */ + umask(027); + +# ifdef RT_OS_LINUX + /* + * And fork again to lose session leader status (non-standard daemon() + * behaviour). + */ + pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid != 0) + exit(0); +# endif /* RT_OS_LINUX */ + + if (fRespawn) + { + /* We implement re-spawning as a third fork(), with the parent process + * monitoring the child and re-starting it after a delay if it exits + * abnormally. */ + unsigned cRespawn = 0; + for (;;) + { + int iStatus, rcWait; + + if (pcRespawn != NULL) + *pcRespawn = cRespawn; + pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid == 0) + return VINF_SUCCESS; + do + rcWait = waitpid(pid, &iStatus, 0); + while (rcWait == -1 && errno == EINTR); + if (rcWait == -1) + exit(1); + if (WIFEXITED(iStatus) && WEXITSTATUS(iStatus) == 0) + exit(0); + sleep(5); + ++cRespawn; + } + } + return VINF_SUCCESS; +#endif +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp new file mode 100644 index 00000000..90d68b23 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp @@ -0,0 +1,1948 @@ +/* $Id: VBoxGuestR3LibDragAndDrop.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop. + */ + +/* + * Copyright (C) 2011-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/path.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/uri.h> +#include <iprt/thread.h> + +#include <iprt/cpp/list.h> +#include <iprt/cpp/ministring.h> + +#ifdef LOG_GROUP + #undef LOG_GROUP +#endif +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/log.h> + +#include <VBox/VBoxGuestLib.h> +#include <VBox/GuestHost/DragAndDrop.h> +#include <VBox/HostServices/DragAndDropSvc.h> + +using namespace DragAndDropSvc; + +#include "VBoxGuestR3LibInternal.h" + + +/********************************************************************************************************************************* +* Private internal functions * +*********************************************************************************************************************************/ + +/** + * Receives the next upcoming message for a given DnD context. + * + * @returns IPRT status code. + * Will return VERR_CANCELLED (implemented by the host service) if we need to bail out. + * @param pCtx DnD context to use. + * @param puMsg Where to store the message type. + * @param pcParms Where to store the number of parameters required for receiving the message. + * @param fWait Whether to wait (block) for a new message to arrive or not. + */ +static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(puMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParms, VERR_INVALID_POINTER); + + int rc; + + do + { + HGCMMsgGetNext Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GET_NEXT_HOST_MSG, 3); + Msg.uMsg.SetUInt32(0); + Msg.cParms.SetUInt32(0); + Msg.fBlock.SetUInt32(fWait ? 1 : 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc); + rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc); + } + + LogRel(("DnD: Received message %s (%#x) from host\n", DnDHostMsgToStr(*puMsg), *puMsg)); + + } while (rc == VERR_INTERRUPTED); + + return rc; +} + + +/** + * Sends a DnD error back to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3DnDSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgGHError Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_EVT_ERROR, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * Never return an error if the host did not accept the error at the current + * time. This can be due to the host not having any appropriate callbacks + * set which would handle that error. + * + * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it + * doesn't an appropriate callback. The code used to ignore ALL errors + * the host would return, also relevant ones. + */ + if (RT_FAILURE(rc)) + LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc)); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a so-called "action message" from the host. + * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages". + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param uMsg Which kind of message to receive. + * @param puScreenID Where to store the host screen ID the message is bound to. Optional. + * @param puX Where to store the absolute X coordinates. Optional. + * @param puY Where to store the absolute Y coordinates. Optional. + * @param puDefAction Where to store the default action to perform. Optional. + * @param puAllActions Where to store the available actions. Optional. + * @param ppszFormats Where to store List of formats. Optional. + * @param pcbFormats Size (in bytes) of where to store the list of formats. Optional. + * + * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1. + * This was part of the initial protocol and needs to go. + */ +static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx, + uint32_t uMsg, + uint32_t *puScreenID, + uint32_t *puX, + uint32_t *puY, + uint32_t *puDefAction, + uint32_t *puAllActions, + char **ppszFormats, + uint32_t *pcbFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* The rest is optional. */ + + const uint32_t cbFormatsTmp = pCtx->cbMaxChunkSize; + + char *pszFormatsTmp = static_cast<char *>(RTMemAlloc(cbFormatsTmp)); + if (!pszFormatsTmp) + return VERR_NO_MEMORY; + + HGCMMsgHGAction Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 8); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uScreenId.SetUInt32(0); + Msg.u.v3.uX.SetUInt32(0); + Msg.u.v3.uY.SetUInt32(0); + Msg.u.v3.uDefAction.SetUInt32(0); + Msg.u.v3.uAllActions.SetUInt32(0); + Msg.u.v3.pvFormats.SetPtr(pszFormatsTmp, cbFormatsTmp); + Msg.u.v3.cbFormats.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (RT_SUCCESS(rc) && puScreenID) + rc = Msg.u.v3.uScreenId.GetUInt32(puScreenID); + if (RT_SUCCESS(rc) && puX) + rc = Msg.u.v3.uX.GetUInt32(puX); + if (RT_SUCCESS(rc) && puY) + rc = Msg.u.v3.uY.GetUInt32(puY); + if (RT_SUCCESS(rc) && puDefAction) + rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction); + if (RT_SUCCESS(rc) && puAllActions) + rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); + if (RT_SUCCESS(rc) && pcbFormats) + rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormats); + + if (RT_SUCCESS(rc)) + { + if (ppszFormats) + { + *ppszFormats = RTStrDup(pszFormatsTmp); + if (!*ppszFormats) + rc = VERR_NO_MEMORY; + } + } + } + + RTStrFree(pszFormatsTmp); + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_EVT_LEAVE message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + */ +static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGLeave Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_EVT_LEAVE, 1); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_EVT_CANCEL message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + */ +static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGCancel Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_CANCEL, 1); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_SND_DIR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszDirname Where to store the directory name of the directory being created. + * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created. + * @param pcbDirnameRecv Size (in bytes) of the actual directory name received. + * @param pfMode Where to store the directory creation mode. + */ +static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx, + char *pszDirname, + uint32_t cbDirname, + uint32_t *pcbDirnameRecv, + uint32_t *pfMode) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszDirname, VERR_INVALID_POINTER); + AssertReturn(cbDirname, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + + HGCMMsgHGSendDir Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DIR, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname); + Msg.u.v3.cbName.SetUInt32(cbDirname); + Msg.u.v3.fMode.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc); + rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc); + + AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA); + } + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_SND_FILE_DATA message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Where to store the file data chunk. + * @param cbData Size (in bytes) of where to store the data chunk. + * @param pcbDataRecv Size (in bytes) of the actual data chunk size received. + */ +static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx, + void *pvData, + uint32_t cbData, + uint32_t *pcbDataRecv) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER); + + HGCMMsgHGSendFileData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvData.SetPtr(pvData, cbData); + Msg.u.v3.cbData.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc); + AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA); + /** @todo Add checksum support. */ + } + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_FILE_HDR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszFilename Where to store the file name of the file being transferred. + * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred. + * @param puFlags File transfer flags. Currently not being used. + * @param pfMode Where to store the file creation mode. + * @param pcbTotal Where to store the file size (in bytes). + */ +static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx, + char *pszFilename, + uint32_t cbFilename, + uint32_t *puFlags, + uint32_t *pfMode, + uint64_t *pcbTotal) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(cbFilename, VERR_INVALID_PARAMETER); + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + AssertReturn(pcbTotal, VERR_INVALID_POINTER); + + HGCMMsgHGSendFileHdr Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_HDR, 6); + Msg.uContext.SetUInt32(0); /** @todo Not used yet. */ + Msg.pvName.SetPtr(pszFilename, cbFilename); + Msg.cbName.SetUInt32(cbFilename); + Msg.uFlags.SetUInt32(0); + Msg.fMode.SetUInt32(0); + Msg.cbTotal.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Get context ID. */ + rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc); + rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc); + rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc); + } + + return rc; +} + +/** + * Host -> Guest + * Helper function for receiving URI data from the host. Do not call directly. + * This function also will take care of the file creation / locking on the guest. + * + * @returns IPRT status code. + * @retval VERR_CANCELLED if the transfer was cancelled by the host. + * @param pCtx DnD context to use. + * @param pDataHdr DnD data header to use. Needed for accounting. + * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking. + */ +static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES pDroppedFiles) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER); + + /* Only count the raw data minus the already received meta data. */ + Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta); + uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta; + uint64_t cToRecvObjs = pDataHdr->cObjects; + + LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n", + cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta)); + + /* Anything to do at all? */ + /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just + * a bunch of 0-byte files to be transferred. */ + if (!cToRecvObjs) + return VINF_SUCCESS; + + LogRel2(("DnD: Receiving URI data started\n")); + + /* + * Allocate temporary chunk buffer. + */ + uint32_t cbChunkMax = pCtx->cbMaxChunkSize; + void *pvChunk = RTMemAlloc(cbChunkMax); + if (!pvChunk) + return VERR_NO_MEMORY; + uint32_t cbChunkRead = 0; + + uint64_t cbFileSize = 0; /* Total file size (in bytes). */ + uint64_t cbFileWritten = 0; /* Written bytes. */ + + const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles); + AssertPtr(pszDropDir); + + int rc; + + /* + * Enter the main loop of retieving files + directories. + */ + DNDTRANSFEROBJECT objCur; + RT_ZERO(objCur); + + char szPathName[RTPATH_MAX] = { 0 }; + uint32_t cbPathName = 0; + uint32_t fFlags = 0; + uint32_t fMode = 0; + + do + { + LogFlowFunc(("Waiting for new message ...\n")); + + uint32_t uNextMsg; + uint32_t cNextParms; + rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, true /* fWait */); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms)); + + switch (uNextMsg) + { + case HOST_DND_FN_HG_SND_DIR: + { + rc = vbglR3DnDHGRecvDir(pCtx, + szPathName, + sizeof(szPathName), + &cbPathName, + &fMode); + LogFlowFunc(("HOST_DND_FN_HG_SND_DIR: " + "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n", + szPathName, cbPathName, fMode, rc)); + + char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName); + if (pszPathAbs) + { +#ifdef RT_OS_WINDOWS + uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL; +#else + uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU; +#endif + rc = RTDirCreate(pszPathAbs, fCreationMode, 0); + if (RT_SUCCESS(rc)) + rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs); + + if (RT_SUCCESS(rc)) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + break; + } + case HOST_DND_FN_HG_SND_FILE_HDR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_DATA: + { + if (uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR) + { + rc = vbglR3DnDHGRecvFileHdr(pCtx, + szPathName, + sizeof(szPathName), + &fFlags, + &fMode, + &cbFileSize); + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_HDR: " + "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n", + szPathName, fFlags, fMode, cbFileSize, rc)); + } + else + { + rc = vbglR3DnDHGRecvFileData(pCtx, + pvChunk, + cbChunkMax, + &cbChunkRead); + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: " + "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc)); + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR) + { + char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName); + if (pszPathAbs) + { + LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n", + szPathName, cbPathName, fMode, cbFileSize)); + + uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE + | RTFILE_O_CREATE_REPLACE; + + /* Is there already a file open, e.g. in transfer? */ + if (!DnDTransferObjectIsOpen(&objCur)) + { +#ifdef RT_OS_WINDOWS + uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL; +#else + uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR; +#endif + rc = DnDTransferObjectInitEx(&objCur, DNDTRANSFEROBJTYPE_FILE, + pszDropDir /* Source (base) path */, szPathName /* Destination path */); + if (RT_SUCCESS(rc)) + { + rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE); + if (RT_SUCCESS(rc)) + { + rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs); + if (RT_SUCCESS(rc)) + { + cbFileWritten = 0; + DnDTransferObjectSetSize(&objCur, cbFileSize); + } + } + } + } + else + { + AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur))); + rc = VERR_WRONG_ORDER; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_FN_HG_SND_FILE_DATA + && cbChunkRead) + { + uint32_t cbChunkWritten; + rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: " + "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n", + cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize)); + + cbFileWritten += cbChunkWritten; + + Assert(cbChunkRead <= cbToRecvBytes); + cbToRecvBytes -= cbChunkRead; + } + } + + /* Data transfer complete? Close the file. */ + bool fClose = DnDTransferObjectIsComplete(&objCur); + if (fClose) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + /* Only since protocol v2 we know the file size upfront. */ + Assert(cbFileWritten <= cbFileSize); + + if (fClose) + { + LogFlowFunc(("Closing file\n")); + DnDTransferObjectDestroy(&objCur); + } + + break; + } + case HOST_DND_FN_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + rc = VERR_CANCELLED; + break; + } + default: + { + LogRel(("DnD: Warning: Message %s (%#x) from host not supported or in wrong order\n", DnDHostMsgToStr(uNextMsg), uNextMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + } + + if (RT_FAILURE(rc)) + break; + + LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs)); + if ( !cbToRecvBytes + && !cToRecvObjs) + { + break; + } + + } while (RT_SUCCESS(rc)); + + LogFlowFunc(("Loop ended with %Rrc\n", rc)); + + /* All URI data processed? */ + if (rc == VERR_NO_DATA) + rc = VINF_SUCCESS; + + /* Delete temp buffer again. */ + if (pvChunk) + RTMemFree(pvChunk); + + /* Cleanup on failure or if the user has canceled the operation or + * something else went wrong. */ + if (RT_FAILURE(rc)) + { + if (rc == VERR_CANCELLED) + LogRel2(("DnD: Receiving URI data was cancelled by the host\n")); + else + LogRel(("DnD: Receiving URI data failed with %Rrc\n", rc)); + + DnDTransferObjectDestroy(&objCur); + DnDDroppedFilesRollback(pDroppedFiles); + } + else + { + LogRel2(("DnD: Receiving URI data finished\n")); + + /** @todo Compare the transfer list with the dirs/files we really transferred. */ + /** @todo Implement checksum verification, if any. */ + } + + /* + * Close the dropped files directory. + * Don't try to remove it here, however, as the files are being needed + * by the client's drag'n drop operation lateron. + */ + int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */); + if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */ + LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_DATA message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr DnD data header to use. Need for accounting and stuff. + * @param pvData Where to store the received data from the host. + * @param cbData Size (in bytes) of where to store the received data. + * @param pcbDataRecv Where to store the received amount of data (in bytes). + */ +static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, + void *pvData, uint32_t cbData, uint32_t *pcbDataRecv) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER); + + LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData)); + + HGCMMsgHGSendData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvData.SetPtr(pvData, cbData); + Msg.u.v3.cbData.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbDataRecv; + rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + /** @todo Use checksum for validating the received data. */ + if (pcbDataRecv) + *pcbDataRecv = cbDataRecv; + LogFlowFuncLeaveRC(rc); + return rc; + } + } + + /* failure */ + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_DATA_HDR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr Where to store the receivd DnD data header. + */ +static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + + Assert(pCtx->uProtocolDeprecated >= 3); /* Only for protocol v3 and up. */ + + HGCMMsgHGSendDataHdr Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA_HDR, 12); + Msg.uContext.SetUInt32(0); + Msg.uFlags.SetUInt32(0); + Msg.uScreenId.SetUInt32(0); + Msg.cbTotal.SetUInt64(0); + Msg.cbMeta.SetUInt32(0); + Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt); + Msg.cbMetaFmt.SetUInt32(0); + Msg.cObjects.SetUInt64(0); + Msg.enmCompression.SetUInt32(0); + Msg.enmChecksumType.SetUInt32(0); + Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum); + Msg.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* Msg.uContext not needed here. */ + Msg.uFlags.GetUInt32(&pDataHdr->uFlags); + Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId); + Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal); + Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta); + Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt); + Msg.cObjects.GetUInt64(&pDataHdr->cObjects); + Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression); + Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType); + Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Helper function for receiving the actual DnD data from the host. Do not call directly. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr Where to store the data header data. + * @param ppvData Returns the received meta data. Needs to be free'd by the caller. + * @param pcbData Where to store the size (in bytes) of the received meta data. + */ +static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, + void **ppvData, uint64_t *pcbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(ppvData, VERR_INVALID_POINTER); + AssertPtrReturn(pcbData, VERR_INVALID_POINTER); + + int rc; + uint32_t cbDataRecv; + + LogFlowFuncEnter(); + + rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr); + if (RT_FAILURE(rc)) + return rc; + + LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects)); + if (pDataHdr->cbMeta) + { + uint64_t cbDataTmp = 0; + void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta); + if (!pvDataTmp) + rc = VERR_NO_MEMORY; + + if (RT_SUCCESS(rc)) + { + uint8_t *pvDataOff = (uint8_t *)pvDataTmp; + while (cbDataTmp < pDataHdr->cbMeta) + { + rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr, + pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize), + &cbDataRecv); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp)); + Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta); + cbDataTmp += cbDataRecv; + pvDataOff += cbDataRecv; + } + else + break; + } + + if (RT_SUCCESS(rc)) + { + Assert(cbDataTmp == pDataHdr->cbMeta); + + LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp)); + + *ppvData = pvDataTmp; + *pcbData = cbDataTmp; + } + else + RTMemFree(pvDataTmp); + } + } + else + { + *ppvData = NULL; + *pcbData = 0; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Main function for receiving the actual DnD data from the host. + * + * @returns VBox status code. + * @retval VERR_CANCELLED if cancelled by the host. + * @param pCtx DnD context to use. + * @param pMeta Where to store the actual meta data received from the host. + */ +static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx, + PVBGLR3GUESTDNDMETADATA pMeta) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pMeta, VERR_INVALID_POINTER); + + AssertMsgReturn(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"), VERR_INVALID_PARAMETER); + + VBOXDNDDATAHDR dataHdr; + RT_ZERO(dataHdr); + dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize; + dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt); + if (!dataHdr.pvMetaFmt) + return VERR_NO_MEMORY; + + void *pvData = NULL; + uint64_t cbData = 0; + int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData); + if (RT_SUCCESS(rc)) + { + LogRel2(("DnD: Received %RU64 bytes meta data in format '%s'\n", cbData, (char *)dataHdr.pvMetaFmt)); + + /** + * Check if this is an URI event. If so, let VbglR3 do all the actual + * data transfer + file/directory creation internally without letting + * the caller know. + * + * This keeps the actual (guest OS-)dependent client (like VBoxClient / + * VBoxTray) small by not having too much redundant code. + */ + Assert(dataHdr.cbMetaFmt); + AssertPtr(dataHdr.pvMetaFmt); + if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */ + { + DNDDROPPEDFILES droppedFiles; + RT_ZERO(droppedFiles); + + rc = DnDDroppedFilesInit(&droppedFiles); + if (RT_SUCCESS(rc)) + rc = DnDDroppedFilesOpenTemp(&droppedFiles, DNDURIDROPPEDFILE_FLAGS_NONE); + + if (RT_FAILURE(rc)) + { + LogRel(("DnD: Initializing dropped files directory failed with %Rrc\n", rc)); + } + else + { + AssertPtr(pvData); + Assert(cbData); + + /* Use the dropped files directory as the root directory for the current transfer. */ + rc = DnDTransferListInitEx(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles), + DNDTRANSFERLISTFMT_NATIVE); + if (RT_SUCCESS(rc)) + { + rc = DnDTransferListAppendRootsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData, + DND_PATH_SEPARATOR_STR, 0 /* fFlags */); + if (RT_SUCCESS(rc)) + { + rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles); + if (RT_SUCCESS(rc)) + { + pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST; + } + } + } + } + } + else /* Raw data. */ + { + pMeta->u.Raw.cbMeta = cbData; + pMeta->u.Raw.pvMeta = pvData; + + pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW; + } + + if (pvData) + RTMemFree(pvData); + } + + if (dataHdr.pvMetaFmt) + RTMemFree(dataHdr.pvMetaFmt); + + if (RT_FAILURE(rc)) + { + if (rc != VERR_CANCELLED) + { + LogRel(("DnD: Receiving data failed with %Rrc\n", rc)); + + int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc); + if (RT_FAILURE(rc2)) + LogRel(("DnD: Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2)); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host + * Utility function to receive the HOST_DND_FN_GH_REQ_PENDING message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param puScreenID For which screen on the host the request is for. Optional. + */ +static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* pScreenID is optional. */ + + HGCMMsgGHReqPending Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_REQ_PENDING, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uScreenId.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (puScreenID) + rc = Msg.u.v3.uContext.GetUInt32(puScreenID); + } + + return rc; +} + +/** + * Guest -> Host + * Utility function to receive the HOST_DND_FN_GH_EVT_DROPPED message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param ppszFormat Requested data format from the host. Optional. + * @param pcbFormat Size of requested data format (in bytes). Optional. + * @param puAction Requested action from the host. Optional. + */ +static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx, + char **ppszFormat, + uint32_t *pcbFormat, + uint32_t *puAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* The rest is optional. */ + + const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize; + + char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp)); + if (!pszFormatTmp) + return VERR_NO_MEMORY; + + HGCMMsgGHDropped Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_EVT_DROPPED, 4); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp); + Msg.u.v3.cbFormat.SetUInt32(0); + Msg.u.v3.uAction.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (pcbFormat) + rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat); + if (RT_SUCCESS(rc) && puAction) + rc = Msg.u.v3.uAction.GetUInt32(puAction); + + if (RT_SUCCESS(rc)) + { + *ppszFormat = RTStrDup(pszFormatTmp); + if (!*ppszFormat) + rc = VERR_NO_MEMORY; + } + } + + RTMemFree(pszFormatTmp); + + return rc; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + + +/********************************************************************************************************************************* +* Public functions * +*********************************************************************************************************************************/ + +/** + * Connects a DnD context to the DnD host service. + * + * @returns IPRT status code. + * @param pCtx DnD context to connect. + */ +VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + /* Initialize header */ + int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID); + if (RT_FAILURE(rc)) + return rc; + Assert(pCtx->uClientID); + + /* Set the default protocol version we would like to use. + * Deprecated since VBox 6.1.x, but let this set to 3 to (hopefully) not break things. */ + pCtx->uProtocolDeprecated = 3; + + pCtx->fHostFeatures = VBOX_DND_HF_NONE; + pCtx->fGuestFeatures = VBOX_DND_GF_NONE; + + /* + * Get the VM's session ID. + * This is not fatal in case we're running with an ancient VBox version. + */ + pCtx->uSessionID = 0; + int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2); + LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2)); + + /* + * Try sending the connect message to tell the protocol version to use. + * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which + * does not implement this command. + */ + HGCMMsgConnect Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_CONNECT, 3); + Msg.u.v3.uContext.SetUInt32(0); /** @todo Context ID not used yet. */ + Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocolDeprecated); /* Deprecated since VBox 6.1.x. */ + Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */ + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* Set the protocol version we're going to use as told by the host. */ + rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocolDeprecated); AssertRC(rc); + + /* + * Next is reporting our features. If this fails, assume older host. + */ + rc2 = VbglR3DnDReportFeatures(pCtx->uClientID, pCtx->fGuestFeatures, &pCtx->fHostFeatures); + if (RT_SUCCESS(rc2)) + { + LogRel2(("DnD: Guest features: %#RX64 - Host features: %#RX64\n", + pCtx->fGuestFeatures, pCtx->fHostFeatures)); + } + else /* Failing here is not fatal; might be running with an older host. */ + { + AssertLogRelMsg(rc2 == VERR_NOT_SUPPORTED || rc2 == VERR_NOT_IMPLEMENTED, + ("Reporting features failed: %Rrc\n", rc2)); + } + + pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */ + } + else + pCtx->uProtocolDeprecated = 0; /* We're using protocol v0 (initial draft) as a fallback. */ + + LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocolDeprecated, rc)); + return rc; +} + +/** + * Disconnects a given DnD context from the DnD host service. + * + * @returns IPRT status code. + * @param pCtx DnD context to disconnect. + * The context is invalid afterwards on successful disconnection. + */ +VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + if (!pCtx->uClientID) /* Already disconnected? Bail out early. */ + return VINF_SUCCESS; + + int rc = VbglR3HGCMDisconnect(pCtx->uClientID); + if (RT_SUCCESS(rc)) + pCtx->uClientID = 0; + + return rc; +} + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3DnDConnect(). + * @param fGuestFeatures Features to report, VBOX_DND_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_DND_HF_XXX. + */ +VBGLR3DECL(int) VbglR3DnDReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_DND_FN_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_DND_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_DND_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + +/** + * Receives the next upcoming DnD event. + * + * This is the main function DnD clients call in order to implement any DnD functionality. + * The purpose of it is to abstract the actual DnD protocol handling as much as possible from + * the clients -- those only need to react to certain events, regardless of how the underlying + * protocol actually is working. + * + * @returns VBox status code. + * @param pCtx DnD context to work with. + * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling + * VbglR3DnDEventFree() when done. + */ +VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppEvent, VERR_INVALID_POINTER); + + PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT)); + if (!pEvent) + return VERR_NO_MEMORY; + + uint32_t uMsg = 0; + uint32_t cParms = 0; + int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */); + if (RT_SUCCESS(rc)) + { + /* Check for VM session change. */ + uint64_t uSessionID; + int rc2 = VbglR3GetSessionId(&uSessionID); + if ( RT_SUCCESS(rc2) + && (uSessionID != pCtx->uSessionID)) + { + LogRel2(("DnD: VM session ID changed to %RU64\n", uSessionID)); + rc = VbglR3DnDDisconnect(pCtx); + if (RT_SUCCESS(rc)) + rc = VbglR3DnDConnect(pCtx); + } + } + + if (rc == VERR_CANCELLED) /* Host service told us that we have to bail out. */ + { + LogRel2(("DnD: Host service requested termination\n")); + + pEvent->enmType = VBGLR3DNDEVENTTYPE_QUIT; + *ppEvent = pEvent; + + return VINF_SUCCESS; + } + + if (RT_SUCCESS(rc)) + { + LogFunc(("Handling uMsg=%RU32\n", uMsg)); + + switch(uMsg) + { + case HOST_DND_FN_HG_EVT_ENTER: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + &pEvent->u.HG_Enter.uScreenID, + NULL /* puXPos */, + NULL /* puYPos */, + NULL /* uDefAction */, + &pEvent->u.HG_Enter.dndLstActionsAllowed, + &pEvent->u.HG_Enter.pszFormats, + &pEvent->u.HG_Enter.cbFormats); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER; + break; + } + case HOST_DND_FN_HG_EVT_MOVE: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + NULL /* puScreenId */, + &pEvent->u.HG_Move.uXpos, + &pEvent->u.HG_Move.uYpos, + &pEvent->u.HG_Move.dndActionDefault, + NULL /* puAllActions */, + NULL /* pszFormats */, + NULL /* pcbFormats */); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE; + break; + } + case HOST_DND_FN_HG_EVT_DROPPED: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + NULL /* puScreenId */, + &pEvent->u.HG_Drop.uXpos, + &pEvent->u.HG_Drop.uYpos, + &pEvent->u.HG_Drop.dndActionDefault, + NULL /* puAllActions */, + NULL /* pszFormats */, + NULL /* pcbFormats */); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP; + break; + } + case HOST_DND_FN_HG_EVT_LEAVE: + { + rc = vbglR3DnDHGRecvLeave(pCtx); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE; + break; + } + case HOST_DND_FN_HG_SND_DATA_HDR: + { + rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE; + break; + } + case HOST_DND_FN_HG_SND_DIR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_HDR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_DATA: + { + /* + * All messages for this block are handled internally + * by vbglR3DnDHGRecvDataMain(), see above. + * + * So if we land here our code is buggy. + */ + rc = VERR_WRONG_ORDER; + break; + } + case HOST_DND_FN_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + rc = VERR_CANCELLED; /* Will emit a cancel event below. */ + break; + } +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case HOST_DND_FN_GH_REQ_PENDING: + { + rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING; + break; + } + case HOST_DND_FN_GH_EVT_DROPPED: + { + rc = vbglR3DnDGHRecvDropped(pCtx, + &pEvent->u.GH_Drop.pszFormat, + &pEvent->u.GH_Drop.cbFormat, + &pEvent->u.GH_Drop.dndActionRequested); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP; + break; + } +#endif + default: + { + rc = VERR_NOT_SUPPORTED; + break; + } + } + } + + if (RT_FAILURE(rc)) + { + /* Current operation cancelled? Set / overwrite event type and tell the caller. */ + if (rc == VERR_CANCELLED) + { + pEvent->enmType = VBGLR3DNDEVENTTYPE_CANCEL; + rc = VINF_SUCCESS; /* Deliver the event to the caller. */ + } + else + { + VbglR3DnDEventFree(pEvent); + LogRel(("DnD: Handling message %s (%#x) failed with %Rrc\n", DnDHostMsgToStr(uMsg), uMsg, rc)); + } + } + + if (RT_SUCCESS(rc)) + *ppEvent = pEvent; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Frees (destroys) a formerly allocated DnD event. + * + * @returns IPRT status code. + * @param pEvent Event to free (destroy). + */ +VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent) +{ + if (!pEvent) + return; + + /* Some messages require additional cleanup. */ + switch (pEvent->enmType) + { + case VBGLR3DNDEVENTTYPE_HG_ENTER: + { + if (pEvent->u.HG_Enter.pszFormats) + RTStrFree(pEvent->u.HG_Enter.pszFormats); + break; + } + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case VBGLR3DNDEVENTTYPE_GH_DROP: + { + if (pEvent->u.GH_Drop.pszFormat) + RTStrFree(pEvent->u.GH_Drop.pszFormat); + break; + } +#endif + case VBGLR3DNDEVENTTYPE_HG_RECEIVE: + { + PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta; + switch (pMeta->enmType) + { + case VBGLR3GUESTDNDMETADATATYPE_RAW: + { + if (pMeta->u.Raw.pvMeta) + { + Assert(pMeta->u.Raw.cbMeta); + RTMemFree(pMeta->u.Raw.pvMeta); + pMeta->u.Raw.cbMeta = 0; + } + break; + } + + case VBGLR3GUESTDNDMETADATATYPE_URI_LIST: + { + DnDTransferListDestroy(&pMeta->u.URI.Transfer); + break; + } + + default: + break; + } + break; + } + + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGAck Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_ACK_OP, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uAction.SetUInt32(dndAction); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Requests the actual DnD data to be sent from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pcszFormat Format to request the data from the host in. + */ +VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER); + if (!RTStrIsValidEncoding(pcszFormat)) + return VERR_INVALID_PARAMETER; + + const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */ + + HGCMMsgHGReqData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_REQ_DATA, 3); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat); + Msg.u.v3.cbFormat.SetUInt32(cbFormat); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Reports back its progress back to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param uStatus DnD status to report. + * @param uPercent Overall progress (in percent) to report. + * @param rcErr Error code (IPRT-style) to report. + */ +VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER); + + HGCMMsgHGProgress Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_EVT_PROGRESS, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uStatus.SetUInt32(uStatus); + Msg.u.v3.uPercent.SetUInt32(uPercent); + Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host + * Acknowledges that there currently is a drag'n drop operation in progress on the guest, + * which eventually could be dragged over to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param dndActionDefault Default action for the operation to report. + * @param dndLstActionsAllowed All available actions for the operation to report. + * @param pcszFormats Available formats for the operation to report. + * @param cbFormats Size (in bytes) of formats to report. + */ +VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx, + VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed, + const char* pcszFormats, uint32_t cbFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER); + AssertReturn(cbFormats, VERR_INVALID_PARAMETER); + + if (!RTStrIsValidEncoding(pcszFormats)) + return VERR_INVALID_UTF8_ENCODING; + + HGCMMsgGHAckPending Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_ACK_PENDING, 5); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uDefAction.SetUInt32(dndActionDefault); + Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed); + Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats); + Msg.u.v3.cbFormats.SetUInt32(cbFormats); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Guest -> Host + * Utility function to send DnD data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Data block to send. + * @param cbData Size (in bytes) of data block to send. + * @param pDataHdr Data header to use -- needed for accounting. + */ +static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx, + void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + + HGCMMsgGHSendDataHdr MsgHdr; + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA_HDR, 12); + MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */ + MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal); + MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta); + MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt); + MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt); + MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects); + MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */ + MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */ + MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */ + + int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr)); + + LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n", + pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc)); + + if (RT_SUCCESS(rc)) + { + HGCMMsgGHSendData MsgData; + VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA, 5); + MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */ + MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */ + MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */ + + uint32_t cbCurChunk; + const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize; + uint32_t cbSent = 0; + + while (cbSent < cbData) + { + cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk); + MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk); + MsgData.u.v3.cbData.SetUInt32(cbCurChunk); + + rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData)); + if (RT_FAILURE(rc)) + break; + + cbSent += cbCurChunk; + } + + LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n", + cbMaxChunk, cbData, cbSent, rc)); + + if (RT_SUCCESS(rc)) + Assert(cbSent == cbData); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host + * Utility function to send a guest directory to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj transfer object containing the directory to send. + */ +static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj) +{ + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER); + + const char *pcszPath = DnDTransferObjectGetDestPath(pObj); + const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */; + const RTFMODE fMode = DnDTransferObjectGetMode(pObj); + + LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode)); + + if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */ + return VERR_INVALID_PARAMETER; + + HGCMMsgGHSendDir Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DIR, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath); + Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath); + Msg.u.v3.fMode.SetUInt32(fMode); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Guest -> Host + * Utility function to send a file from the guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj Transfer object containing the file to send. + */ +static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE); + AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER); + + uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE; + + int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE); + if (RT_FAILURE(rc)) + return rc; + + uint32_t cbBuf = pCtx->cbMaxChunkSize; + AssertReturn(cbBuf, VERR_INVALID_PARAMETER); + void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */ + if (!pvBuf) + { + int rc2 = DnDTransferObjectClose(pObj); + AssertRC(rc2); + return VERR_NO_MEMORY; + } + + const char *pcszPath = DnDTransferObjectGetDestPath(pObj); + const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX); + const uint64_t cbSize = DnDTransferObjectGetSize(pObj); + const RTFMODE fMode = DnDTransferObjectGetMode(pObj); + + LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode)); + + HGCMMsgGHSendFileHdr MsgHdr; + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_HDR, 6); + MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */ + MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */ + MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */ + MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */ + MsgHdr.fMode.SetUInt32(fMode); /* File mode */ + MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */ + + rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr)); + + LogFlowFunc(("Sending file header resulted in %Rrc\n", rc)); + + if (RT_SUCCESS(rc)) + { + /* + * Send the actual file data, chunk by chunk. + */ + HGCMMsgGHSendFileData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + uint64_t cbToReadTotal = cbSize; + uint64_t cbWrittenTotal = 0; + while (cbToReadTotal) + { + uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf); + uint32_t cbRead = 0; + if (cbToRead) + rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead); + + LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n", + cbToReadTotal, cbToRead, cbRead, rc)); + + if ( RT_SUCCESS(rc) + && cbRead) + { + Msg.u.v3.pvData.SetPtr(pvBuf, cbRead); + Msg.u.v3.cbData.SetUInt32(cbRead); + /** @todo Calculate + set checksums. */ + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + + if (RT_FAILURE(rc)) + { + LogFlowFunc(("Reading failed with rc=%Rrc\n", rc)); + break; + } + + Assert(cbRead <= cbToReadTotal); + cbToReadTotal -= cbRead; + cbWrittenTotal += cbRead; + + LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize)); + }; + } + + RTMemFree(pvBuf); + int rc2 = DnDTransferObjectClose(pObj); + if (RT_SUCCESS(rc)) + rc = rc2; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host + * Utility function to send a transfer object from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj Transfer object to send from guest to the host. + */ +static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + + int rc; + + const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj); + + switch (enmType) + { + case DNDTRANSFEROBJTYPE_DIRECTORY: + rc = vbglR3DnDGHSendDir(pCtx, pObj); + break; + + case DNDTRANSFEROBJTYPE_FILE: + rc = vbglR3DnDGHSendFile(pCtx, pObj); + break; + + default: + AssertMsgFailed(("Object type %ld not implemented\n", enmType)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + return rc; +} + +/** + * Guest -> Host + * Utility function to send raw data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Block to raw data to send. + * @param cbData Size (in bytes) of raw data to send. + */ +static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + /* cbData can be 0. */ + + VBOXDNDDATAHDR dataHdr; + RT_ZERO(dataHdr); + + dataHdr.cbMeta = (uint32_t)cbData; + dataHdr.cbTotal = cbData; + + return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr); +} + +/** + * Guest -> Host + * Utility function to send transfer data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pTransferList Dnd transfer list to send. + */ +static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList) +{ + AssertPtrReturn(pCtx,VERR_INVALID_POINTER); + AssertPtrReturn(pTransferList, VERR_INVALID_POINTER); + + /* + * Send the (meta) data; in case of URIs it's the root entries of a + * transfer list the host needs to know upfront to set up the drag'n drop operation. + */ + char *pszList = NULL; + size_t cbList; + int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList); + if (RT_FAILURE(rc)) + return rc; + + void *pvURIList = (void *)pszList; + uint32_t cbURLIist = (uint32_t)cbList; + + /* The total size also contains the size of the meta data. */ + uint64_t cbTotal = cbURLIist; + cbTotal += DnDTransferListObjTotalBytes(pTransferList); + + /* We're going to send a transfer list in text format. */ + const char szMetaFmt[] = "text/uri-list"; + const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */ + + VBOXDNDDATAHDR dataHdr; + dataHdr.uFlags = 0; /* Flags not used yet. */ + dataHdr.cbTotal = cbTotal; + dataHdr.cbMeta = cbURLIist; + dataHdr.pvMetaFmt = (void *)szMetaFmt; + dataHdr.cbMetaFmt = cbMetaFmt; + dataHdr.cObjects = DnDTransferListObjCount(pTransferList); + + rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr); + + if (RT_SUCCESS(rc)) + { + while (DnDTransferListObjCount(pTransferList)) + { + PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList); + + rc = vbglR3DnDGHSendURIObject(pCtx, pObj); + if (RT_FAILURE(rc)) + break; + + DnDTransferListObjRemoveFirst(pTransferList); + } + + Assert(DnDTransferListObjCount(pTransferList) == 0); + } + + return rc; +} + +/** + * Guest -> Host + * Sends data, which either can be raw or URI data, from guest to the host. This function + * initiates the actual data transfer from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszFormat In which format the data will be sent. + * @param pvData Data block to send. + * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR_STR. + * @param cbData Size (in bytes) of data block to send. + */ +VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData)); + + LogRel2(("DnD: Sending %RU32 bytes meta data in format '%s'\n", cbData, pszFormat)); + + int rc; + if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat))) + { + DNDTRANSFERLIST lstTransfer; + RT_ZERO(lstTransfer); + + rc = DnDTransferListInit(&lstTransfer); + if (RT_SUCCESS(rc)) + { + /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */ + /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */ + const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE; + + rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData, + DND_PATH_SEPARATOR_STR, fFlags); + if (RT_SUCCESS(rc)) + rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer); + DnDTransferListDestroy(&lstTransfer); + } + } + else + rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData); + + if (RT_FAILURE(rc)) + { + LogRel(("DnD: Sending data failed with rc=%Rrc\n", rc)); + + if (rc != VERR_CANCELLED) + { + int rc2 = VbglR3DnDSendError(pCtx, rc); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2)); + } + } + + return rc; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp new file mode 100644 index 00000000..79a6da13 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp @@ -0,0 +1,199 @@ +/* $Id: VBoxGuestR3LibDrmClient.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, DRM client handling. + */ + +/* + * Copyright (C) 2020-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + +#include <iprt/env.h> +#include <iprt/path.h> +#include <iprt/process.h> + +#if defined(RT_OS_LINUX) +# include <VBox/HostServices/GuestPropertySvc.h> + +/** Defines the DRM client executable (image). */ +# define VBOX_DRMCLIENT_EXECUTABLE "/usr/bin/VBoxDRMClient" +# define VBOX_DRMCLIENT_LEGACY_EXECUTABLE "/usr/bin/VBoxClient" +/** Defines the guest property that defines if the DRM resizing client needs to be active or not. */ +# define VBOX_DRMCLIENT_GUEST_PROP_RESIZE "/VirtualBox/GuestAdd/DRMResize" + +/** + * Check if specified guest property exist. + * + * @returns \c true if the property exists and its flags do match, \c false otherwise. + * @param pszPropName Guest property name. + * @param fPropFlags Guest property flags mask to verify if property exist. + * If \p fPropFlags is 0, flags verification is omitted. + */ +static bool vbglR3DrmClientCheckProp(const char *pszPropName, uint32_t fPropFlags) +{ + bool fExist = false; +# if defined(VBOX_WITH_GUEST_PROPS) + uint32_t idClient; + + int rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + char *pcszFlags = NULL; + + rc = VbglR3GuestPropReadEx(idClient, pszPropName, NULL /* ppszValue */, &pcszFlags, NULL); + if (RT_SUCCESS(rc)) + { + /* Check property flags match. */ + if (fPropFlags) + { + uint32_t fFlags = 0; + + rc = GuestPropValidateFlags(pcszFlags, &fFlags); + fExist = RT_SUCCESS(rc) && (fFlags == fPropFlags); + } + else + fExist = true; + + RTStrFree(pcszFlags); + } + + VbglR3GuestPropDisconnect(idClient); + } +# endif /* VBOX_WITH_GUEST_PROPS */ + return fExist; +} +#endif /* RT_OS_LINUX */ + +/** + * Returns true if the DRM resizing client is needed. + * This is achieved by querying existence of a guest property. + * + * @returns \c true if the DRM resizing client is needed, \c false if not. + */ +VBGLR3DECL(bool) VbglR3DrmClientIsNeeded(void) +{ +#if defined(RT_OS_LINUX) + return vbglR3DrmClientCheckProp(VBOX_DRMCLIENT_GUEST_PROP_RESIZE, 0); +#else + return false; +#endif +} + +/** + * Returns true if the DRM IPC server socket access should be restricted. + * + * Restricted access means that only users from a certain group should + * be granted with read and write access permission to IPC socket. Check + * is done by examining \c VBGLR3DRMIPCPROPRESTRICT guest property. Property + * is only considered valid if is read-only for guest. I.e., the following + * property should be set on the host side: + * + * VBoxManage guestproperty set <VM> /VirtualBox/GuestAdd/DRMIpcRestricted 1 --flags RDONLYGUEST + * + * @returns \c true if restricted socket access is required, \c false otherwise. + */ +VBGLR3DECL(bool) VbglR3DrmRestrictedIpcAccessIsNeeded(void) +{ +#if defined(RT_OS_LINUX) + return vbglR3DrmClientCheckProp(VBGLR3DRMIPCPROPRESTRICT, GUEST_PROP_F_RDONLYGUEST); +#else + return false; +#endif +} + +/** + * Returns true if the DRM resizing client already is running. + * This is achieved by querying existence of a guest property. + * + * @returns \c true if the DRM resizing client is running, \c false if not. + */ +VBGLR3DECL(bool) VbglR3DrmClientIsRunning(void) +{ + return VbglR3DrmClientIsNeeded(); +} + +#if defined(RT_OS_LINUX) +static int VbglR3DrmStart(const char *pszCmd, const char **apszArgs) +{ + return RTProcCreate(pszCmd, apszArgs, RTENV_DEFAULT, + RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL); +} +#endif + +/** + * Starts (executes) the DRM resizing client process ("VBoxDRMClient"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmClientStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[2] = { VBOX_DRMCLIENT_EXECUTABLE, NULL }; /** @todo r=andy Pass path + process name as argv0? */ + return VbglR3DrmStart(VBOX_DRMCLIENT_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + +/** + * Starts (executes) the legacy DRM resizing client process ("VBoxClient --vmsvga"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmLegacyClientStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--vmsvga", NULL }; + return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + +/** + * Starts (executes) the legacy X11 resizing agent process ("VBoxClient --display"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmLegacyX11AgentStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--display", NULL }; + return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp new file mode 100644 index 00000000..55b1a57e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp @@ -0,0 +1,103 @@ +/* $Id: VBoxGuestR3LibEvent.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Events. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/log.h> +#include <iprt/assert.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Wait for the host to signal one or more events and return which. + * + * The events will only be delivered by the host if they have been enabled + * previously using @a VbglR3CtlFilterMask. If one or several of the events + * have already been signalled but not yet waited for, this function will return + * immediately and return those events. + * + * @returns IPRT status code. + * + * @param fMask The events we want to wait for, or-ed together. + * @param cMillies How long to wait before giving up and returning + * (VERR_TIMEOUT). Use RT_INDEFINITE_WAIT to wait until we + * are interrupted or one of the events is signalled. + * @param pfEvents Where to store the events signalled. Optional. + */ +VBGLR3DECL(int) VbglR3WaitEvent(uint32_t fMask, uint32_t cMillies, uint32_t *pfEvents) +{ + LogFlow(("VbglR3WaitEvent: fMask=%#x, cMillies=%u, pfEvents=%p\n", fMask, cMillies, pfEvents)); + AssertReturn((fMask & ~VMMDEV_EVENT_VALID_EVENT_MASK) == 0, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfEvents, VERR_INVALID_POINTER); + + VBGLIOCWAITFOREVENTS WaitEvents; + VBGLREQHDR_INIT(&WaitEvents.Hdr, WAIT_FOR_EVENTS); + WaitEvents.u.In.fEvents = fMask; + WaitEvents.u.In.cMsTimeOut = cMillies; + int rc = vbglR3DoIOCtl(VBGL_IOCTL_WAIT_FOR_EVENTS, &WaitEvents.Hdr, sizeof(WaitEvents)); + if (pfEvents) + { + if (RT_SUCCESS(rc)) + *pfEvents = WaitEvents.u.Out.fEvents; + else + *pfEvents = 0; + } + + LogFlow(("VbglR3WaitEvent: rc=%Rrc fEvents=%#x\n", rc, WaitEvents.u.Out.fEvents)); + return rc; +} + + +/** + * Causes any pending VbglR3WaitEvent calls (VBGL_IOCTL_WAIT_FOR_EVENTS) to + * return with a VERR_INTERRUPTED status. + * + * Can be used in combination with a termination flag variable for interrupting + * event loops. After calling this, VBGL_IOCTL_WAIT_FOR_EVENTS should no longer + * be called in the same session. At the time of writing this is not enforced; + * at the time of reading it may be. + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3InterruptEventWaits(void) +{ + VBGLREQHDR Req; + VBGLREQHDR_INIT(&Req, INTERRUPT_ALL_WAIT_FOR_EVENTS); + return vbglR3DoIOCtl(VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS, &Req, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp new file mode 100644 index 00000000..d5f28cb4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp @@ -0,0 +1,90 @@ +/* $Id: VBoxGuestR3LibGR.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, GR. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/errcore.h> +#include "VBoxGuestR3LibInternal.h" + + +int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType) +{ + VMMDevRequestHeader *pReq; + + AssertPtrReturn(ppReq, VERR_INVALID_PARAMETER); + AssertMsgReturn(cb >= sizeof(VMMDevRequestHeader) && cb < _1G, ("%#zx vs %#zx\n", cb, sizeof(VMMDevRequestHeader)), + VERR_INVALID_PARAMETER); + + pReq = (VMMDevRequestHeader *)RTMemTmpAlloc(cb); + if (RT_LIKELY(pReq)) + { + pReq->size = (uint32_t)cb; + pReq->version = VMMDEV_REQUEST_HEADER_VERSION; + pReq->requestType = enmReqType; + pReq->rc = VERR_GENERAL_FAILURE; + pReq->reserved1 = 0; + pReq->fRequestor = 0; + + *ppReq = pReq; + + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +int vbglR3GRPerform(VMMDevRequestHeader *pReq) +{ + PVBGLREQHDR pReqHdr = (PVBGLREQHDR)pReq; + uint32_t const cbReq = pReqHdr->cbIn; + Assert(pReqHdr->cbOut == 0 || pReqHdr->cbOut == cbReq); + pReqHdr->cbOut = cbReq; + if (pReq->size < _1K) + return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST(cbReq), pReqHdr, cbReq); + return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST_BIG, pReqHdr, cbReq); +} + + +void vbglR3GRFree(VMMDevRequestHeader *pReq) +{ + RTMemTmpFree(pReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp new file mode 100644 index 00000000..69c9df5b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp @@ -0,0 +1,2214 @@ +/* $Id: VBoxGuestR3LibGuestCtrl.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest control. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/cpp/autores.h> +#include <iprt/stdarg.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/GuestHost/GuestControl.h> +#include <VBox/HostServices/GuestControlSvc.h> + +#ifndef RT_OS_WINDOWS +# include <signal.h> +# ifdef RT_OS_DARWIN +# include <pthread.h> +# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */ +# endif +#endif + +#include "VBoxGuestR3LibInternal.h" + +using namespace guestControl; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Set if GUEST_MSG_PEEK_WAIT and friends are supported. */ +static int g_fVbglR3GuestCtrlHavePeekGetCancel = -1; + + +/** + * Connects to the guest control service. + * + * @returns VBox status code + * @param pidClient Where to put The client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pidClient) +{ + return VbglR3HGCMConnect("VBoxGuestControlSvc", pidClient); +} + + +/** + * Disconnect from the guest control service. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Waits until a new host message arrives. + * This will block until a message becomes available. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + */ +static int vbglR3GuestCtrlMsgWaitFor(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + HGCMMsgWaitFor Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, + GUEST_MSG_WAIT, /* Tell the host we want our next message. */ + 2); /* Just peek for the next message! */ + VbglHGCMParmUInt32Set(&Msg.msg, 0); + VbglHGCMParmUInt32Set(&Msg.num_parms, 0); + + /* + * We should always get a VERR_TOO_MUCH_DATA response here, see + * guestControl::HostMessage::Peek() and its caller ClientState::SendReply(). + * We accept success too here, in case someone decide to make the protocol + * slightly more sane. + * + * Note! A really sane protocol design would have a separate call for getting + * info about a pending message (returning VINF_SUCCESS), and a separate + * one for retriving the actual message parameters. Not this weird + * stuff, to put it rather bluntly. + * + * Note! As a result of this weird design, we are not able to correctly + * retrieve message if we're interrupted by a signal, like SIGCHLD. + * Because IPRT wants to use waitpid(), we're forced to have a handler + * installed for SIGCHLD, so when working with child processes there + * will be signals in the air and we will get VERR_INTERRUPTED returns. + * The way HGCM handles interrupted calls is to silently (?) drop them + * as they complete (see VMMDev), so the server knows little about it + * and just goes on to the next message inline. + * + * So, as a "temporary" mesasure, we block SIGCHLD here while waiting, + * because it will otherwise be impossible do simple stuff like 'mkdir' + * on a mac os x guest, and probably most other unix guests. + */ +#ifdef RT_OS_WINDOWS + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +#else + sigset_t SigSet; + sigemptyset(&SigSet); + sigaddset(&SigSet, SIGCHLD); + sigprocmask(SIG_BLOCK, &SigSet, NULL); + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + sigprocmask(SIG_UNBLOCK, &SigSet, NULL); +#endif + if ( rc == VERR_TOO_MUCH_DATA + || RT_SUCCESS(rc)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg); + if (RT_SUCCESS(rc2)) + { + rc2 = VbglHGCMParmUInt32Get(&Msg.num_parms, pcParameters); + if (RT_SUCCESS(rc2)) + { + /* Ok, so now we know what message type and how much parameters there are. */ + return rc; + } + } + rc = rc2; + } + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; +} + + +/** + * Determins the value of g_fVbglR3GuestCtrlHavePeekGetCancel. + * + * @returns true if supported, false if not. + * @param idClient The client ID to use for the testing. + */ +DECL_NO_INLINE(static, bool) vbglR3GuestCtrlDetectPeekGetCancelSupport(uint32_t idClient) +{ + /* + * Seems we get VINF_SUCCESS back from the host if we try unsupported + * guest control messages, so we need to supply some random message + * parameters and check that they change. + */ + uint32_t const idDummyMsg = UINT32_C(0x8350bdca); + uint32_t const cDummyParmeters = UINT32_C(0x7439604f); + uint32_t const cbDummyMask = UINT32_C(0xc0ffe000); + Assert(cDummyParmeters > VMMDEV_MAX_HGCM_PARMS); + + int rc; + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; + HGCMFunctionParameter cParams; + HGCMFunctionParameter acbParams[14]; + } PeekCall; + Assert(RT_ELEMENTS(PeekCall.acbParams) + 2 < VMMDEV_MAX_HGCM_PARMS); + + do + { + memset(&PeekCall, 0xf6, sizeof(PeekCall)); + VBGL_HGCM_HDR_INIT(&PeekCall.Hdr, idClient, GUEST_MSG_PEEK_NOWAIT, 16); + VbglHGCMParmUInt32Set(&PeekCall.idMsg, idDummyMsg); + VbglHGCMParmUInt32Set(&PeekCall.cParams, cDummyParmeters); + for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++) + VbglHGCMParmUInt32Set(&PeekCall.acbParams[i], i | cbDummyMask); + + rc = VbglR3HGCMCall(&PeekCall.Hdr, sizeof(PeekCall)); + } while (rc == VERR_INTERRUPTED); + + LogRel2(("vbglR3GuestCtrlDetectPeekGetCancelSupport: rc=%Rrc %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x\n", + rc, PeekCall.idMsg.u.value32, PeekCall.cParams.u.value32, + PeekCall.acbParams[ 0].u.value32, PeekCall.acbParams[ 1].u.value32, + PeekCall.acbParams[ 2].u.value32, PeekCall.acbParams[ 3].u.value32, + PeekCall.acbParams[ 4].u.value32, PeekCall.acbParams[ 5].u.value32, + PeekCall.acbParams[ 6].u.value32, PeekCall.acbParams[ 7].u.value32, + PeekCall.acbParams[ 8].u.value32, PeekCall.acbParams[ 9].u.value32, + PeekCall.acbParams[10].u.value32, PeekCall.acbParams[11].u.value32, + PeekCall.acbParams[12].u.value32, PeekCall.acbParams[13].u.value32)); + + /* + * VERR_TRY_AGAIN is likely and easy. + */ + if ( rc == VERR_TRY_AGAIN + && PeekCall.idMsg.u.value32 == 0 + && PeekCall.cParams.u.value32 == 0 + && PeekCall.acbParams[0].u.value32 == 0 + && PeekCall.acbParams[1].u.value32 == 0 + && PeekCall.acbParams[2].u.value32 == 0 + && PeekCall.acbParams[3].u.value32 == 0) + { + g_fVbglR3GuestCtrlHavePeekGetCancel = 1; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#1)\n")); + return true; + } + + /* + * VINF_SUCCESS is annoying but with 16 parameters we've got plenty to check. + */ + if ( rc == VINF_SUCCESS + && PeekCall.idMsg.u.value32 != idDummyMsg + && PeekCall.idMsg.u.value32 != 0 + && PeekCall.cParams.u.value32 <= VMMDEV_MAX_HGCM_PARMS) + { + for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++) + if (PeekCall.acbParams[i].u.value32 != (i | cbDummyMask)) + { + g_fVbglR3GuestCtrlHavePeekGetCancel = 0; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#1)\n")); + return false; + } + g_fVbglR3GuestCtrlHavePeekGetCancel = 1; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#2)\n")); + return true; + } + + /* + * Okay, pretty sure it's not supported then. + */ + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#3)\n")); + g_fVbglR3GuestCtrlHavePeekGetCancel = 0; + return false; +} + + +/** + * Reads g_fVbglR3GuestCtrlHavePeekGetCancel and resolved -1. + * + * @returns true if supported, false if not. + * @param idClient The client ID to use for the testing. + */ +DECLINLINE(bool) vbglR3GuestCtrlSupportsPeekGetCancel(uint32_t idClient) +{ + int fState = g_fVbglR3GuestCtrlHavePeekGetCancel; + if (RT_LIKELY(fState != -1)) + return fState != 0; + return vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient); +} + + +/** + * Figures which getter function to use to retrieve the message. + */ +DECLINLINE(uint32_t) vbglR3GuestCtrlGetMsgFunctionNo(uint32_t idClient) +{ + return vbglR3GuestCtrlSupportsPeekGetCancel(idClient) ? GUEST_MSG_GET : GUEST_MSG_WAIT; +} + + +/** + * Checks if the host supports the optimizes message and session functions. + * + * @returns true / false. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * We may need to use this for checking. + * @since 6.0 + */ +VBGLR3DECL(bool) VbglR3GuestCtrlSupportsOptimizations(uint32_t idClient) +{ + return vbglR3GuestCtrlSupportsPeekGetCancel(idClient); +} + + +/** + * Make us the guest control master client. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMakeMeMaster(uint32_t idClient) +{ + int rc; + do + { + VBGLIOCHGCMCALL Hdr; + VBGL_HGCM_HDR_INIT(&Hdr, idClient, GUEST_MSG_MAKE_ME_MASTER, 0); + rc = VbglR3HGCMCall(&Hdr, sizeof(Hdr)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param fGuestFeatures Features to report, VBOX_GUESTCTRL_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_GUESTCTRL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3GuestCtrlReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_GUESTCTRL_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_GUESTCTRL_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Query the host features. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pfHostFeatures Where to store the host feature, VBOX_GUESTCTRL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3GuestCtrlQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_QUERY_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, 0); + VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & RT_BIT_64(63)) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Peeks at the next host message, waiting for one to turn up. + * + * @returns VBox status code. + * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so + * caller just have to repeat this call. + * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck). + * + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use + * for the VM restore check. Optional. + * + * @note Restore check is only performed optimally with a 6.0 host. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgPeekWait(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters, uint64_t *pidRestoreCheck) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + int rc; + if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient)) + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */ + HGCMFunctionParameter cParameters; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_PEEK_WAIT, 2); + VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0); + VbglHGCMParmUInt32Set(&Msg.cParameters, 0); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + LogRel2(("VbglR3GuestCtrlMsgPeekWait -> %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit + && Msg.cParameters.type == VMMDevHGCMParmType_32bit, + ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type), + VERR_INTERNAL_ERROR_3); + + *pidMsg = (uint32_t)Msg.idMsg.u.value64; + *pcParameters = Msg.cParameters.u.value32; + return rc; + } + + /* + * If interrupted we must cancel the call so it doesn't prevent us from making another one. + */ + if (rc == VERR_INTERRUPTED) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_CANCEL, 0); + int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr)); + AssertRC(rc2); + } + + /* + * If restored, update pidRestoreCheck. + */ + if (rc == VERR_VM_RESTORED && pidRestoreCheck) + *pidRestoreCheck = Msg.idMsg.u.value64; + + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; + } + + /* + * Fallback if host < v6.0. + * + * Note! The restore check isn't perfect. Would require checking afterwards + * and stash the result if we were restored during the call. Too much + * hazzle for a downgrade scenario. + */ + if (pidRestoreCheck) + { + uint64_t idRestoreCur = *pidRestoreCheck; + rc = VbglR3GetSessionId(&idRestoreCur); + if (RT_SUCCESS(rc) && idRestoreCur != *pidRestoreCheck) + { + *pidRestoreCheck = idRestoreCur; + return VERR_VM_RESTORED; + } + } + + rc = vbglR3GuestCtrlMsgWaitFor(idClient, pidMsg, pcParameters); + if (rc == VERR_TOO_MUCH_DATA) + rc = VINF_SUCCESS; + return rc; +} + + +/** + * Asks the host guest control service to set a message filter to this + * client so that it only will receive certain messages in the future. + * The filter(s) are a bitmask for the context IDs, served from the host. + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param uValue The value to filter messages for. + * @param uMaskAdd Filter mask to add. + * @param uMaskRemove Filter mask to remove. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgFilterSet(uint32_t idClient, uint32_t uValue, uint32_t uMaskAdd, uint32_t uMaskRemove) +{ + HGCMMsgFilterSet Msg; + + /* Tell the host we want to set a filter. */ + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_FILTER_SET, 4); + VbglHGCMParmUInt32Set(&Msg.value, uValue); + VbglHGCMParmUInt32Set(&Msg.mask_add, uMaskAdd); + VbglHGCMParmUInt32Set(&Msg.mask_remove, uMaskRemove); + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Replies to a message from the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param rc Guest rc to reply. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc) +{ + return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */, + NULL /* pvPayload */, 0 /* cbPayload */); +} + + +/** + * Replies to a message from the host, extended version. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param rc Guest rc to reply. + * @param uType Reply type; not used yet and must be 0. + * @param pvPayload Pointer to data payload to reply. Optional. + * @param cbPayload Size of data payload (in bytes) to reply. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgReplyEx(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc, uint32_t uType, + void *pvPayload, uint32_t cbPayload) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* Everything else is optional. */ + + HGCMMsgReply Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_REPLY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmUInt32Set(&Msg.rc, (uint32_t)rc); /* int vs. uint32_t */ + VbglHGCMParmPtrSet(&Msg.payload, pvPayload, cbPayload); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Tell the host to skip the current message replying VERR_NOT_SUPPORTED + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param rcSkip The status code to pass back to Main when skipping. + * @param idMsg The message ID to skip, pass UINT32_MAX to pass any. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t idClient, int rcSkip, uint32_t idMsg) +{ + if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient)) + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter rcSkip; + HGCMFunctionParameter idMsg; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SKIP, 2); + VbglHGCMParmUInt32Set(&Msg.rcSkip, (uint32_t)rcSkip); + VbglHGCMParmUInt32Set(&Msg.idMsg, idMsg); + return VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } + + /* This is generally better than nothing... */ + return VbglR3GuestCtrlMsgSkipOld(idClient); +} + + +/** + * Tells the host service to skip the current message returned by + * VbglR3GuestCtrlMsgWaitFor(). + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgSkipOld(uint32_t idClient) +{ + HGCMMsgSkip Msg; + + /* Tell the host we want to skip the current assigned message. */ + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_SKIP_OLD, 1); + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Asks the host to cancel (release) all pending waits which were deferred. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t idClient) +{ + HGCMMsgCancelPendingWaits Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_CANCEL, 0); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Prepares a session. + * @since 6.0 + * @sa GUEST_SESSION_PREPARE + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionPrepare(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + HGCMFunctionParameter pKey; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_PREPARE, 2); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Accepts a session. + * @since 6.0 + * @sa GUEST_SESSION_ACCEPT + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionAccept(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + HGCMFunctionParameter pKey; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_ACCEPT, 2); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Cancels a prepared session. + * @since 6.0 + * @sa GUEST_SESSION_CANCEL_PREPARED + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionCancelPrepared(uint32_t idClient, uint32_t idSession) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_CANCEL_PREPARED, 1); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Invalidates the internal state because the (VM) session has been changed (i.e. restored). + * + * @returns VBox status code. + * @param idClient Client ID to use for invalidating state. + * @param idNewControlSession New control session ID. Currently unused. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionHasChanged(uint32_t idClient, uint64_t idNewControlSession) +{ + RT_NOREF(idNewControlSession); + + vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient); + + return VINF_SUCCESS; +} + + +/** + * Asks a specific guest session to close. + * + * @return IPRT status code. + * @param pCtx Guest control command context to use. + * @param fFlags Some kind of flag. Figure it out yourself. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t fFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + HGCMMsgSessionClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_CLOSE, pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Notifies a guest session. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uType Notification type of type GUEST_SESSION_NOTIFYTYPE_XXX. + * @param iResult Result code (rc) to notify. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionNotify(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uType, int32_t iResult) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgSessionNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmUInt32Set(&Msg.result, (uint32_t)iResult); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Initializes a session startup info, extended version. + * + * @returns VBox status code. + * @param pStartupInfo Session startup info to initializes. + * @param cbUser Size (in bytes) to use for the user name buffer. + * @param cbPassword Size (in bytes) to use for the password buffer. + * @param cbDomain Size (in bytes) to use for the domain name buffer. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInitEx(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo, + size_t cbUser, size_t cbPassword, size_t cbDomain) +{ + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + +#define ALLOC_STR(a_Str, a_cb) \ + if ((a_cb) > 0) \ + { \ + pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \ + AssertPtrBreak(pStartupInfo->psz##a_Str); \ + pStartupInfo->cb##a_Str = (uint32_t)a_cb; \ + } + + do + { + ALLOC_STR(User, cbUser); + ALLOC_STR(Password, cbPassword); + ALLOC_STR(Domain, cbDomain); + + return VINF_SUCCESS; + + } while (0); + +#undef ALLOC_STR + + VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo); + return VERR_NO_MEMORY; +} + +/** + * Initializes a session startup info. + * + * @returns VBox status code. + * @param pStartupInfo Session startup info to initializes. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInit(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + return VbglR3GuestCtrlSessionStartupInfoInitEx(pStartupInfo, + GUEST_PROC_DEF_USER_LEN, GUEST_PROC_DEF_PASSWORD_LEN, + GUEST_PROC_DEF_DOMAIN_LEN); +} + +/** + * Destroys a session startup info. + * + * @param pStartupInfo Session startup info to destroy. + */ +VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoDestroy(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + RTStrFree(pStartupInfo->pszUser); + RTStrFree(pStartupInfo->pszPassword); + RTStrFree(pStartupInfo->pszDomain); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); +} + +/** + * Free's a session startup info. + * + * @param pStartupInfo Session startup info to free. + * The pointer will not be valid anymore after return. + */ +VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoFree(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo); + + RTMemFree(pStartupInfo); + pStartupInfo = NULL; +} + +/** + * Duplicates a session startup info. + * + * @returns Duplicated session startup info on success, or NULL on error. + * @param pStartupInfo Session startup info to duplicate. + */ +VBGLR3DECL(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO) VbglR3GuestCtrlSessionStartupInfoDup(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + AssertPtrReturn(pStartupInfo, NULL); + + PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO) + RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + if (pStartupInfoDup) + { + do + { + pStartupInfoDup->pszUser = NULL; + pStartupInfoDup->pszPassword = NULL; + pStartupInfoDup->pszDomain = NULL; + +#define DUP_STR(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \ + } + DUP_STR(User); + DUP_STR(Password); + DUP_STR(Domain); + +#undef DUP_STR + + return pStartupInfoDup; + + } while (0); /* To use break macros above. */ + + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfoDup); + } + + return NULL; +} + +/** + * Retrieves a HOST_SESSION_CREATE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param ppStartupInfo Where to store the allocated session startup info. + * Needs to be free'd by VbglR3GuestCtrlSessionStartupInfoFree(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLSESSIONSTARTUPINFO *ppStartupInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER); + AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER); + + PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo + = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + if (!pStartupInfo) + return VERR_NO_MEMORY; + + int rc = VbglR3GuestCtrlSessionStartupInfoInit(pStartupInfo); + if (RT_FAILURE(rc)) + { + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo); + return rc; + } + + do + { + HGCMMsgSessionOpen Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CREATE); + VbglHGCMParmUInt32Set(&Msg.protocol, 0); + VbglHGCMParmPtrSet(&Msg.username, pStartupInfo->pszUser, pStartupInfo->cbUser); + VbglHGCMParmPtrSet(&Msg.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword); + VbglHGCMParmPtrSet(&Msg.domain, pStartupInfo->pszDomain, pStartupInfo->cbDomain); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.protocol.GetUInt32(&pStartupInfo->uProtocol); + Msg.flags.GetUInt32(&pStartupInfo->fFlags); + + pStartupInfo->uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if (RT_SUCCESS(rc)) + { + *ppStartupInfo = pStartupInfo; + } + else + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo); + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Retrieves a HOST_SESSION_CLOSE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfFlags, uint32_t *pidSession) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgSessionClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CLOSE); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + + if (pidSession) + *pidSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_RENAME message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetRename(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszSource, uint32_t cbSource, + char *pszDest, uint32_t cbDest, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszSource, VERR_INVALID_POINTER); + AssertReturn(cbSource, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDest, VERR_INVALID_POINTER); + AssertReturn(cbDest, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgPathRename Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_RENAME); + VbglHGCMParmPtrSet(&Msg.source, pszSource, cbSource); + VbglHGCMParmPtrSet(&Msg.dest, pszDest, cbDest); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_USER_DOCUMENTS message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserDocuments(PVBGLR3GUESTCTRLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER); + + int rc; + do + { + HGCMMsgPathUserDocuments Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_DOCUMENTS); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + Msg.context.GetUInt32(&pCtx->uContextID); + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_USER_HOME message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserHome(PVBGLR3GUESTCTRLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER); + + int rc; + do + { + HGCMMsgPathUserHome Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_HOME); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + Msg.context.GetUInt32(&pCtx->uContextID); + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + +/** + * Retrieves a HOST_MSG_SHUTDOWN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pfAction Where to store the action flags on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlGetShutdown(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfAction, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgShutdown Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SHUTDOWN); + VbglHGCMParmUInt32Set(&Msg.action, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.action.GetUInt32(pfAction); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + +/** + * Initializes a process startup info, extended version. + * + * @returns VBox status code. + * @param pStartupInfo Process startup info to initializes. + * @param cbCmd Size (in bytes) to use for the command buffer. + * @param cbUser Size (in bytes) to use for the user name buffer. + * @param cbPassword Size (in bytes) to use for the password buffer. + * @param cbDomain Size (in bytes) to use for the domain buffer. + * @param cbArgs Size (in bytes) to use for the arguments buffer. + * @param cbEnv Size (in bytes) to use for the environment buffer. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInitEx(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo, + size_t cbCmd, + size_t cbUser, size_t cbPassword, size_t cbDomain, + size_t cbArgs, size_t cbEnv) +{ + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + AssertReturn(cbCmd, VERR_INVALID_PARAMETER); + AssertReturn(cbUser, VERR_INVALID_PARAMETER); + AssertReturn(cbPassword, VERR_INVALID_PARAMETER); + AssertReturn(cbDomain, VERR_INVALID_PARAMETER); + AssertReturn(cbArgs, VERR_INVALID_PARAMETER); + AssertReturn(cbEnv, VERR_INVALID_PARAMETER); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + +#define ALLOC_STR(a_Str, a_cb) \ + if ((a_cb) > 0) \ + { \ + pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \ + AssertPtrBreak(pStartupInfo->psz##a_Str); \ + pStartupInfo->cb##a_Str = (uint32_t)a_cb; \ + } + + do + { + ALLOC_STR(Cmd, cbCmd); + ALLOC_STR(Args, cbArgs); + ALLOC_STR(Env, cbEnv); + ALLOC_STR(User, cbUser); + ALLOC_STR(Password, cbPassword); + ALLOC_STR(Domain, cbDomain); + + return VINF_SUCCESS; + + } while (0); + +#undef ALLOC_STR + + VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo); + return VERR_NO_MEMORY; +} + +/** + * Initializes a process startup info with default values. + * + * @param pStartupInfo Process startup info to initializes. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInit(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + return VbglR3GuestCtrlProcStartupInfoInitEx(pStartupInfo, + GUEST_PROC_DEF_CMD_LEN, + GUEST_PROC_DEF_USER_LEN /* Deprecated, now handled via session creation. */, + GUEST_PROC_DEF_PASSWORD_LEN /* Ditto. */, + GUEST_PROC_DEF_DOMAIN_LEN /* Ditto. */, + GUEST_PROC_DEF_ARGS_LEN, GUEST_PROC_DEF_ENV_LEN); +} + +/** + * Destroys a process startup info. + * + * @param pStartupInfo Process startup info to destroy. + */ +VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoDestroy(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + RTStrFree(pStartupInfo->pszCmd); + RTStrFree(pStartupInfo->pszArgs); + RTStrFree(pStartupInfo->pszEnv); + RTStrFree(pStartupInfo->pszUser); + RTStrFree(pStartupInfo->pszPassword); + RTStrFree(pStartupInfo->pszDomain); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); +} + +/** + * Free's a process startup info. + * + * @param pStartupInfo Process startup info to free. + * The pointer will not be valid anymore after return. + */ +VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoFree(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo); + + RTMemFree(pStartupInfo); + pStartupInfo = NULL; +} + +/** + * Duplicates a process startup info. + * + * @returns Duplicated process startup info on success, or NULL on error. + * @param pStartupInfo Process startup info to duplicate. + */ +VBGLR3DECL(PVBGLR3GUESTCTRLPROCSTARTUPINFO) VbglR3GuestCtrlProcStartupInfoDup(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + AssertPtrReturn(pStartupInfo, NULL); + + PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLPROCSTARTUPINFO) + RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + if (pStartupInfoDup) + { + do + { + pStartupInfoDup->pszCmd = NULL; + pStartupInfoDup->pszArgs = NULL; + pStartupInfoDup->pszEnv = NULL; + pStartupInfoDup->pszUser = NULL; + pStartupInfoDup->pszPassword = NULL; + pStartupInfoDup->pszDomain = NULL; + +#define DUP_STR(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \ + } + +#define DUP_MEM(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTMemDup(pStartupInfo->psz##a_Str, pStartupInfo->cb##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)pStartupInfo->cb##a_Str; \ + } + + DUP_STR(Cmd); + DUP_MEM(Args); + DUP_MEM(Env); + DUP_STR(User); + DUP_STR(Password); + DUP_STR(Domain); + +#undef DUP_STR +#undef DUP_MEM + + return pStartupInfoDup; + + } while (0); /* To use break macros above. */ + + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfoDup); + } + + return NULL; +} + +/** + * Retrieves a HOST_EXEC_CMD message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param ppStartupInfo Where to store the allocated session startup info. + * Needs to be free'd by VbglR3GuestCtrlProcStartupInfoFree(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLPROCSTARTUPINFO *ppStartupInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER); + + PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo + = (PVBGLR3GUESTCTRLPROCSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + if (!pStartupInfo) + return VERR_NO_MEMORY; + + int rc = VbglR3GuestCtrlProcStartupInfoInit(pStartupInfo); + if (RT_FAILURE(rc)) + { + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo); + return rc; + } + + unsigned cRetries = 0; + const unsigned cMaxRetries = 32; /* Should be enough for now. */ + const unsigned cGrowthFactor = 2; /* By how much the buffers will grow if they're too small yet. */ + + do + { + LogRel(("VbglR3GuestCtrlProcGetStart: Retrieving\n")); + + HGCMMsgProcExec Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_CMD); + VbglHGCMParmPtrSet(&Msg.cmd, pStartupInfo->pszCmd, pStartupInfo->cbCmd); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.num_args, 0); + VbglHGCMParmPtrSet(&Msg.args, pStartupInfo->pszArgs, pStartupInfo->cbArgs); + VbglHGCMParmUInt32Set(&Msg.num_env, 0); + VbglHGCMParmUInt32Set(&Msg.cb_env, 0); + VbglHGCMParmPtrSet(&Msg.env, pStartupInfo->pszEnv, pStartupInfo->cbEnv); + if (pCtx->uProtocol < 2) + { + VbglHGCMParmPtrSet(&Msg.u.v1.username, pStartupInfo->pszUser, pStartupInfo->cbUser); + VbglHGCMParmPtrSet(&Msg.u.v1.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword); + VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0); + } + else + { + VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0); + VbglHGCMParmPtrSet(&Msg.u.v2.affinity, pStartupInfo->uAffinity, sizeof(pStartupInfo->uAffinity)); + } + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_FAILURE(rc)) + { + LogRel(("VbglR3GuestCtrlProcGetStart: 1 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + + if ( rc == VERR_BUFFER_OVERFLOW + && cRetries++ < cMaxRetries) + { +#define GROW_STR(a_Str, a_cbMax) \ + pStartupInfo->psz##a_Str = (char *)RTMemRealloc(pStartupInfo->psz##a_Str, \ + RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax)); \ + AssertPtrBreakStmt(pStartupInfo->psz##a_Str, VERR_NO_MEMORY); \ + pStartupInfo->cb##a_Str = RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax); + + /* We can't tell which parameter doesn't fit, so we have to resize all. */ + GROW_STR(Cmd , GUEST_PROC_MAX_CMD_LEN); + GROW_STR(Args, GUEST_PROC_MAX_ARGS_LEN); + GROW_STR(Env, GUEST_PROC_MAX_ENV_LEN); + +#undef GROW_STR + LogRel(("VbglR3GuestCtrlProcGetStart: 2 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + LogRel(("g_fVbglR3GuestCtrlHavePeekGetCancel=%RTbool\n", RT_BOOL(g_fVbglR3GuestCtrlHavePeekGetCancel))); + } + else + break; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(&pStartupInfo->fFlags); + Msg.num_args.GetUInt32(&pStartupInfo->cArgs); + Msg.num_env.GetUInt32(&pStartupInfo->cEnvVars); + Msg.cb_env.GetUInt32(&pStartupInfo->cbEnv); + if (pCtx->uProtocol < 2) + Msg.u.v1.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS); + else + { + Msg.u.v2.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS); + Msg.u.v2.priority.GetUInt32(&pStartupInfo->uPriority); + Msg.u.v2.num_affinity.GetUInt32(&pStartupInfo->cAffinity); + } + } + } while (( rc == VERR_INTERRUPTED + || rc == VERR_BUFFER_OVERFLOW) && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if (RT_SUCCESS(rc)) + { + *ppStartupInfo = pStartupInfo; + } + else + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo); + + LogRel(("VbglR3GuestCtrlProcGetStart: Returning %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Allocates and gets host data, based on the message ID. + * + * This will block until data becomes available. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param puPID Where to return the guest PID to retrieve output from on success. + * @param puHandle Where to return the guest process handle to retrieve output from on success. + * @param pfFlags Where to return the output flags on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puHandle, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcOutput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_GET_OUTPUT); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMMsgProcOutput, data)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.handle.GetUInt32(puHandle); + Msg.flags.GetUInt32(pfFlags); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves the input data from host which then gets sent to the started + * process (HOST_EXEC_SET_INPUT). + * + * This will block until data becomes available. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *pfFlags, + void *pvData, uint32_t cbData, + uint32_t *pcbSize) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcInput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_SET_INPUT); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.flags.GetUInt32(pfFlags); + Msg.size.GetUInt32(pcbSize); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_DIR_REMOVE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszPath, uint32_t cbPath, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirRemove Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_REMOVE); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_OPEN message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszFileName, uint32_t cbFileName, + char *pszAccess, uint32_t cbAccess, + char *pszDisposition, uint32_t cbDisposition, + char *pszSharing, uint32_t cbSharing, + uint32_t *puCreationMode, + uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 7, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertReturn(cbFileName, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszAccess, VERR_INVALID_POINTER); + AssertReturn(cbAccess, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDisposition, VERR_INVALID_POINTER); + AssertReturn(cbDisposition, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszSharing, VERR_INVALID_POINTER); + AssertReturn(cbSharing, VERR_INVALID_PARAMETER); + AssertPtrReturn(puCreationMode, VERR_INVALID_POINTER); + AssertPtrReturn(poffAt, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileOpen Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_OPEN); + VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName); + VbglHGCMParmPtrSet(&Msg.openmode, pszAccess, cbAccess); + VbglHGCMParmPtrSet(&Msg.disposition, pszDisposition, cbDisposition); + VbglHGCMParmPtrSet(&Msg.sharing, pszSharing, cbSharing); + VbglHGCMParmUInt32Set(&Msg.creationmode, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.creationmode.GetUInt32(puCreationMode); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_CLOSE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_READ message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *puToRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileRead Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(puToRead); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_READ_AT message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetReadAt(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puToRead, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileReadAt Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ_AT); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.offset.GetUInt64(poffAt); + Msg.size.GetUInt32(puToRead); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_WRITE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileWrite Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(pcbSize); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_FILE_WRITE_AT message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWriteAt(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileWriteAt Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE_AT); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(pcbSize); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_FILE_SEEK message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puSeekMethod, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puSeekMethod, VERR_INVALID_POINTER); + AssertPtrReturn(poffAt, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileSeek Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_SEEK); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.method, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.method.GetUInt32(puSeekMethod); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_TELL message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileTell Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_TELL); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_SET_SIZE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint64_t *pcbNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pcbNew, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileSetSize Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.id32Context, HOST_MSG_FILE_SET_SIZE); + VbglHGCMParmUInt32Set(&Msg.id32Handle, 0); + VbglHGCMParmUInt64Set(&Msg.cb64NewSize, 0); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.id32Context.GetUInt32(&pCtx->uContextID); + Msg.id32Handle.GetUInt32(puHandle); + Msg.cb64NewSize.GetUInt64(pcbNew); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_EXEC_TERMINATE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetTerminate(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puPID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcTerminate Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_TERMINATE); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_EXEC_WAIT_FOR message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puWaitFlags, uint32_t *puTimeoutMS) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcWaitFor Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_WAIT_FOR); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.timeout, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.flags.GetUInt32(puWaitFlags); + Msg.timeout.GetUInt32(puTimeoutMS); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Replies to a HOST_MSG_FILE_OPEN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param uFileHandle File handle of opened file on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint32_t uFileHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_OPEN); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.open.handle, uFileHandle); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.open)); +} + + +/** + * Replies to a HOST_MSG_FILE_CLOSE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u)); +} + + +/** + * Sends an unexpected file handling error to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbError(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_ERROR); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u)); +} + + +/** + * Replies to a HOST_MSG_FILE_READ message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pvData Pointer to read file data from guest on success. + * @param cbData Size (in bytes) of read file data from guest on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmPtrSet(&Msg.u.read.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.read)); +} + + +/** + * Replies to a HOST_MSG_FILE_READ_AT message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pvData Pointer to read file data from guest on success. + * @param cbData Size (in bytes) of read file data from guest on success. + * @param offNew New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbReadOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, + void *pvData, uint32_t cbData, int64_t offNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ_OFFSET); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmPtrSet(&Msg.u.ReadOffset.pvData, pvData, cbData); + VbglHGCMParmUInt64Set(&Msg.u.ReadOffset.off64New, (uint64_t)offNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.ReadOffset)); +} + + +/** + * Replies to a HOST_MSG_FILE_WRITE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.write.written, cbWritten); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.write)); +} + + +/** + * Replies to a HOST_MSG_FILE_WRITE_AT message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial. + * @param offNew New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWriteOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten, int64_t offNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.WriteOffset.cb32Written, cbWritten); + VbglHGCMParmUInt64Set(&Msg.u.WriteOffset.off64New, (uint64_t)offNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.WriteOffset)); +} + + +/** + * Replies to a HOST_MSG_FILE_SEEK message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param offCurrent New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SEEK); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.seek.offset, offCurrent); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.seek)); +} + + +/** + * Replies to a HOST_MSG_FILE_TELL message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param offCurrent Current offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_TELL); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.tell.offset, offCurrent); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.tell)); +} + + +/** + * Replies to a HOST_MSG_FILE_SET_SIZE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbNew New file size (in bytes) of the guest file on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t cbNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SET_SIZE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.SetSize.cb64Size, cbNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.SetSize)); +} + + +/** + * Callback for reporting a guest process status (along with some other stuff) to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uStatus Status to report. Of type PROC_STS_XXX. + * @param fFlags Additional status flags, depending on the reported status. See RTPROCSTATUS. + * @param pvData Pointer to additional status data. Optional. + * @param cbData Size (in bytes) of additional status data. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, uint32_t fFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatus Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_STATUS, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Sends output (from stdout/stderr) from a running process. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uHandle Guest process handle the output belong to. + * @param fFlags Additional output flags. + * @param pvData Pointer to actual output data. + * @param cbData Size (in bytes) of output data. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID,uint32_t uHandle, uint32_t fFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcOutput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_OUTPUT, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.handle, uHandle); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Callback for reporting back the input status of a guest process to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uStatus Status to report. Of type INPUT_STS_XXX. + * @param fFlags Additional input flags. + * @param cbWritten Size (in bytes) of input data handled. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, + uint32_t fFlags, uint32_t cbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatusInput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_INPUT_STATUS, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmUInt32Set(&Msg.written, cbWritten); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp new file mode 100644 index 00000000..6a439813 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp @@ -0,0 +1,1032 @@ +/* $Id: VBoxGuestR3LibGuestProp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#if defined(VBOX_VBGLR3_XFREE86) || defined(VBOX_VBGLR3_XORG) +# define VBOX_VBGLR3_XSERVER +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#ifndef VBOX_VBGLR3_XSERVER +# include <iprt/mem.h> +#endif +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/stdarg.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/HostServices/GuestPropertySvc.h> + +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" char* xf86strcpy(char*,const char*); +# undef strcpy +# define strcpy xf86strcpy +extern "C" void* xf86memchr(const void*,int,xf86size_t); +# undef memchr +# define memchr xf86memchr +extern "C" void* xf86memset(const void*,int,xf86size_t); +# undef memset +# define memset xf86memset + +#endif /* VBOX_VBGLR3_XFREE86 */ + +#ifdef VBOX_VBGLR3_XSERVER + +# undef RTStrEnd +# define RTStrEnd xf86RTStrEnd + +DECLINLINE(char const *) RTStrEnd(char const *pszString, size_t cchMax) +{ + /* Avoid potential issues with memchr seen in glibc. + * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */ + while (cchMax > RTSTR_MEMCHR_MAX) + { + char const *pszRet = (char const *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX); + if (RT_LIKELY(pszRet)) + return pszRet; + pszString += RTSTR_MEMCHR_MAX; + cchMax -= RTSTR_MEMCHR_MAX; + } + return (char const *)memchr(pszString, '\0', cchMax); +} + +DECLINLINE(char *) RTStrEnd(char *pszString, size_t cchMax) +{ + /* Avoid potential issues with memchr seen in glibc. + * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */ + while (cchMax > RTSTR_MEMCHR_MAX) + { + char *pszRet = (char *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX); + if (RT_LIKELY(pszRet)) + return pszRet; + pszString += RTSTR_MEMCHR_MAX; + cchMax -= RTSTR_MEMCHR_MAX; + } + return (char *)memchr(pszString, '\0', cchMax); +} + +#endif /* VBOX_VBGLR3_XSERVER */ + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Structure containing information needed to enumerate through guest + * properties. + * + * @remarks typedef in VBoxGuestLib.h. + */ +struct VBGLR3GUESTPROPENUM +{ + /** @todo add a magic and validate the handle. */ + /** The buffer containing the raw enumeration data */ + char *pchBuf; + /** The end of the buffer */ + char *pchBufEnd; + /** Pointer to the next entry to enumerate inside the buffer */ + char *pchNext; +}; + + + +/** + * Connects to the guest property service. + * + * @returns VBox status code + * @returns VERR_NOT_SUPPORTED if guest properties are not available on the host. + * @param pidClient Where to put the client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3GuestPropConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxGuestPropSvc", pidClient); + if (rc == VERR_NOT_IMPLEMENTED || rc == VERR_HGCM_SERVICE_NOT_FOUND) + rc = VERR_NOT_SUPPORTED; + return rc; +} + + +/** + * Disconnect from the guest property service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + */ +VBGLR3DECL(int) VbglR3GuestPropDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Checks if @a pszPropName exists. + * + * @returns \c true if the guest property exists, \c false if not. + * @param idClient The HGCM client ID for the guest property session. + * @param pszPropName The property name. + */ +VBGLR3DECL(bool) VbglR3GuestPropExist(uint32_t idClient, const char *pszPropName) +{ + return RT_SUCCESS(VbglR3GuestPropReadEx(idClient, pszPropName, NULL /*ppszValue*/, NULL /* ppszFlags */, NULL /* puTimestamp */)); +} + + +/** + * Write a property value. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Utf8 + * @param pszValue The value to store. Utf8. If this is NULL then + * the property will be removed. + * @param pszFlags The flags for the property + */ +VBGLR3DECL(int) VbglR3GuestPropWrite(HGCMCLIENTID idClient, const char *pszName, const char *pszValue, const char *pszFlags) +{ + int rc; + + if (pszValue != NULL) + { + GuestPropMsgSetProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 3); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSetString(&Msg.value, pszValue); + VbglHGCMParmPtrSetString(&Msg.flags, pszFlags); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + else + { + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + return rc; +} + + +/** + * Write a property value. + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValue The value to store. Must be valid UTF-8. + * If this is NULL then the property will be removed. + * + * @note if the property already exists and pszValue is not NULL then the + * property's flags field will be left unchanged + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValue(HGCMCLIENTID idClient, const char *pszName, const char *pszValue) +{ + int rc; + + if (pszValue != NULL) + { + GuestPropMsgSetPropertyValue Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 2); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSetString(&Msg.value, pszValue); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + else + { + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + return rc; +} + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Write a property value where the value is formatted in RTStrPrintfV fashion. + * + * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY. + * + * @param idClient The client ID returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted. + * @param va The format arguments. + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValueV(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, va_list va) +{ + /* + * Format the value and pass it on to the setter. + */ + int rc = VERR_NO_STR_MEMORY; + char *pszValue; + if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0) + { + rc = VbglR3GuestPropWriteValue(idClient, pszName, pszValue); + RTStrFree(pszValue); + } + return rc; +} + + +/** + * Write a property value where the value is formatted in RTStrPrintf fashion. + * + * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY. + * + * @param idClient The client ID returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted. + * @param ... The format arguments. + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValueF(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, ...) +{ + va_list va; + va_start(va, pszValueFormat); + int rc = VbglR3GuestPropWriteValueV(idClient, pszName, pszValueFormat, va); + va_end(va); + return rc; +} +#endif /* VBOX_VBGLR3_XSERVER */ + +/** + * Retrieve a property. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, pszValue, pu64Timestamp and pszFlags + * containing valid data. + * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pcBuf is not large + * enough. In this case the size needed will be placed in + * @a pcbBufActual if it is not NULL. + * @retval VERR_NOT_FOUND if the key wasn't found. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Utf8 + * @param pvBuf A scratch buffer to store the data retrieved into. + * The returned data is only valid for it's lifetime. + * @a ppszValue will point to the start of this buffer. + * @param cbBuf The size of @a pcBuf + * @param ppszValue Where to store the pointer to the value retrieved. + * Optional. + * @param pu64Timestamp Where to store the timestamp. Optional. + * @param ppszFlags Where to store the pointer to the flags. Optional. + * @param pcbBufActual If @a pcBuf is not large enough, the size needed. + * Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropRead(HGCMCLIENTID idClient, const char *pszName, + void *pvBuf, uint32_t cbBuf, + char **ppszValue, uint64_t *pu64Timestamp, + char **ppszFlags, + uint32_t *pcbBufActual) +{ + /* + * Create the GET_PROP message and call the host. + */ + GuestPropMsgGetProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_GET_PROP, 4); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf); + VbglHGCMParmUInt64Set(&Msg.timestamp, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * The cbBufActual parameter is also returned on overflow so the call can + * adjust his/her buffer. + */ + if ( rc == VERR_BUFFER_OVERFLOW + || pcbBufActual != NULL) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual); + AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2); + } + if (RT_FAILURE(rc)) + return rc; + + /* + * Buffer layout: Value\0Flags\0. + * + * If the caller cares about any of these strings, make sure things are + * properly terminated (paranoia). + */ + if ( RT_SUCCESS(rc) + && (ppszValue != NULL || ppszFlags != NULL)) + { + /* Validate / skip 'Name'. */ + char *pszFlags = RTStrEnd((char *)pvBuf, cbBuf) + 1; + AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA); + if (ppszValue) + *ppszValue = (char *)pvBuf; + + if (ppszFlags) + { + /* Validate 'Flags'. */ + char *pszEos = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf)); + AssertPtrReturn(pszEos, VERR_TOO_MUCH_DATA); + *ppszFlags = pszFlags; + } + } + + /* And the timestamp, if requested. */ + if (pu64Timestamp != NULL) + { + rc = VbglHGCMParmUInt64Get(&Msg.timestamp, pu64Timestamp); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} + +/** + * Reads a guest property by returning allocated values. + * + * @returns VBox status code, fully bitched. + * + * @param idClient The HGCM client ID for the guest property session. + * @param pszPropName The property name. + * @param ppszValue Where to return the value. This is always set + * to NULL. Needs to be free'd using RTStrFree(). Optional. + * @param ppszFlags Where to return the value flags. + * Needs to be free'd using RTStrFree(). Optional. + * @param puTimestamp Where to return the timestamp. This is only set + * on success. Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropReadEx(uint32_t idClient, + const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) +{ + AssertPtrReturn(pszPropName, VERR_INVALID_POINTER); + + uint32_t cbBuf = _1K; + void *pvBuf = NULL; + int rc = VINF_SUCCESS; /* MSC can't figure out the loop */ + + if (ppszValue) + *ppszValue = NULL; + + for (unsigned cTries = 0; cTries < 10; cTries++) + { + /* + * (Re-)Allocate the buffer and try read the property. + */ + RTMemFree(pvBuf); + pvBuf = RTMemAlloc(cbBuf); + if (!pvBuf) + { + rc = VERR_NO_MEMORY; + break; + } + char *pszValue; + char *pszFlags; + uint64_t uTimestamp; + rc = VbglR3GuestPropRead(idClient, pszPropName, pvBuf, cbBuf, &pszValue, &uTimestamp, &pszFlags, NULL); + if (RT_FAILURE(rc)) + { + if (rc == VERR_BUFFER_OVERFLOW) + { + /* try again with a bigger buffer. */ + cbBuf *= 2; + continue; + } + break; + } + + if (ppszValue) + { + *ppszValue = RTStrDup(pszValue); + if (!*ppszValue) + { + rc = VERR_NO_MEMORY; + break; + } + } + + if (puTimestamp) + *puTimestamp = uTimestamp; + if (ppszFlags) + *ppszFlags = RTStrDup(pszFlags); + break; /* done */ + } + + if (pvBuf) + RTMemFree(pvBuf); + return rc; +} + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Retrieve a property value, allocating space for it. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, *ppszValue containing valid data. + * @retval VERR_NOT_FOUND if the key wasn't found. + * @retval VERR_TOO_MUCH_DATA if we were unable to determine the right size + * to allocate for the buffer. This can happen as the result of a + * race between our allocating space and the host changing the + * property value. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Must be valid UTF-8. + * @param ppszValue Where to store the pointer to the value returned. + * This is always set to NULL or to the result, even + * on failure. + */ +VBGLR3DECL(int) VbglR3GuestPropReadValueAlloc(HGCMCLIENTID idClient, const char *pszName, char **ppszValue) +{ + /* + * Quick input validation. + */ + AssertPtr(ppszValue); + *ppszValue = NULL; + AssertPtrReturn(pszName, VERR_INVALID_PARAMETER); + + /* + * There is a race here between our reading the property size and the + * host changing the value before we read it. Try up to ten times and + * report the problem if that fails. + */ + char *pszValue = NULL; + void *pvBuf = NULL; + uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN; + int rc = VERR_BUFFER_OVERFLOW; + for (unsigned i = 0; i < 10 && rc == VERR_BUFFER_OVERFLOW; ++i) + { + /* We leave a bit of space here in case the maximum value is raised. */ + cbBuf += 1024; + void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf); + if (pvTmpBuf) + { + pvBuf = pvTmpBuf; + rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cbBuf, &pszValue, NULL, NULL, &cbBuf); + } + else + rc = VERR_NO_MEMORY; + } + if (RT_SUCCESS(rc)) + { + Assert(pszValue == (char *)pvBuf); + *ppszValue = pszValue; + } + else + { + RTMemFree(pvBuf); + if (rc == VERR_BUFFER_OVERFLOW) + /* VERR_BUFFER_OVERFLOW has a different meaning here as a + * return code, but we need to report the race. */ + rc = VERR_TOO_MUCH_DATA; + } + + return rc; +} + + +/** + * Free the memory used by VbglR3GuestPropReadValueAlloc for returning a + * value. + * + * @param pszValue the memory to be freed. NULL pointers will be ignored. + */ +VBGLR3DECL(void) VbglR3GuestPropReadValueFree(char *pszValue) +{ + RTMemFree(pszValue); +} +#endif /* VBOX_VBGLR3_XSERVER */ + +/** + * Retrieve a property value, using a user-provided buffer to store it. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, pszValue containing valid data. + * @retval VERR_BUFFER_OVERFLOW and the size needed in pcchValueActual if the + * buffer provided was too small + * @retval VERR_NOT_FOUND if the key wasn't found. + * + * @note There is a race here between obtaining the size of the buffer + * needed to hold the value and the value being updated. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Utf8 + * @param pszValue Where to store the value retrieved. + * @param cchValue The size of the buffer pointed to by @a pszValue + * @param pcchValueActual Where to store the size of the buffer needed if + * the buffer supplied is too small. Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropReadValue(HGCMCLIENTID idClient, const char *pszName, + char *pszValue, uint32_t cchValue, + uint32_t *pcchValueActual) +{ + void *pvBuf = pszValue; + uint32_t cchValueActual; + int rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cchValue, &pszValue, NULL, NULL, &cchValueActual); + if (pcchValueActual != NULL) + *pcchValueActual = cchValueActual; + return rc; +} + + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Raw API for enumerating guest properties which match a given pattern. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success and pcBuf points to a packed array + * of the form \<name\>, \<value\>, \<timestamp string\>, \<flags\>, + * terminated by four empty strings. pcbBufActual will contain the + * total size of the array. + * @retval VERR_BUFFER_OVERFLOW if the buffer provided was too small. In + * this case pcbBufActual will contain the size of the buffer needed. + * @returns IPRT error code in other cases, and pchBufActual is undefined. + * + * @param idClient The client ID returned by VbglR3GuestPropConnect + * @param pszzPatterns A packed array of zero terminated strings, terminated + * by an empty string. + * @param pcBuf The buffer to store the results to. + * @param cbBuf The size of the buffer + * @param pcbBufActual Where to store the size of the returned data on + * success or the buffer size needed if @a pcBuf is too + * small. + */ +VBGLR3DECL(int) VbglR3GuestPropEnumRaw(HGCMCLIENTID idClient, + const char *pszzPatterns, + char *pcBuf, + uint32_t cbBuf, + uint32_t *pcbBufActual) +{ + GuestPropMsgEnumProperties Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3); + + /* Get the length of the patterns array... */ + size_t cchPatterns = 0; + for (size_t cchCurrent = strlen(pszzPatterns); cchCurrent != 0; + cchCurrent = strlen(pszzPatterns + cchPatterns)) + cchPatterns += cchCurrent + 1; + /* ...including the terminator. */ + ++cchPatterns; + VbglHGCMParmPtrSet(&Msg.patterns, (char *)pszzPatterns, (uint32_t)cchPatterns); + VbglHGCMParmPtrSet(&Msg.strings, pcBuf, cbBuf); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if ( pcbBufActual + && ( RT_SUCCESS(rc) + || rc == VERR_BUFFER_OVERFLOW)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual); + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Start enumerating guest properties which match a given pattern. + * + * This function creates a handle which can be used to continue enumerating. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, *ppHandle points to a handle for continuing + * the enumeration and *ppszName, *ppszValue, *pu64Timestamp and + * *ppszFlags are set. + * @retval VERR_TOO_MUCH_DATA if it was not possible to determine the amount + * of local space needed to store all the enumeration data. This is + * due to a race between allocating space and the host adding new + * data, so retrying may help here. Other parameters are left + * uninitialised + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param papszPatterns The patterns against which the properties are + * matched. Pass NULL if everything should be matched. + * @param cPatterns The number of patterns in @a papszPatterns. 0 means + * match everything. + * @param ppHandle where the handle for continued enumeration is stored + * on success. This must be freed with + * VbglR3GuestPropEnumFree when it is no longer needed. + * @param ppszName Where to store the next property name. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param ppszValue Where to store the next property value. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param pu64Timestamp Where to store the next property timestamp. This + * will be set to zero if there are no more properties + * to enumerate. Optional. + * @param ppszFlags Where to store the next property flags. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * + * @remarks While all output parameters are optional, you need at least one to + * figure out when to stop. + */ +VBGLR3DECL(int) VbglR3GuestPropEnum(HGCMCLIENTID idClient, + char const * const *papszPatterns, + uint32_t cPatterns, + PVBGLR3GUESTPROPENUM *ppHandle, + char const **ppszName, + char const **ppszValue, + uint64_t *pu64Timestamp, + char const **ppszFlags) +{ + /* Create the handle. */ + PVBGLR3GUESTPROPENUM pHandle = (PVBGLR3GUESTPROPENUM)RTMemAllocZ(sizeof(VBGLR3GUESTPROPENUM)); + if (RT_LIKELY(pHandle)) + {/* likely */} + else + return VERR_NO_MEMORY; + + /* Get the length of the pattern string, including the final terminator. */ + size_t cbPatterns = 1; + for (uint32_t i = 0; i < cPatterns; ++i) + cbPatterns += strlen(papszPatterns[i]) + 1; + + /* Pack the pattern array. */ + char *pszzPatterns = (char *)RTMemAlloc(cbPatterns); + size_t off = 0; + for (uint32_t i = 0; i < cPatterns; ++i) + { + size_t cb = strlen(papszPatterns[i]) + 1; + memcpy(&pszzPatterns[off], papszPatterns[i], cb); + off += cb; + } + pszzPatterns[off] = '\0'; + + /* In reading the guest property data we are racing against the host + * adding more of it, so loop a few times and retry on overflow. */ + uint32_t cbBuf = 4096; /* picked out of thin air */ + char *pchBuf = NULL; + int rc = VINF_SUCCESS; + for (int i = 0; i < 10; ++i) + { + void *pvNew = RTMemRealloc(pchBuf, cbBuf); + if (pvNew) + pchBuf = (char *)pvNew; + else + { + rc = VERR_NO_MEMORY; + break; + } + rc = VbglR3GuestPropEnumRaw(idClient, pszzPatterns, pchBuf, cbBuf, &cbBuf); + if (rc != VERR_BUFFER_OVERFLOW) + break; + cbBuf += 4096; /* Just to increase our chances */ + } + RTMemFree(pszzPatterns); + if (RT_SUCCESS(rc)) + { + /* + * Complete the handle and call VbglR3GuestPropEnumNext to retrieve the first entry. + */ + pHandle->pchNext = pchBuf; + pHandle->pchBuf = pchBuf; + pHandle->pchBufEnd = pchBuf + cbBuf; + + const char *pszNameTmp; + if (!ppszName) + ppszName = &pszNameTmp; + rc = VbglR3GuestPropEnumNext(pHandle, ppszName, ppszValue, pu64Timestamp, ppszFlags); + if (RT_SUCCESS(rc)) + { + *ppHandle = pHandle; + return rc; + } + } + else if (rc == VERR_BUFFER_OVERFLOW) + rc = VERR_TOO_MUCH_DATA; + RTMemFree(pchBuf); + RTMemFree(pHandle); + return rc; +} + + +/** + * Get the next guest property. + * + * See @a VbglR3GuestPropEnum. + * + * @returns VBox status code. + * + * @param pHandle Handle obtained from @a VbglR3GuestPropEnum. + * @param ppszName Where to store the next property name. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param ppszValue Where to store the next property value. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param pu64Timestamp Where to store the next property timestamp. This + * will be set to zero if there are no more properties + * to enumerate. Optional. + * @param ppszFlags Where to store the next property flags. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * + * @remarks While all output parameters are optional, you need at least one to + * figure out when to stop. + */ +VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle, + char const **ppszName, + char const **ppszValue, + uint64_t *pu64Timestamp, + char const **ppszFlags) +{ + /* + * The VBGLR3GUESTPROPENUM structure contains a buffer containing the raw + * properties data and a pointer into the buffer which tracks how far we + * have parsed so far. The buffer contains packed strings in groups of + * four - name, value, timestamp (as a decimal string) and flags. It is + * terminated by four empty strings. We can rely on this layout unless + * the caller has been poking about in the structure internals, in which + * case they must take responsibility for the results. + * + * Layout: + * Name\0Value\0Timestamp\0Flags\0 + */ + char *pchNext = pHandle->pchNext; /* The cursor. */ + char *pchEnd = pHandle->pchBufEnd; /* End of buffer, for size calculations. */ + + char *pszName = pchNext; + char *pszValue = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); /* 0x1 is also an invalid pointer :) */ + + char *pszTimestamp = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + char *pszFlags = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + /* + * Don't move the index pointer if we found the terminating "\0\0\0\0" entry. + * Don't try convert the timestamp either. + */ + uint64_t u64Timestamp; + if (*pszName != '\0') + { + pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + /* Convert the timestamp string into a number. */ + int rc = RTStrToUInt64Full(pszTimestamp, 0, &u64Timestamp); + AssertRCSuccessReturn(rc, VERR_PARSE_ERROR); + + pHandle->pchNext = pchNext; + AssertPtr(pchNext); + } + else + { + u64Timestamp = 0; + AssertMsgReturn(!*pszValue && !*pszTimestamp && !*pszFlags, + ("'%s' '%s' '%s'\n", pszValue, pszTimestamp, pszFlags), + VERR_PARSE_ERROR); + } + + /* + * Everything is fine, set the return values. + */ + if (ppszName) + *ppszName = *pszName != '\0' ? pszName : NULL; + if (ppszValue) + *ppszValue = *pszValue != '\0' ? pszValue : NULL; + if (pu64Timestamp) + *pu64Timestamp = u64Timestamp; + if (ppszFlags) + *ppszFlags = *pszFlags != '\0' ? pszFlags : NULL; + return VINF_SUCCESS; +} + + +/** + * Free an enumeration handle returned by @a VbglR3GuestPropEnum. + * @param pHandle the handle to free + */ +VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle) +{ + if (!pHandle) + return; + RTMemFree(pHandle->pchBuf); + RTMemFree(pHandle); +} + + +/** + * Deletes a guest property. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to delete. Utf8 + */ +VBGLR3DECL(int) VbglR3GuestPropDelete(HGCMCLIENTID idClient, const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Deletes a set of keys. + * + * The set is specified in the same way as for VbglR3GuestPropEnum. + * + * @returns VBox status code. Stops on first failure. + * See also VbglR3GuestPropEnum. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param papszPatterns The patterns against which the properties are + * matched. Pass NULL if everything should be matched. + * @param cPatterns The number of patterns in @a papszPatterns. 0 means + * match everything. + */ +VBGLR3DECL(int) VbglR3GuestPropDelSet(HGCMCLIENTID idClient, + const char * const *papszPatterns, + uint32_t cPatterns) +{ + PVBGLR3GUESTPROPENUM pHandle; + char const *pszName, *pszValue, *pszFlags; + uint64_t pu64Timestamp; + int rc = VbglR3GuestPropEnum(idClient, + (char **)papszPatterns, /** @todo fix this cast. */ + cPatterns, + &pHandle, + &pszName, + &pszValue, + &pu64Timestamp, + &pszFlags); + + while (RT_SUCCESS(rc) && pszName) + { + rc = VbglR3GuestPropWriteValue(idClient, pszName, NULL); + if (RT_FAILURE(rc)) + break; + + rc = VbglR3GuestPropEnumNext(pHandle, + &pszName, + &pszValue, + &pu64Timestamp, + &pszFlags); + } + + VbglR3GuestPropEnumFree(pHandle); + return rc; +} + + +/** + * Wait for notification of changes to a guest property. If this is called in + * a loop, the timestamp of the last notification seen can be passed as a + * parameter to be sure that no notifications are missed. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, @a ppszName, @a ppszValue, + * @a pu64Timestamp and @a ppszFlags containing valid data. + * @retval VINF_NOT_FOUND if no previous notification could be found with the + * timestamp supplied. This will normally mean that a large number + * of notifications occurred in between. + * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pvBuf is not large + * enough. In this case the size needed will be placed in + * @a pcbBufActual if it is not NULL. + * @retval VERR_TIMEOUT if a timeout occurred before a notification was seen. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszPatterns The patterns that the property names must matchfor + * the change to be reported. + * @param pvBuf A scratch buffer to store the data retrieved into. + * The returned data is only valid for it's lifetime. + * @a ppszValue will point to the start of this buffer. + * @param cbBuf The size of @a pvBuf + * @param u64Timestamp The timestamp of the last event seen. Pass zero + * to wait for the next event. + * @param cMillies Timeout in milliseconds. Use RT_INDEFINITE_WAIT + * to wait indefinitely. + * @param ppszName Where to store the pointer to the name retrieved. + * Optional. + * @param ppszValue Where to store the pointer to the value retrieved. + * Optional. + * @param pu64Timestamp Where to store the timestamp. Optional. + * @param ppszFlags Where to store the pointer to the flags. Optional. + * @param pcbBufActual If @a pcBuf is not large enough, the size needed. + * Optional. + * @param pfWasDeleted A flag which indicates that property was deleted. + * Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropWait(HGCMCLIENTID idClient, + const char *pszPatterns, + void *pvBuf, uint32_t cbBuf, + uint64_t u64Timestamp, uint32_t cMillies, + char ** ppszName, char **ppszValue, + uint64_t *pu64Timestamp, char **ppszFlags, + uint32_t *pcbBufActual, bool *pfWasDeleted) +{ + /* + * Create the GET_NOTIFICATION message and call the host. + */ + GuestPropMsgGetNotification Msg; + VBGL_HGCM_HDR_INIT_TIMED(&Msg.hdr, idClient, GUEST_PROP_FN_GET_NOTIFICATION, 4, cMillies); + + VbglHGCMParmPtrSetString(&Msg.patterns, pszPatterns); + RT_BZERO(pvBuf, cbBuf); + VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf); + VbglHGCMParmUInt64Set(&Msg.timestamp, u64Timestamp); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * The cbBufActual parameter is also returned on overflow so the caller can + * adjust their buffer. + */ + if ( rc == VERR_BUFFER_OVERFLOW + && pcbBufActual != NULL) + { + int rc2 = Msg.size.GetUInt32(pcbBufActual); + AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2); + } + if (RT_FAILURE(rc)) + return rc; + + /* + * Buffer layout: Name\0Value\0Flags\0fWasDeleted\0. + * + * If the caller cares about any of these strings, make sure things are + * properly terminated (paranoia). + */ + if ( RT_SUCCESS(rc) + && (ppszName != NULL || ppszValue != NULL || ppszFlags != NULL || pfWasDeleted != NULL)) + { + /* Validate / skip 'Name'. */ + char *pszValue = RTStrEnd((char *)pvBuf, cbBuf) + 1; + AssertPtrReturn(pszValue, VERR_TOO_MUCH_DATA); + if (ppszName) + *ppszName = (char *)pvBuf; + + /* Validate / skip 'Value'. */ + char *pszFlags = RTStrEnd(pszValue, cbBuf - (pszValue - (char *)pvBuf)) + 1; + AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA); + if (ppszValue) + *ppszValue = pszValue; + + if (ppszFlags) + *ppszFlags = pszFlags; + + /* Skip 'Flags' and deal with 'fWasDeleted' if it's present. */ + char *pszWasDeleted = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf)) + 1; + AssertPtrReturn(pszWasDeleted, VERR_TOO_MUCH_DATA); + char chWasDeleted = 0; + if ( (size_t)pszWasDeleted - (size_t)pvBuf < cbBuf + && (chWasDeleted = *pszWasDeleted) != '\0') + AssertMsgReturn((chWasDeleted == '0' || chWasDeleted == '1') && pszWasDeleted[1] == '\0', + ("'%s'\n", pszWasDeleted), VERR_PARSE_ERROR); + if (pfWasDeleted) + *pfWasDeleted = chWasDeleted == '1'; + } + + /* And the timestamp, if requested. */ + if (pu64Timestamp != NULL) + { + rc = Msg.timestamp.GetUInt64(pu64Timestamp); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} +#endif /* VBOX_VBGLR3_XSERVER */ diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp new file mode 100644 index 00000000..92bc9037 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp @@ -0,0 +1,119 @@ +/* $Id: VBoxGuestR3LibGuestUser.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * guest user reporting / utility functions. + */ + +/* + * Copyright (C) 2013-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <VBox/log.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Reports a state change of a specific guest user. + * + * @returns IPRT status value + * @param pszUser Guest user name to report state for. + * @param pszDomain Domain the guest user's account is bound to. + * @param enmState Guest user state to report. + * @param puDetails Pointer to state details. Optional. + * @param cbDetails Size (in bytes) of state details. Pass 0 + * if puDetails is NULL. + */ +VBGLR3DECL(int) VbglR3GuestUserReportState(const char *pszUser, const char *pszDomain, VBoxGuestUserState enmState, + uint8_t *puDetails, uint32_t cbDetails) +{ + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + /* pszDomain is optional. */ + /* puDetails is optional. */ + AssertReturn(cbDetails == 0 || puDetails != NULL, VERR_INVALID_PARAMETER); + AssertReturn(cbDetails < 16U*_1M, VERR_OUT_OF_RANGE); + + uint32_t cbBase = sizeof(VMMDevReportGuestUserState); + uint32_t cbUser = (uint32_t)strlen(pszUser) + 1; /* Include terminating zero */ + uint32_t cbDomain = pszDomain ? (uint32_t)strlen(pszDomain) + 1 /* Ditto */ : 0; + + /* Allocate enough space for all fields. */ + uint32_t cbSize = cbBase + + cbUser + + cbDomain + + cbDetails; + VMMDevReportGuestUserState *pReport = (VMMDevReportGuestUserState *)RTMemAllocZ(cbSize); + if (!pReport) + return VERR_NO_MEMORY; + + int rc = vmmdevInitRequest(&pReport->header, VMMDevReq_ReportGuestUserState); + if (RT_SUCCESS(rc)) + { + pReport->header.size = cbSize; + + pReport->status.state = enmState; + pReport->status.cbUser = cbUser; + pReport->status.cbDomain = cbDomain; + pReport->status.cbDetails = cbDetails; + + /* + * Note: cbOffDynamic contains the first dynamic array entry within + * VBoxGuestUserStatus. + * Therefore it's vital to *not* change the order of the struct members + * without altering this code. Don't try this at home. + */ + uint32_t cbOffDynamic = RT_UOFFSETOF(VBoxGuestUserStatus, szUser); + + /* pDynamic marks the beginning for the dynamically allocated areas. */ + uint8_t *pDynamic = (uint8_t *)&pReport->status; + pDynamic += cbOffDynamic; + AssertPtr(pDynamic); + + memcpy(pDynamic, pszUser, cbUser); + if (cbDomain) + memcpy(pDynamic + cbUser, pszDomain, cbDomain); + if (cbDetails) + memcpy(pDynamic + cbUser + cbDomain, puDetails, cbDetails); + + rc = vbglR3GRPerform(&pReport->header); + } + + RTMemFree(pReport); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp new file mode 100644 index 00000000..ee295737 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp @@ -0,0 +1,108 @@ +/* $Id: VBoxGuestR3LibHGCM.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * generic HGCM. + */ + +/* + * Copyright (C) 2015-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" +#include <VBox/VBoxGuestLib.h> +#include <iprt/string.h> + + +/** + * Connects to an HGCM service. + * + * @returns VBox status code + * @param pszServiceName Name of the host service. + * @param pidClient Where to put the client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient) +{ + AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER); + AssertPtrReturn(pidClient, VERR_INVALID_POINTER); + + VBGLIOCHGCMCONNECT Info; + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT); + Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + int rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName); + if (RT_FAILURE(rc)) + return rc; + rc = vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + *pidClient = Info.u.Out.idClient; + return rc; +} + + +/** + * Disconnect from an HGCM service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3HGCMConnect(). + */ +VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient) +{ + VBGLIOCHGCMDISCONNECT Info; + VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT); + Info.u.In.idClient = idClient; + + return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info)); +} + + +/** + * Makes a fully prepared HGCM call. + * + * @returns VBox status code. + * @param pInfo Fully prepared HGCM call info. + * @param cbInfo Size of the info. This may sometimes be larger than + * what the parameter count indicates because of + * parameter changes between versions and such. + */ +VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo) +{ + /* Expect caller to have filled in pInfo. */ + AssertMsg(pInfo->Hdr.cbIn == cbInfo, ("cbIn=%#x cbInfo=%#zx\n", pInfo->Hdr.cbIn, cbInfo)); + AssertMsg(pInfo->Hdr.cbOut == cbInfo, ("cbOut=%#x cbInfo=%#zx\n", pInfo->Hdr.cbOut, cbInfo)); + Assert(sizeof(*pInfo) + pInfo->cParms * sizeof(HGCMFunctionParameter) <= cbInfo); + Assert(pInfo->u32ClientID != 0); + + return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CALL(cbInfo), &pInfo->Hdr, cbInfo); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp new file mode 100644 index 00000000..ec4a7fe0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp @@ -0,0 +1,235 @@ +/* $Id: VBoxGuestR3LibHostChannel.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Host Channel. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +#include <iprt/mem.h> + +#include <VBox/HostServices/VBoxHostChannel.h> + +#include "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3HostChannelInit(uint32_t *pidClient) +{ + return VbglR3HGCMConnect("VBoxHostChannel", pidClient); +} + +VBGLR3DECL(void) VbglR3HostChannelTerm(uint32_t idClient) +{ + VbglR3HGCMDisconnect(idClient); +} + +VBGLR3DECL(int) VbglR3HostChannelAttach(uint32_t *pu32ChannelHandle, + uint32_t u32HGCMClientId, + const char *pszName, + uint32_t u32Flags) +{ + /* Make a heap copy of the name, because HGCM can not use some of other memory types. */ + size_t cbName = strlen(pszName) + 1; + char *pszCopy = (char *)RTMemAlloc(cbName); + if (pszCopy == NULL) + { + return VERR_NO_MEMORY; + } + + memcpy(pszCopy, pszName, cbName); + + VBoxHostChannelAttach parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_ATTACH, 3); + VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName); + VbglHGCMParmUInt32Set(&parms.flags, u32Flags); + VbglHGCMParmUInt32Set(&parms.handle, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + *pu32ChannelHandle = parms.handle.u.value32; + + RTMemFree(pszCopy); + + return rc; +} + +VBGLR3DECL(void) VbglR3HostChannelDetach(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId) +{ + VBoxHostChannelDetach parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_DETACH, 1); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + + VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelSend(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + void *pvData, + uint32_t cbData) +{ + VBoxHostChannelSend parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_SEND, 2); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + + return VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelRecv(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeReceived, + uint32_t *pu32SizeRemaining) +{ + VBoxHostChannelRecv parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_RECV, 4); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeReceived, 0); + VbglHGCMParmUInt32Set(&parms.sizeRemaining, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeReceived = parms.sizeReceived.u.value32; + *pu32SizeRemaining = parms.sizeRemaining.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelControl(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + VBoxHostChannelControl parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_CONTROL, 5); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmUInt32Set(&parms.code, u32Code); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeDataReturned = parms.sizeDataReturned.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelEventWait(uint32_t *pu32ChannelHandle, + uint32_t u32HGCMClientId, + uint32_t *pu32EventId, + void *pvParm, + uint32_t cbParm, + uint32_t *pu32SizeReturned) +{ + VBoxHostChannelEventWait parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_WAIT, 4); + VbglHGCMParmUInt32Set(&parms.handle, 0); + VbglHGCMParmUInt32Set(&parms.id, 0); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmUInt32Set(&parms.sizeReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32ChannelHandle = parms.handle.u.value32; + *pu32EventId = parms.id.u.value32; + *pu32SizeReturned = parms.sizeReturned.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelEventCancel(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId) +{ + RT_NOREF1(u32ChannelHandle); + + VBoxHostChannelEventCancel parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_CANCEL, 0); + + return VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelQuery(const char *pszName, + uint32_t u32HGCMClientId, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + /* Make a heap copy of the name, because HGCM can not use some of other memory types. */ + size_t cbName = strlen(pszName) + 1; + char *pszCopy = (char *)RTMemAlloc(cbName); + if (pszCopy == NULL) + { + return VERR_NO_MEMORY; + } + + memcpy(pszCopy, pszName, cbName); + + VBoxHostChannelQuery parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_QUERY, 5); + VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName); + VbglHGCMParmUInt32Set(&parms.code, u32Code); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeDataReturned = parms.sizeDataReturned.u.value32; + } + + RTMemFree(pszCopy); + + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp new file mode 100644 index 00000000..9bd22e8a --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp @@ -0,0 +1,226 @@ +/* $Id: */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, host version check. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include <VBox/log.h> + +#ifdef RT_OS_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <iprt/win/windows.h> +#endif + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Checks for a Guest Additions update by comparing the installed version on the + * guest and the reported host version. + * + * @returns VBox status code + * + * @param idClient The client id returned by + * VbglR3InfoSvcConnect(). + * @param pfUpdate Receives pointer to boolean flag indicating + * whether an update was found or not. + * @param ppszHostVersion Receives pointer of allocated version string. + * The returned pointer must be freed using + * VbglR3GuestPropReadValueFree(). Always set to + * NULL. + * @param ppszGuestVersion Receives pointer of allocated revision string. + * The returned pointer must be freed using + * VbglR3GuestPropReadValueFree(). Always set to + * NULL. + */ +VBGLR3DECL(int) VbglR3HostVersionCheckForUpdate(HGCMCLIENTID idClient, bool *pfUpdate, char **ppszHostVersion, char **ppszGuestVersion) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(pfUpdate); + AssertPtr(ppszHostVersion); + AssertPtr(ppszGuestVersion); + + *ppszHostVersion = NULL; + *ppszGuestVersion = NULL; + + /* We assume we have an update initially. + Every block down below is allowed to veto */ + *pfUpdate = true; + + /* Do we need to do all this stuff? */ + char *pszCheckHostVersion; + int rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/CheckHostVersion", &pszCheckHostVersion); + if (RT_FAILURE(rc)) + { + if (rc == VERR_NOT_FOUND) + rc = VINF_SUCCESS; /* If we don't find the value above we do the check by default */ + else + LogFlow(("Could not read check host version flag! rc = %Rrc\n", rc)); + } + else + { + /* Only don't do the check if we have a valid "0" in it */ + if (!strcmp(pszCheckHostVersion, "0")) + { + LogRel(("No host version update check performed (disabled).\n")); + *pfUpdate = false; + } + VbglR3GuestPropReadValueFree(pszCheckHostVersion); + } + + /* Collect all needed information */ + /* Make sure we only notify the user once by comparing the host version with + * the last checked host version (if any) */ + if (RT_SUCCESS(rc) && *pfUpdate) + { + /* Look up host version */ + rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/HostInfo/VBoxVer", ppszHostVersion); + if (RT_FAILURE(rc)) + { + LogFlow(("Could not read VBox host version! rc = %Rrc\n", rc)); + } + else + { + LogFlow(("Host version: %s\n", *ppszHostVersion)); + + /* Get last checked host version */ + char *pszLastCheckedHostVersion; + rc = VbglR3HostVersionLastCheckedLoad(idClient, &pszLastCheckedHostVersion); + if (RT_SUCCESS(rc)) + { + LogFlow(("Last checked host version: %s\n", pszLastCheckedHostVersion)); + if (strcmp(*ppszHostVersion, pszLastCheckedHostVersion) == 0) + *pfUpdate = false; /* We already notified this version, skip */ + VbglR3GuestPropReadValueFree(pszLastCheckedHostVersion); + } + else if (rc == VERR_NOT_FOUND) /* Never wrote a last checked host version before */ + { + LogFlow(("Never checked a host version before.\n")); + rc = VINF_SUCCESS; + } + } + + /* Look up guest version */ + if (RT_SUCCESS(rc)) + { + rc = VbglR3GetAdditionsVersion(ppszGuestVersion, NULL /* Extended version not needed here */, + NULL /* Revision not needed here */); + if (RT_FAILURE(rc)) + LogFlow(("Could not read VBox guest version! rc = %Rrc\n", rc)); + } + } + + /* Do the actual version comparison (if needed, see block(s) above) */ + if (RT_SUCCESS(rc) && *pfUpdate) + { + if (RTStrVersionCompare(*ppszHostVersion, *ppszGuestVersion) > 0) /* Is host version greater than guest add version? */ + { + /* Yay, we have an update! */ + LogRel(("Guest Additions update found! Please upgrade this machine to the latest Guest Additions.\n")); + } + else + { + /* How sad ... */ + *pfUpdate = false; + } + } + + /* Cleanup on failure */ + if (RT_FAILURE(rc)) + { + if (*ppszHostVersion) + { + VbglR3GuestPropReadValueFree(*ppszHostVersion); + *ppszHostVersion = NULL; + } + if (*ppszGuestVersion) + { + VbglR3GuestPropReadValueFree(*ppszGuestVersion); + *ppszGuestVersion = NULL; + } + } + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, pfUpdate, ppszHostVersion, ppszGuestVersion); + return VERR_NOT_SUPPORTED; +#endif +} + + +/** Retrieves the last checked host version. + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param ppszVer Receives pointer of allocated version string. + * The returned pointer must be freed using RTStrFree() on VINF_SUCCESS. + */ +VBGLR3DECL(int) VbglR3HostVersionLastCheckedLoad(HGCMCLIENTID idClient, char **ppszVer) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(ppszVer); + return VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", ppszVer); +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, ppszVer); + return VERR_NOT_SUPPORTED; +#endif +} + + +/** Stores the last checked host version for later lookup. + * Requires strings in form of "majorVer.minorVer.build". + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param pszVer Pointer to version string to store. + */ +VBGLR3DECL(int) VbglR3HostVersionLastCheckedStore(HGCMCLIENTID idClient, const char *pszVer) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(pszVer); + return VbglR3GuestPropWriteValue(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", pszVer); +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, pszVer); + return VERR_NOT_SUPPORTED; +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h new file mode 100644 index 00000000..08e1d57f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h @@ -0,0 +1,129 @@ +/* $Id: VBoxGuestR3LibInternal.h $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 support library for the guest additions, Internal header. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h +#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/VMMDev.h> +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +typedef unsigned long xf86size_t; +extern "C" xf86size_t xf86strlen(const char*); +# undef strlen +# define strlen xf86strlen +#endif /* VBOX_VBGLR3_XFREE86 */ + +RT_C_DECLS_BEGIN + +int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq); +int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq); +int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType); +int vbglR3GRPerform(VMMDevRequestHeader *pReq); +void vbglR3GRFree(VMMDevRequestHeader *pReq); + + + +DECLINLINE(void) VbglHGCMParmUInt32Set(HGCMFunctionParameter *pParm, uint32_t u32) +{ + pParm->type = VMMDevHGCMParmType_32bit; + pParm->u.value64 = 0; /* init unused bits to 0 */ + pParm->u.value32 = u32; +} + + +DECLINLINE(int) VbglHGCMParmUInt32Get(HGCMFunctionParameter *pParm, uint32_t *pu32) +{ + if (pParm->type == VMMDevHGCMParmType_32bit) + { + *pu32 = pParm->u.value32; + return VINF_SUCCESS; + } + return VERR_INVALID_PARAMETER; +} + + +DECLINLINE(void) VbglHGCMParmUInt64Set(HGCMFunctionParameter *pParm, uint64_t u64) +{ + pParm->type = VMMDevHGCMParmType_64bit; + pParm->u.value64 = u64; +} + + +DECLINLINE(int) VbglHGCMParmUInt64Get(HGCMFunctionParameter *pParm, uint64_t *pu64) +{ + if (pParm->type == VMMDevHGCMParmType_64bit) + { + *pu64 = pParm->u.value64; + return VINF_SUCCESS; + } + return VERR_INVALID_PARAMETER; +} + + +DECLINLINE(void) VbglHGCMParmPtrSet(HGCMFunctionParameter *pParm, void *pv, uint32_t cb) +{ + pParm->type = VMMDevHGCMParmType_LinAddr; + pParm->u.Pointer.size = cb; + pParm->u.Pointer.u.linearAddr = (uintptr_t)pv; +} + + +#ifdef IPRT_INCLUDED_string_h + +DECLINLINE(void) VbglHGCMParmPtrSetString(HGCMFunctionParameter *pParm, const char *psz) +{ + pParm->type = VMMDevHGCMParmType_LinAddr_In; + pParm->u.Pointer.size = (uint32_t)strlen(psz) + 1; + pParm->u.Pointer.u.linearAddr = (uintptr_t)psz; +} + +#endif /* IPRT_INCLUDED_string_h */ + +#ifdef VBOX_VBGLR3_XFREE86 +# undef strlen +#endif /* VBOX_VBGLR3_XFREE86 */ + +RT_C_DECLS_END + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp new file mode 100644 index 00000000..ef4eee8f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp @@ -0,0 +1,94 @@ +/* $Id: VBoxGuestR3LibLog.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Logging. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Write to the backdoor logger from ring 3 guest code. + * + * @returns IPRT status code. + * + * @param pch The string to log. Does not need to be terminated. + * @param cch The number of chars (bytes) to log. + * + * @remarks This currently does not accept more than 255 bytes of data at + * one time. It should probably be rewritten to use pass a pointer + * in the IOCtl. + */ +VBGLR3DECL(int) VbglR3WriteLog(const char *pch, size_t cch) +{ + /* + * Quietly skip empty strings. + * (Happens in the RTLogBackdoorPrintf case.) + */ + int rc; + if (cch > 0) + { + if (RT_VALID_PTR(pch)) + { + /* + * We need to repackage the string for ring-0. + */ + size_t cbMsg = VBGL_IOCTL_LOG_SIZE(cch); + PVBGLIOCLOG pMsg = (PVBGLIOCLOG)RTMemTmpAlloc(cbMsg); + if (pMsg) + { + VBGLREQHDR_INIT_EX(&pMsg->Hdr, VBGL_IOCTL_LOG_SIZE_IN(cch), VBGL_IOCTL_LOG_SIZE_OUT); + memcpy(pMsg->u.In.szMsg, pch, cch); + pMsg->u.In.szMsg[cch] = '\0'; + rc = vbglR3DoIOCtl(VBGL_IOCTL_LOG(cch), &pMsg->Hdr, cbMsg); + + RTMemTmpFree(pMsg); + } + else + rc = VERR_NO_TMP_MEMORY; + } + else + rc = VERR_INVALID_POINTER; + } + else + rc = VINF_SUCCESS; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp new file mode 100644 index 00000000..5d334e14 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp @@ -0,0 +1,135 @@ +/* $Id: VBoxGuestR3LibMisc.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Misc. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/log.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Change the IRQ filter mask. + * + * @returns IPRT status code. + * @param fOr The OR mask. + * @param fNot The NOT mask. + */ +VBGLR3DECL(int) VbglR3CtlFilterMask(uint32_t fOr, uint32_t fNot) +{ + VBGLIOCCHANGEFILTERMASK Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_FILTER_MASK); + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_FILTER_MASK, &Info.Hdr, sizeof(Info)); +} + + +/** + * Report a change in the capabilities that we support to the host. + * + * @returns IPRT status code. + * @param fOr Capabilities which have been added. + * @param fNot Capabilities which have been removed. + * + * @todo Move to a different file. + */ +VBGLR3DECL(int) VbglR3SetGuestCaps(uint32_t fOr, uint32_t fNot) +{ + VBGLIOCSETGUESTCAPS Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_GUEST_CAPABILITIES); + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info)); +} + + +/** + * Acquire capabilities to report to the host. + * + * The capabilities which can be acquired are the same as those reported by + * VbglR3SetGuestCaps, and once a capability has been acquired once is is + * switched to "acquire mode" and can no longer be set using VbglR3SetGuestCaps. + * Capabilities can also be switched to acquire mode without actually being + * acquired. A client can not acquire a capability which has been acquired and + * not released by another client. Capabilities acquired are automatically + * released on session termination. + * + * @returns IPRT status code + * @returns VERR_RESOURCE_BUSY and acquires nothing if another client has + * acquired and not released at least one of the @a fOr capabilities + * @param fOr Capabilities to acquire or to switch to acquire mode + * @param fNot Capabilities to release + * @param fConfig if set, capabilities in @a fOr are switched to acquire mode + * but not acquired, and @a fNot is ignored. See + * VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE for details. + */ +VBGLR3DECL(int) VbglR3AcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fConfig) +{ + VBGLIOCACQUIREGUESTCAPS Info; + VBGLREQHDR_INIT(&Info.Hdr, ACQUIRE_GUEST_CAPABILITIES); + Info.u.In.fFlags = fConfig ? VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE : VBGL_IOC_AGC_FLAGS_DEFAULT; + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info)); +} + + +/** + * Query the session ID of this VM. + * + * The session id is an unique identifier that gets changed for each VM start, + * reset or restore. Useful for detection a VM restore. + * + * @returns IPRT status code. + * @param pu64IdSession Session id (out). This is NOT changed on + * failure, so the caller can depend on this to + * deal with backward compatibility (see + * VBoxServiceVMInfoWorker() for an example.) + */ +VBGLR3DECL(int) VbglR3GetSessionId(uint64_t *pu64IdSession) +{ + VMMDevReqSessionId Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetSessionId); + Req.idSession = 0; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + *pu64IdSession = Req.idSession; + + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp new file mode 100644 index 00000000..04ff7674 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp @@ -0,0 +1,180 @@ +/* $Id: VBoxGuestR3LibModule.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared modules. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" +#include <iprt/mem.h> +#include <iprt/string.h> + +/** + * Registers a new shared module for the VM + * + * @returns IPRT status code. + * @param pszModuleName Module name + * @param pszVersion Module version + * @param GCBaseAddr Module base address + * @param cbModule Module size + * @param cRegions Number of shared region descriptors + * @param pRegions Shared region(s) + */ +VBGLR3DECL(int) VbglR3RegisterSharedModule(char *pszModuleName, char *pszVersion, + RTGCPTR64 GCBaseAddr, uint32_t cbModule, + unsigned cRegions, VMMDEVSHAREDREGIONDESC *pRegions) +{ + VMMDevSharedModuleRegistrationRequest *pReq; + int rc; + + /* Sanity check. */ + AssertReturn(cRegions < VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER); + + pReq = (VMMDevSharedModuleRegistrationRequest *)RTMemAllocZ(RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest, + aRegions[cRegions])); + AssertReturn(pReq, VERR_NO_MEMORY); + + vmmdevInitRequest(&pReq->header, VMMDevReq_RegisterSharedModule); + pReq->header.size = RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest, aRegions[cRegions]); + pReq->GCBaseAddr = GCBaseAddr; + pReq->cbModule = cbModule; + pReq->cRegions = cRegions; +#ifdef RT_OS_WINDOWS +# if ARCH_BITS == 32 + pReq->enmGuestOS = VBOXOSFAMILY_Windows32; +# else + pReq->enmGuestOS = VBOXOSFAMILY_Windows64; +# endif +#else + /** @todo */ + pReq->enmGuestOS = VBOXOSFAMILY_Unknown; +#endif + for (unsigned i = 0; i < cRegions; i++) + pReq->aRegions[i] = pRegions[i]; + + if ( RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName) != VINF_SUCCESS + || RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion) != VINF_SUCCESS) + { + rc = VERR_BUFFER_OVERFLOW; + goto end; + } + + rc = vbglR3GRPerform(&pReq->header); + +end: + RTMemFree(pReq); + return rc; + +} + +/** + * Unregisters a shared module for the VM + * + * @returns IPRT status code. + * @param pszModuleName Module name + * @param pszVersion Module version + * @param GCBaseAddr Module base address + * @param cbModule Module size + */ +VBGLR3DECL(int) VbglR3UnregisterSharedModule(char *pszModuleName, char *pszVersion, RTGCPTR64 GCBaseAddr, uint32_t cbModule) +{ + VMMDevSharedModuleUnregistrationRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_UnregisterSharedModule); + Req.GCBaseAddr = GCBaseAddr; + Req.cbModule = cbModule; + + if ( RTStrCopy(Req.szName, sizeof(Req.szName), pszModuleName) != VINF_SUCCESS + || RTStrCopy(Req.szVersion, sizeof(Req.szVersion), pszVersion) != VINF_SUCCESS) + { + return VERR_BUFFER_OVERFLOW; + } + return vbglR3GRPerform(&Req.header); +} + +/** + * Checks registered modules for shared pages + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3CheckSharedModules() +{ + VMMDevSharedModuleCheckRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_CheckSharedModules); + return vbglR3GRPerform(&Req.header); +} + +/** + * Checks if page sharing is enabled. + * + * @returns true/false enabled/disabled + */ +VBGLR3DECL(bool) VbglR3PageSharingIsEnabled() +{ + VMMDevPageSharingStatusRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetPageSharingStatus); + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + return Req.fEnabled; + return false; +} + +/** + * Checks if page sharing is enabled. + * + * @returns true/false enabled/disabled + */ +VBGLR3DECL(int) VbglR3PageIsShared(RTGCPTR pPage, bool *pfShared, uint64_t *puPageFlags) +{ +#ifdef DEBUG + VMMDevPageIsSharedRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_DebugIsPageShared); + Req.GCPtrPage = pPage; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pfShared = Req.fShared; + *puPageFlags = Req.uPageFlags; + } + return rc; +#else + RT_NOREF3(pPage, pfShared, puPageFlags); + return VERR_NOT_IMPLEMENTED; +#endif +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp new file mode 100644 index 00000000..87a08f66 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp @@ -0,0 +1,90 @@ +/* $Id: VBoxGuestR3LibMouse.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Mouse. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + + +/** + * Retrieve mouse coordinates and features from the host. + * + * @returns VBox status code. + * + * @param pfFeatures Where to store the mouse features. + * @param px Where to store the X co-ordinate. + * @param py Where to store the Y co-ordinate. + */ +VBGLR3DECL(int) VbglR3GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py) +{ + VMMDevReqMouseStatus Req; + vmmdevInitRequest(&Req.header, VMMDevReq_GetMouseStatus); + Req.mouseFeatures = 0; + Req.pointerXPos = 0; + Req.pointerYPos = 0; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + if (pfFeatures) + *pfFeatures = Req.mouseFeatures; + if (px) + *px = Req.pointerXPos; + if (py) + *py = Req.pointerYPos; + } + return rc; +} + + +/** + * Send mouse features to the host. + * + * @returns VBox status code. + * + * @param fFeatures Supported mouse pointer features. The main guest driver + * will mediate different callers and show the host any + * feature enabled by any guest caller. + */ +VBGLR3DECL(int) VbglR3SetMouseStatus(uint32_t fFeatures) +{ + VBGLIOCSETMOUSESTATUS Req; + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fFeatures; + return vbglR3DoIOCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp new file mode 100644 index 00000000..4ab654a8 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp @@ -0,0 +1,118 @@ +/** $Id: VBoxGuestR3LibPidFile.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * Create a PID file. + */ + +/* + * Copyright (C) 2015-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/file.h> +#include <iprt/string.h> +#include <iprt/process.h> +#include "VBoxGuestR3LibInternal.h" + +/** + * Creates a PID File and returns the open file descriptor. + * + * On DOS based system, file sharing (deny write) is used for locking the PID + * file. + * + * On Unix-y systems, an exclusive advisory lock is used for locking the PID + * file since the file sharing support is usually missing there. + * + * This API will overwrite any existing PID Files without a lock on them, on the + * assumption that they are stale files which an old process did not properly + * clean up. + * + * @returns IPRT status code. + * @param pszPath The path and filename to create the PID File under + * @param phFile Where to store the file descriptor of the open (and locked + * on Unix-y systems) PID File. On failure, or if another + * process owns the PID File, this will be set to NIL_RTFILE. + */ +VBGLR3DECL(int) VbglR3PidFile(const char *pszPath, PRTFILE phFile) +{ + AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(phFile, VERR_INVALID_PARAMETER); + *phFile = NIL_RTFILE; + + RTFILE hPidFile; + int rc = RTFileOpen(&hPidFile, pszPath, + RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE + | (0644 << RTFILE_O_CREATE_MODE_SHIFT)); + if (RT_SUCCESS(rc)) + { +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + /** @todo using size 0 for locking means lock all on Posix. + * We should adopt this as our convention too, or something + * similar. */ + rc = RTFileLock(hPidFile, RTFILE_LOCK_WRITE, 0, 0); + if (RT_FAILURE(rc)) + RTFileClose(hPidFile); + else +#endif + { + char szBuf[256]; + size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", + RTProcSelf()); + RTFileWrite(hPidFile, szBuf, cbPid, NULL); + *phFile = hPidFile; + } + } + return rc; +} + + +/** + * Close and remove an open PID File. + * + * @param pszPath The path to the PID File, + * @param hFile The handle for the file. NIL_RTFILE is ignored as usual. + */ +VBGLR3DECL(void) VbglR3ClosePidFile(const char *pszPath, RTFILE hFile) +{ + AssertPtrReturnVoid(pszPath); + if (hFile != NIL_RTFILE) + { +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + RTFileWriteAt(hFile, 0, "-1", 2, NULL); +#else + RTFileDelete(pszPath); +#endif + RTFileClose(hFile); + } +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp new file mode 100644 index 00000000..15ba7a66 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp @@ -0,0 +1,108 @@ +/* $Id: VBoxGuestR3LibRuntimeXF86.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * implements the minimum of runtime functions needed for + * XFree86 driver code. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#if defined(VBOX_VBGLR3_XFREE86) +extern "C" { +# define XFree86LOADER +# include <xf86_ansic.h> +# undef size_t +} +#else +# include <stdarg.h> +# include <stdlib.h> +# define xalloc malloc +# define xfree free +extern "C" void ErrorF(const char *f, ...); +#endif + +RTDECL(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + ErrorF("Assertion failed! Expression: %s at %s in\n", pszExpr, + pszFunction); + ErrorF("%s:%u\n", pszFile, uLine); +} + +RTDECL(void) RTAssertMsg2Weak(const char *pszFormat, ...) +{ + NOREF(pszFormat); +} + +RTDECL(bool) RTAssertShouldPanic(void) +{ + return false; +} + +RTDECL(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup) +{ + NOREF(fFlagsAndGroup); + return NULL; +} + +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstance(void) +{ + return NULL; +} + +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstanceEx(uint32_t fFlagsAndGroup) +{ + NOREF(fFlagsAndGroup); + return NULL; +} + +RTDECL(void) RTLogLoggerEx(PRTLOGGER, unsigned, unsigned, const char *pszFormat, ...) +{ + NOREF(pszFormat); +} + +RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) +{ + NOREF(pszTag); + return xalloc(cb); +} + +RTDECL(void) RTMemTmpFree(void *pv) +{ + xfree(pv); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp new file mode 100644 index 00000000..533832aa --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp @@ -0,0 +1,209 @@ +/* $Id: VBoxGuestR3LibSeamless.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Seamless mode. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/string.h> + +#include <VBox/log.h> + +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" void* xf86memcpy(void*,const void*,xf86size_t); +# undef memcpy +# define memcpy xf86memcpy +#endif /* VBOX_VBGLR3_XFREE86 */ + +/** + * Tell the host that we support (or no longer support) seamless mode. + * + * @returns IPRT status value + * @param fState whether or not we support seamless mode + */ +VBGLR3DECL(int) VbglR3SeamlessSetCap(bool fState) +{ + if (fState) + return VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS, 0); + return VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS); +} + +/** + * Wait for a seamless mode change event. + * + * @returns IPRT status value. + * @param[out] pMode On success, the seamless mode to switch into (i.e. + * disabled, visible region or host window). + */ +VBGLR3DECL(int) VbglR3SeamlessWaitEvent(VMMDevSeamlessMode *pMode) +{ + AssertPtrReturn(pMode, VERR_INVALID_POINTER); + + /** @todo r=andy The (similar / duplicate) Windows code does similar waiting. Merge / fix this. */ + uint32_t fEvent = 0; + int rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 1000 /* ms */, &fEvent); + if (RT_SUCCESS(rc)) + { + /* did we get the right event? */ + if (fEvent & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST) + { + VMMDevSeamlessChangeRequest seamlessChangeRequest; + + /* get the seamless change request */ + vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest); + seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1; + seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + rc = vbglR3GRPerform(&seamlessChangeRequest.header); + if (RT_SUCCESS(rc)) + { + *pMode = seamlessChangeRequest.mode; + return VINF_SUCCESS; + } + } + else + rc = VERR_TRY_AGAIN; + } + else if ( rc == VERR_INTERRUPTED + || rc == VERR_TIMEOUT /* just in case */) + rc = VERR_TRY_AGAIN; + return rc; +} + +/** + * Request the last seamless mode switch from the host again. + * + * @returns IPRT status value. + * @param[out] pMode On success, the seamless mode that was switched + * into (i.e. disabled, visible region or host window). + */ +VBGLR3DECL(int) VbglR3SeamlessGetLastEvent(VMMDevSeamlessMode *pMode) +{ + VMMDevSeamlessChangeRequest seamlessChangeRequest; + int rc; + + AssertPtrReturn(pMode, VERR_INVALID_PARAMETER); + + /* get the seamless change request */ + vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest); + seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1; + seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + rc = vbglR3GRPerform(&seamlessChangeRequest.header); + if (RT_SUCCESS(rc)) + { + *pMode = seamlessChangeRequest.mode; + return VINF_SUCCESS; + } + return rc; +} + +/** + * Inform the host about the visible region + * + * @returns IPRT status code + * @param cRects number of rectangles in the list of visible rectangles + * @param pRects list of visible rectangles on the guest display + * + * @todo A scatter-gather version of vbglR3GRPerform would be nice, so that we don't have + * to copy our rectangle and header data into a single structure and perform an + * additional allocation. + * @todo Would that really gain us much, given that the rectangles may not + * be grouped at all, or in the format we need? Keeping the memory + * for our "single structure" around (re-alloc-ing it if necessary) + * sounds like a simpler optimisation if we need it. + */ +VBGLR3DECL(int) VbglR3SeamlessSendRects(uint32_t cRects, PRTRECT pRects) +{ + VMMDevVideoSetVisibleRegion *pReq; + int rc; + + AssertReturn(pRects || cRects == 0, VERR_INVALID_PARAMETER); + AssertMsgReturn(cRects <= _1M, ("%u\n", cRects), VERR_OUT_OF_RANGE); + + rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, + sizeof(VMMDevVideoSetVisibleRegion) + + cRects * sizeof(RTRECT) + - sizeof(RTRECT), + VMMDevReq_VideoSetVisibleRegion); + if (RT_SUCCESS(rc)) + { + pReq->cRect = cRects; + if (cRects) + memcpy(&pReq->Rect, pRects, cRects * sizeof(RTRECT)); + /* This will fail harmlessly for cRect == 0 and older host code */ + rc = vbglR3GRPerform(&pReq->header); + LogFunc(("Visible region request returned %Rrc, internal %Rrc.\n", + rc, pReq->header.rc)); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + LogFunc(("Sending %u rectangles to the host: %Rrc\n", cRects, rc)); + return rc; +} + +VBGLR3DECL(int) VbglR3SeamlessSendMonitorPositions(uint32_t cPositions, PRTPOINT pPositions) +{ + if (!pPositions || cPositions <= 0) + return VERR_INVALID_PARAMETER; + + VMMDevVideoUpdateMonitorPositions *pReq; + int rc; + + rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, + sizeof(VMMDevVideoUpdateMonitorPositions) + + (cPositions - 1) * sizeof(RTPOINT), + VMMDevReq_VideoUpdateMonitorPositions); + if (RT_SUCCESS(rc)) + { + pReq->cPositions = cPositions; + if (cPositions) + memcpy(&pReq->aPositions, pPositions, cPositions * sizeof(RTPOINT)); + rc = vbglR3GRPerform(&pReq->header); + LogFunc(("Monitor position update request returned %Rrc, internal %Rrc.\n", + rc, pReq->header.rc)); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + LogFunc(("Sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc)); + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp new file mode 100644 index 00000000..384d2a9f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp @@ -0,0 +1,432 @@ +/* $Id: VBoxGuestR3LibSharedFolders.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, shared folders. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/cpp/autores.h> +#include <iprt/stdarg.h> +#include <VBox/log.h> +#include <VBox/shflsvc.h> /** @todo File should be moved to VBox/HostServices/SharedFolderSvc.h */ + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Connects to the shared folder service. + * + * @returns VBox status code + * @param pidClient Where to put the client id on success. The client id + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3SharedFolderConnect(HGCMCLIENTID *pidClient) +{ + return VbglR3HGCMConnect("VBoxSharedFolders", pidClient); +} + + +/** + * Disconnect from the shared folder service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + */ +VBGLR3DECL(int) VbglR3SharedFolderDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Checks whether a shared folder share exists or not. + * + * @returns True if shared folder exists, false if not. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param pszShareName Shared folder name to check. + */ +VBGLR3DECL(bool) VbglR3SharedFolderExists(HGCMCLIENTID idClient, const char *pszShareName) +{ + AssertPtr(pszShareName); + + uint32_t cMappings; + VBGLR3SHAREDFOLDERMAPPING *paMappings; + + /** @todo Use some caching here? */ + bool fFound = false; + int rc = VbglR3SharedFolderGetMappings(idClient, true /* Only process auto-mounted folders */, &paMappings, &cMappings); + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < cMappings && !fFound; i++) + { + char *pszName = NULL; + rc = VbglR3SharedFolderGetName(idClient, paMappings[i].u32Root, &pszName); + if ( RT_SUCCESS(rc) + && *pszName) + { + if (RTStrICmp(pszName, pszShareName) == 0) + fFound = true; + RTStrFree(pszName); + } + } + VbglR3SharedFolderFreeMappings(paMappings); + } + return fFound; +} + + +/** + * Get the list of available shared folders. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3SharedFolderConnect(). + * @param fAutoMountOnly Flag whether only auto-mounted shared folders + * should be reported. + * @param ppaMappings Allocated array which will retrieve the mapping info. Needs + * to be freed with VbglR3SharedFolderFreeMappings() later. + * @param pcMappings The number of mappings returned in @a ppaMappings. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMappings(HGCMCLIENTID idClient, bool fAutoMountOnly, + PVBGLR3SHAREDFOLDERMAPPING *ppaMappings, uint32_t *pcMappings) +{ + AssertPtrReturn(pcMappings, VERR_INVALID_PARAMETER); + AssertPtrReturn(ppaMappings, VERR_INVALID_PARAMETER); + + *pcMappings = 0; + *ppaMappings = NULL; + + VBoxSFQueryMappings Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAPPINGS, 3); + + /* Set the mapping flags. */ + uint32_t u32Flags = 0; /** @todo SHFL_MF_UTF8 is not implemented yet. */ + if (fAutoMountOnly) /* We only want the mappings which get auto-mounted. */ + u32Flags |= SHFL_MF_AUTOMOUNT; + VbglHGCMParmUInt32Set(&Msg.flags, u32Flags); + + /* + * Prepare and get the actual mappings from the host service. + */ + int rc = VINF_SUCCESS; + uint32_t cMappings = 8; /* Should be a good default value. */ + uint32_t cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING); + VBGLR3SHAREDFOLDERMAPPING *ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)RTMemAllocZ(cbSize); + if (!ppaMappingsTemp) + return VERR_NO_MEMORY; + + do + { + VbglHGCMParmUInt32Set(&Msg.numberOfMappings, cMappings); + VbglHGCMParmPtrSet(&Msg.mappings, ppaMappingsTemp, cbSize); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + VbglHGCMParmUInt32Get(&Msg.numberOfMappings, pcMappings); + + /* Do we have more mappings than we have allocated space for? */ + if (rc == VINF_BUFFER_OVERFLOW) + { + cMappings = *pcMappings; + cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING); + void *pvNew = RTMemRealloc(ppaMappingsTemp, cbSize); + AssertPtrBreakStmt(pvNew, rc = VERR_NO_MEMORY); + ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)pvNew; + } + } + } while (rc == VINF_BUFFER_OVERFLOW); /** @todo r=bird: This won't happen because the weird host code never returns it. */ + + if ( RT_FAILURE(rc) + || !*pcMappings) + { + RTMemFree(ppaMappingsTemp); + ppaMappingsTemp = NULL; + } + + /* In this case, just return success with 0 mappings */ + if ( rc == VERR_INVALID_PARAMETER + && fAutoMountOnly) + rc = VINF_SUCCESS; + + *ppaMappings = ppaMappingsTemp; + + return rc; +} + + +/** + * Frees the shared folder mappings allocated by + * VbglR3SharedFolderGetMappings() before. + * + * @param paMappings What + */ +VBGLR3DECL(void) VbglR3SharedFolderFreeMappings(PVBGLR3SHAREDFOLDERMAPPING paMappings) +{ + if (paMappings) + RTMemFree(paMappings); +} + + +/** + * Get the real name of a shared folder. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param u32Root Root ID of shared folder to get the name for. + * @param ppszName Where to return the name string. This shall be + * freed by calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetName(HGCMCLIENTID idClient, uint32_t u32Root, char **ppszName) +{ + AssertPtr(ppszName); + + VBoxSFQueryMapName Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_NAME, 2); + + int rc; + uint32_t cbString = SHFLSTRING_HEADER_SIZE + SHFL_MAX_LEN * sizeof(RTUTF16); + PSHFLSTRING pString = (PSHFLSTRING)RTMemAlloc(cbString); + if (pString) + { + if (!ShflStringInitBuffer(pString, cbString)) + { + RTMemFree(pString); + return VERR_INVALID_PARAMETER; + } + + VbglHGCMParmUInt32Set(&Msg.root, u32Root); + VbglHGCMParmPtrSet(&Msg.name, pString, cbString); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + *ppszName = NULL; + rc = RTUtf16ToUtf8(&pString->String.ucs2[0], ppszName); + } + RTMemFree(pString); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Queries information about a shared folder. + * + * @returns VBox status code. + * + * @param idClient The client ID. + * @param idRoot The root ID of the folder to query information for. + * @param fQueryFlags SHFL_MIQF_XXX. + * @param ppszName Where to return the pointer to the name. + * Free using RTStrFree. Optional. + * @param ppszMountPoint Where to return the pointer to the auto mount point. + * Free using RTStrFree. Optional. + * @param pfFlags Where to return the flags (SHFL_MIF_XXX). Optional. + * @param puRootIdVersion where to return the root ID version. Optional. + * This helps detecting root-id reuse. + * + * @remarks ASSUMES UTF-16 connection to host. + */ +VBGLR3DECL(int) VbglR3SharedFolderQueryFolderInfo(HGCMCLIENTID idClient, uint32_t idRoot, uint64_t fQueryFlags, + char **ppszName, char **ppszMountPoint, + uint64_t *pfFlags, uint32_t *puRootIdVersion) +{ + AssertReturn(!(fQueryFlags & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH)), VERR_INVALID_FLAGS); + + /* + * Allocate string buffers first. + */ + int rc; + PSHFLSTRING pNameBuf = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16)); + PSHFLSTRING pMountPoint = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16)); + if (pNameBuf && pMountPoint) + { + ShflStringInitBuffer(pNameBuf, SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16)); + ShflStringInitBuffer(pMountPoint, SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16)); + + /* + * Make the call. + */ + VBoxSFQueryMapInfo Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_INFO, 5); + VbglHGCMParmUInt32Set(&Msg.root, idRoot); + VbglHGCMParmPtrSet(&Msg.name, pNameBuf, SHFLSTRING_HEADER_SIZE + pNameBuf->u16Size); + VbglHGCMParmPtrSet(&Msg.mountPoint, pMountPoint, SHFLSTRING_HEADER_SIZE + pMountPoint->u16Size); + VbglHGCMParmUInt64Set(&Msg.flags, fQueryFlags); + VbglHGCMParmUInt32Set(&Msg.rootIdVersion, 0); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* + * Copy out the results. + */ + if (puRootIdVersion) + *puRootIdVersion = Msg.rootIdVersion.u.value64; + + if (pfFlags) + *pfFlags = Msg.flags.u.value64; + + if (ppszName) + { + *ppszName = NULL; + rc = RTUtf16ToUtf8Ex(pNameBuf->String.utf16, pNameBuf->u16Length / sizeof(RTUTF16), ppszName, 0, NULL); + } + + if (ppszMountPoint && RT_SUCCESS(rc)) + { + *ppszMountPoint = NULL; + rc = RTUtf16ToUtf8Ex(pMountPoint->String.utf16, pMountPoint->u16Length / sizeof(RTUTF16), ppszMountPoint, 0, NULL); + if (RT_FAILURE(rc) && ppszName) + { + RTStrFree(*ppszName); + *ppszName = NULL; + } + } + } + } + else + rc = VERR_NO_MEMORY; + RTMemFree(pMountPoint); + RTMemFree(pNameBuf); + return rc; +} + + +/** + * Waits for changes to the mappings (add, remove, restore). + * + * @returns VBox status code. + * @retval VINF_SUCCESS on change + * @retval VINF_TRY_AGAIN on restore. + * @retval VERR_OUT_OF_RESOURCES if there are too many guys waiting. + * + * @param idClient The client ID. + * @param uPrevVersion The mappings config version number returned the last + * time around. Use UINT32_MAX for the first call. + * @param puCurVersion Where to return the current mappings config version. + */ +VBGLR3DECL(int) VbglR3SharedFolderWaitForMappingsChanges(HGCMCLIENTID idClient, uint32_t uPrevVersion, uint32_t *puCurVersion) +{ + VBoxSFWaitForMappingsChanges Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES, 1); + VbglHGCMParmUInt32Set(&Msg.version, uPrevVersion); + + int rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + + *puCurVersion = Msg.version.u.value32; + return rc; +} + + +/** + * Cancels all threads currently waiting for changes for this client. + * + * @returns VBox status code. + * @param idClient The client ID. + */ +VBGLR3DECL(int) VbglR3SharedFolderCancelMappingsChangesWaits(HGCMCLIENTID idClient) +{ + VBGLIOCHGCMCALL CallInfo; + VBGL_HGCM_HDR_INIT(&CallInfo, idClient, SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS, 0); + + return VbglR3HGCMCall(&CallInfo, sizeof(CallInfo)); +} + + +/** + * Retrieves the prefix for a shared folder mount point. If no prefix + * is set in the guest properties "sf_" is returned. + * + * @returns VBox status code. + * @param ppszPrefix Where to return the prefix string. This shall be + * freed by calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMountPrefix(char **ppszPrefix) +{ + AssertPtrReturn(ppszPrefix, VERR_INVALID_POINTER); + int rc; +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClientGuestProp; + rc = VbglR3GuestPropConnect(&idClientGuestProp); + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountPrefix", ppszPrefix); + if (rc == VERR_NOT_FOUND) /* No prefix set? Then set the default. */ + { +#endif +/** @todo r=bird: Inconsistent! VbglR3SharedFolderGetMountDir does not return a default. */ + rc = RTStrDupEx(ppszPrefix, "sf_"); +#ifdef VBOX_WITH_GUEST_PROPS + } + VbglR3GuestPropDisconnect(idClientGuestProp); + } +#endif + return rc; +} + + +/** + * Retrieves the mount root directory for auto-mounted shared + * folders. mount point. If no string is set (VERR_NOT_FOUND) + * it's up on the caller (guest) to decide where to mount. + * + * @returns VBox status code. + * @param ppszDir Where to return the directory + * string. This shall be freed by + * calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMountDir(char **ppszDir) +{ + AssertPtrReturn(ppszDir, VERR_INVALID_POINTER); + int rc = VERR_NOT_FOUND; +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClientGuestProp; + rc = VbglR3GuestPropConnect(&idClientGuestProp); + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountDir", ppszDir); + VbglR3GuestPropDisconnect(idClientGuestProp); + } +#endif + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp new file mode 100644 index 00000000..721465b6 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp @@ -0,0 +1,79 @@ +/* $Id: VBoxGuestR3LibStat.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Statistics. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + + +/** + * Query the current statistics update interval. + * + * @returns IPRT status code. + * @param pcMsInterval Update interval in ms (out). + */ +VBGLR3DECL(int) VbglR3StatQueryInterval(PRTMSINTERVAL pcMsInterval) +{ + VMMDevGetStatisticsChangeRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetStatisticsChangeRequest); + Req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST; + Req.u32StatInterval = 1; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pcMsInterval = Req.u32StatInterval * 1000; + if (*pcMsInterval / 1000 != Req.u32StatInterval) + *pcMsInterval = ~(RTMSINTERVAL)0; + } + return rc; +} + + +/** + * Report guest statistics. + * + * @returns IPRT status code. + * @param pReq Request packet with statistics. + */ +VBGLR3DECL(int) VbglR3StatReport(VMMDevReportGuestStats *pReq) +{ + vmmdevInitRequest(&pReq->header, VMMDevReq_ReportGuestStats); + return vbglR3GRPerform(&pReq->header); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp new file mode 100644 index 00000000..708f6079 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp @@ -0,0 +1,55 @@ +/* $Id: VBoxGuestR3LibTime.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Time. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/time.h> +#include "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3GetHostTime(PRTTIMESPEC pTime) +{ + VMMDevReqHostTime Req; + vmmdevInitRequest(&Req.header, VMMDevReq_GetHostTime); + Req.time = UINT64_MAX; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + RTTimeSpecSetMilli(pTime, (int64_t)Req.time); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp new file mode 100644 index 00000000..44101af7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp @@ -0,0 +1,618 @@ +/* $Id: VBoxGuestR3LibVideo.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Video. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + +#include <VBox/log.h> +#include <VBox/HostServices/GuestPropertySvc.h> /* For Save and RetrieveVideoMode */ +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" void* xf86memcpy(void*,const void*,xf86size_t); +# undef memcpy +# define memcpy xf86memcpy +extern "C" void* xf86memset(const void*,int,xf86size_t); +# undef memset +# define memset xf86memset +#endif /* VBOX_VBGLR3_XFREE86 */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VIDEO_PROP_PREFIX "/VirtualBox/GuestAdd/Vbgl/Video/" + + +/** + * Enable or disable video acceleration. + * + * @returns VBox status code. + * + * @param fEnable Pass zero to disable, any other value to enable. + */ +VBGLR3DECL(int) VbglR3VideoAccelEnable(bool fEnable) +{ + VMMDevVideoAccelEnable Req; + vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelEnable); + Req.u32Enable = fEnable; + Req.cbRingBuffer = VMMDEV_VBVA_RING_BUFFER_SIZE; + Req.fu32Status = 0; + return vbglR3GRPerform(&Req.header); +} + + +/** + * Flush the video buffer. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3VideoAccelFlush(void) +{ + VMMDevVideoAccelFlush Req; + vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelFlush); + return vbglR3GRPerform(&Req.header); +} + + +/** + * Send mouse pointer shape information to the host. + * + * @returns VBox status code. + * + * @param fFlags Mouse pointer flags. + * @param xHot X coordinate of hot spot. + * @param yHot Y coordinate of hot spot. + * @param cx Pointer width. + * @param cy Pointer height. + * @param pvImg Pointer to the image data (can be NULL). + * @param cbImg Size of the image data pointed to by pvImg. + */ +VBGLR3DECL(int) VbglR3SetPointerShape(uint32_t fFlags, uint32_t xHot, uint32_t yHot, uint32_t cx, uint32_t cy, + const void *pvImg, size_t cbImg) +{ + VMMDevReqMousePointer *pReq; + size_t cbReq = vmmdevGetMousePointerReqSize(cx, cy); + AssertReturn( !pvImg + || cbReq == RT_UOFFSETOF(VMMDevReqMousePointer, pointerData) + cbImg, + VERR_INVALID_PARAMETER); + int rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, cbReq, VMMDevReq_SetPointerShape); + if (RT_SUCCESS(rc)) + { + pReq->fFlags = fFlags; + pReq->xHot = xHot; + pReq->yHot = yHot; + pReq->width = cx; + pReq->height = cy; + if (pvImg) + memcpy(pReq->pointerData, pvImg, cbImg); + + rc = vbglR3GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + return rc; +} + + +/** + * Send mouse pointer shape information to the host. + * This version of the function accepts a request for clients that + * already allocate and manipulate the request structure directly. + * + * @returns VBox status code. + * + * @param pReq Pointer to the VMMDevReqMousePointer structure. + */ +VBGLR3DECL(int) VbglR3SetPointerShapeReq(VMMDevReqMousePointer *pReq) +{ + int rc = vbglR3GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param pcx Where to store the horizontal pixel resolution + * @param pcy Where to store the vertical pixel resolution + * requested (a value of zero means do not change). + * @param pcBits Where to store the bits per pixel requested (a value + * of zero means do not change). + * @param piDisplay Where to store the display number the request was for + * - 0 for the primary display, 1 for the first + * secondary display, etc. + * @param fAck whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + * + */ +static int getDisplayChangeRequest2(uint32_t *pcx, uint32_t *pcy, + uint32_t *pcBits, uint32_t *piDisplay, + bool fAck) +{ + VMMDevDisplayChangeRequest2 Req; + + AssertPtrReturn(pcx, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcy, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER); + AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER); + RT_ZERO(Req); + vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequest2); + if (fAck) + Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + rc = Req.header.rc; + if (RT_SUCCESS(rc)) + { + *pcx = Req.xres; + *pcy = Req.yres; + *pcBits = Req.bpp; + *piDisplay = Req.display; + } + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param pcx Where to store the horizontal pixel resolution + * requested (a value of zero means do not change). + * @param pcy Where to store the vertical pixel resolution + * requested (a value of zero means do not change). + * @param pcBits Where to store the bits per pixel requested (a value + * of zero means do not change). + * @param piDisplay Where to store the display number the request was for + * - 0 for the primary display, 1 for the first + * secondary display, etc. + * @param fAck whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + * + * @param pdx New horizontal position of the secondary monitor. + * Optional. + * @param pdy New vertical position of the secondary monitor. + * Optional. + * @param pfEnabled Secondary monitor is enabled or not. Optional. + * @param pfChangeOrigin Whether the mode hint retrieved included + * information about origin/display offset inside the + * frame-buffer. Optional. + * + */ +VBGLR3DECL(int) VbglR3GetDisplayChangeRequest(uint32_t *pcx, uint32_t *pcy, + uint32_t *pcBits, + uint32_t *piDisplay, + uint32_t *pdx, uint32_t *pdy, + bool *pfEnabled, + bool *pfChangeOrigin, + bool fAck) +{ + VMMDevDisplayChangeRequestEx Req; + int rc = VINF_SUCCESS; + + AssertPtrReturn(pcx, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcy, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pdx, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pdy, VERR_INVALID_PARAMETER); + AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfEnabled, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfChangeOrigin, VERR_INVALID_PARAMETER); + + RT_ZERO(Req); + rc = vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequestEx); + AssertRCReturn(rc, rc); + if (fAck) + Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + rc = Req.header.rc; + if (RT_SUCCESS(rc)) + { + *pcx = Req.xres; + *pcy = Req.yres; + *pcBits = Req.bpp; + *piDisplay = Req.display; + if (pdx) + *pdx = Req.cxOrigin; + if (pdy) + *pdy = Req.cyOrigin; + if (pfEnabled) + *pfEnabled = Req.fEnabled; + if (pfChangeOrigin) + *pfChangeOrigin = Req.fChangeOrigin; + return VINF_SUCCESS; + } + + /* NEEDS TESTING: test below with current Additions on VBox 4.1 or older. */ + /** @todo Can we find some standard grep-able string for "NEEDS TESTING"? */ + if (rc == VERR_NOT_IMPLEMENTED) /* Fall back to the old API. */ + { + if (pfEnabled) + *pfEnabled = true; + if (pfChangeOrigin) + *pfChangeOrigin = false; + return getDisplayChangeRequest2(pcx, pcy, pcBits, piDisplay, fAck); + } + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param cDisplaysIn How many elements in the paDisplays array. + * @param pcDisplaysOut How many elements were returned. + * @param paDisplays Display information. + * @param fAck Whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + */ +VBGLR3DECL(int) VbglR3GetDisplayChangeRequestMulti(uint32_t cDisplaysIn, + uint32_t *pcDisplaysOut, + VMMDevDisplayDef *paDisplays, + bool fAck) +{ + VMMDevDisplayChangeRequestMulti *pReq; + size_t cbDisplays; + size_t cbAlloc; + int rc = VINF_SUCCESS; + + AssertReturn(cDisplaysIn > 0 && cDisplaysIn <= 64 /* VBOX_VIDEO_MAX_SCREENS */, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcDisplaysOut, VERR_INVALID_PARAMETER); + AssertPtrReturn(paDisplays, VERR_INVALID_PARAMETER); + + cbDisplays = cDisplaysIn * sizeof(VMMDevDisplayDef); + cbAlloc = RT_UOFFSETOF(VMMDevDisplayChangeRequestMulti, aDisplays) + cbDisplays; + pReq = (VMMDevDisplayChangeRequestMulti *)RTMemTmpAlloc(cbAlloc); + AssertPtrReturn(pReq, VERR_NO_MEMORY); + + memset(pReq, 0, cbAlloc); + rc = vmmdevInitRequest(&pReq->header, VMMDevReq_GetDisplayChangeRequestMulti); + AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc); + + pReq->header.size += (uint32_t)cbDisplays; + pReq->cDisplays = cDisplaysIn; + if (fAck) + pReq->eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + + rc = vbglR3GRPerform(&pReq->header); + AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc); + + rc = pReq->header.rc; + if (RT_SUCCESS(rc)) + { + memcpy(paDisplays, pReq->aDisplays, pReq->cDisplays * sizeof(VMMDevDisplayDef)); + *pcDisplaysOut = pReq->cDisplays; + } + + RTMemTmpFree(pReq); + return rc; +} + + +/** + * Query the host as to whether it likes a specific video mode. + * + * @returns the result of the query + * @param cx the width of the mode being queried + * @param cy the height of the mode being queried + * @param cBits the bpp of the mode being queried + */ +VBGLR3DECL(bool) VbglR3HostLikesVideoMode(uint32_t cx, uint32_t cy, uint32_t cBits) +{ + bool fRc = true; /* If for some reason we can't contact the host then + * we like everything. */ + int rc; + VMMDevVideoModeSupportedRequest req; + + vmmdevInitRequest(&req.header, VMMDevReq_VideoModeSupported); + req.width = cx; + req.height = cy; + req.bpp = cBits; + req.fSupported = true; + rc = vbglR3GRPerform(&req.header); + if (RT_SUCCESS(rc) && RT_SUCCESS(req.header.rc)) + fRc = req.fSupported; + return fRc; +} + +/** + * Get the highest screen number for which there is a saved video mode or "0" + * if there are no saved modes. + * + * @returns iprt status value + * @returns VERR_NOT_SUPPORTED if the guest property service is not available. + * @param pcScreen where to store the virtual screen number + */ +VBGLR3DECL(int) VbglR3VideoModeGetHighestSavedScreen(unsigned *pcScreen) +{ +#if defined(VBOX_WITH_GUEST_PROPS) + int rc; + HGCMCLIENTID idClient = 0; + PVBGLR3GUESTPROPENUM pHandle = NULL; + const char *pszName = NULL; + unsigned cHighestScreen = 0; + + /* Validate input. */ + AssertPtrReturn(pcScreen, VERR_INVALID_POINTER); + + /* Query the data. */ + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + const char *pszPattern = VIDEO_PROP_PREFIX"*"; + rc = VbglR3GuestPropEnum(idClient, &pszPattern, 1, &pHandle, &pszName, NULL, NULL, NULL); + int rc2 = VbglR3GuestPropDisconnect(idClient); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc = rc2; + } + + /* Process the data. */ + while (RT_SUCCESS(rc) && pszName != NULL) + { + uint32_t cScreen; + + rc = RTStrToUInt32Full(pszName + sizeof(VIDEO_PROP_PREFIX) - 1, 10, &cScreen); + if (RT_SUCCESS(rc)) /* There may be similar properties with text. */ + cHighestScreen = RT_MAX(cHighestScreen, cScreen); + rc = VbglR3GuestPropEnumNext(pHandle, &pszName, NULL, NULL, NULL); + } + + VbglR3GuestPropEnumFree(pHandle); + + /* Return result. */ + if (RT_SUCCESS(rc)) + *pcScreen = cHighestScreen; + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(pcScreen); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} + +/** + * Save video mode parameters to the guest property store. + * + * @returns iprt status value + * @param idScreen The virtual screen number. + * @param cx mode width + * @param cy mode height + * @param cBits bits per pixel for the mode + * @param x virtual screen X offset + * @param y virtual screen Y offset + * @param fEnabled is this virtual screen enabled? + */ +VBGLR3DECL(int) VbglR3SaveVideoMode(unsigned idScreen, unsigned cx, unsigned cy, unsigned cBits, + unsigned x, unsigned y, bool fEnabled) +{ +#ifdef VBOX_WITH_GUEST_PROPS + unsigned cHighestScreen = 0; + int rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen); + if (RT_SUCCESS(rc)) + { + HGCMCLIENTID idClient = 0; + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + int rc2; + char szModeName[GUEST_PROP_MAX_NAME_LEN]; + char szModeParms[GUEST_PROP_MAX_VALUE_LEN]; + RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen); + RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u,%ux%u,%u", cx, cy, cBits, x, y, (unsigned) fEnabled); + + rc = VbglR3GuestPropWriteValue(idClient, szModeName, szModeParms); + /* Write out the mode using the legacy name too, in case the user + * re-installs older Additions. */ + if (idScreen == 0) + { + RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u", cx, cy, cBits); + VbglR3GuestPropWriteValue(idClient, VIDEO_PROP_PREFIX "SavedMode", szModeParms); + } + + rc2 = VbglR3GuestPropDisconnect(idClient); + if (rc != VINF_PERMISSION_DENIED) + { + if (RT_SUCCESS(rc)) + rc = rc2; + if (RT_SUCCESS(rc)) + { + /* Sanity check 1. We do not try to make allowance for someone else + * changing saved settings at the same time as us. */ + bool fEnabled2 = false; + unsigned cx2 = 0; + unsigned cy2 = 0; + unsigned cBits2 = 0; + unsigned x2 = 0; + unsigned y2 = 0; + rc = VbglR3RetrieveVideoMode(idScreen, &cx2, &cy2, &cBits2, &x2, &y2, &fEnabled2); + if ( RT_SUCCESS(rc) + && (cx != cx2 || cy != cy2 || cBits != cBits2 || x != x2 || y != y2 || fEnabled != fEnabled2)) + rc = VERR_WRITE_ERROR; + /* Sanity check 2. Same comment. */ + else if (RT_SUCCESS(rc)) + { + unsigned cHighestScreen2 = 0; + rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen2); + if (RT_SUCCESS(rc)) + if (cHighestScreen2 != RT_MAX(cHighestScreen, idScreen)) + rc = VERR_INTERNAL_ERROR; + } + } + } + } + } + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idScreen, cx, cy, cBits, x, y, fEnabled); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} + + +/** + * Retrieve video mode parameters from the guest property store. + * + * @returns iprt status value + * @param idScreen The virtual screen number. + * @param pcx where to store the mode width + * @param pcy where to store the mode height + * @param pcBits where to store the bits per pixel for the mode + * @param px where to store the virtual screen X offset + * @param py where to store the virtual screen Y offset + * @param pfEnabled where to store whether this virtual screen is enabled + */ +VBGLR3DECL(int) VbglR3RetrieveVideoMode(unsigned idScreen, + unsigned *pcx, unsigned *pcy, + unsigned *pcBits, + unsigned *px, unsigned *py, + bool *pfEnabled) +{ +#ifdef VBOX_WITH_GUEST_PROPS + /* + * First we retrieve the video mode which is saved as a string in the + * guest property store. + */ + HGCMCLIENTID idClient = 0; + int rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + int rc2; + /* The buffer for VbglR3GuestPropReadValue. If this is too small then + * something is wrong with the data stored in the property. */ + char szModeParms[1024]; + char szModeName[GUEST_PROP_MAX_NAME_LEN]; /** @todo add a VbglR3GuestPropReadValueF/FV that does the RTStrPrintf for you. */ + RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen); + rc = VbglR3GuestPropReadValue(idClient, szModeName, szModeParms, sizeof(szModeParms), NULL); + /* Try legacy single screen name. */ + if (rc == VERR_NOT_FOUND && idScreen == 0) + rc = VbglR3GuestPropReadValue(idClient, + VIDEO_PROP_PREFIX"SavedMode", + szModeParms, sizeof(szModeParms), + NULL); + rc2 = VbglR3GuestPropDisconnect(idClient); + if (RT_SUCCESS(rc)) + rc = rc2; + + /* + * Now we convert the string returned to numeric values. + */ + if (RT_SUCCESS(rc)) + { + /* Mandatory chunk: 640x480x32 */ + char *pszNext; + uint32_t cx = 0; + rc = VERR_PARSE_ERROR; + rc2 = RTStrToUInt32Ex(szModeParms, &pszNext, 10, &cx); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + uint32_t cy = 0; + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &cy); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + uint8_t cBits = 0; + rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &cBits); + if (rc2 == VINF_SUCCESS || rc2 == VWRN_TRAILING_CHARS) + { + /* Optional chunk: ,32x64,1 (we fail if this is partially there) */ + uint32_t x = 0; + uint32_t y = 0; + uint8_t fEnabled = 1; + if (rc2 == VINF_SUCCESS) + rc = VINF_SUCCESS; + else if (*pszNext == ',') + { + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &x); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &y); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == ',') + { + rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &fEnabled); + if (rc2 == VINF_SUCCESS) + rc = VINF_SUCCESS; + } + } + } + + /* + * Set result if successful. + */ + if (rc == VINF_SUCCESS) + { + if (pcx) + *pcx = cx; + if (pcy) + *pcy = cy; + if (pcBits) + *pcBits = cBits; + if (px) + *px = x; + if (py) + *py = y; + if (pfEnabled) + *pfEnabled = RT_BOOL(fEnabled); + } + } + } + } + } + } + + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idScreen, pcx, pcy, pcBits, px, py, pfEnabled); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp new file mode 100644 index 00000000..76f1a59d --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp @@ -0,0 +1,64 @@ +/* $Id: VBoxGuestR3LibVrdp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, VRDP. + */ + +/* + * Copyright (C) 2007-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/time.h> +#include <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3VrdpGetChangeRequest(bool *pfActive, uint32_t *puExperienceLevel) +{ + VMMDevVRDPChangeRequest Req; + RT_ZERO(Req); /* implicit padding */ + vmmdevInitRequest(&Req.header, VMMDevReq_GetVRDPChangeRequest); //VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetVRDPChangeRequest); + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pfActive = Req.u8VRDPActive != 0; + *puExperienceLevel = Req.u32VRDPExperienceLevel; + } + else + { + *pfActive = false; + *puExperienceLevel = 0; + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp new file mode 100644 index 00000000..d9cc3a0f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp @@ -0,0 +1,52 @@ +/* $Id: VbglR0CanUsePhysPageList.cpp $ */ +/** @file + * VBoxGuestLibR0 - Physical memory heap. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * 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 * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +/** + * Checks whether the host supports physical page lists or not. + * + * @returns true if it does, false if it doesn't. + */ +DECLR0VBGL(bool) VbglR0CanUsePhysPageList(void) +{ + /* a_fLocked is false, because the actual capability of the host is requested. + * See VBGLR0_CAN_USE_PHYS_PAGE_LIST definition. + */ + int rc = vbglR0Enter(); + return RT_SUCCESS(rc) + && VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk new file mode 100644 index 00000000..9268576b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk @@ -0,0 +1,52 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the common guest addition code library testcases. +# + +# +# Copyright (C) 2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +SUB_DEPTH = ../../../../../../.. +include $(KBUILD_PATH)/subheader.kmk +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD) + +# +# Testcase for the physical heap. +# +PROGRAMS += tstVbglR0PhysHeap-1 +tstVbglR0PhysHeap-1_TEMPLATE = VBOXR3TSTEXE +tstVbglR0PhysHeap-1_SOURCES = \ + tstVbglR0PhysHeap-1.cpp + + +endif # defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD) +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp new file mode 100644 index 00000000..c367ffd7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp @@ -0,0 +1,415 @@ +/* $Id: tstVbglR0PhysHeap-1.cpp $ */ +/** @file + * IPRT Testcase - Offset Based Heap. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/initterm.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/param.h> +#include <iprt/test.h> +#include <iprt/time.h> + +#define IN_TESTCASE +#define IN_RING0 /* pretend we're in ring-0 so we get access to the functions */ +#include "../VBoxGuestR0LibInternal.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct +{ + uint32_t cb; + void *pv; +} TSTHISTORYENTRY; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +VBGLDATA g_vbgldata; + +int g_cChunks = 0; +size_t g_cbChunks = 0; + +/** Drop-in replacement for RTMemContAlloc */ +static void *tstMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + RTTESTI_CHECK(cb > 0); + +#define TST_MAX_CHUNKS 24 + if (g_cChunks < TST_MAX_CHUNKS) + { + void *pvRet = RTMemAlloc(cb); + if (pvRet) + { + g_cChunks++; + g_cbChunks += cb; + *pPhys = (uint32_t)(uintptr_t)pvRet ^ (UINT32_C(0xf0f0f0f0) & ~(uint32_t)PAGE_OFFSET_MASK); + + /* Avoid problematic values that won't happen in real life: */ + if (!*pPhys) + *pPhys = 4U << PAGE_SHIFT; + if (UINT32_MAX - *pPhys < cb) + *pPhys -= RT_ALIGN_32(cb, PAGE_SIZE); + + return pvRet; + } + } + + *pPhys = NIL_RTCCPHYS; + return NULL; +} + + +/** Drop-in replacement for RTMemContFree */ +static void tstMemContFree(void *pv, size_t cb) +{ + RTTESTI_CHECK(RT_VALID_PTR(pv)); + RTTESTI_CHECK(cb > 0); + RTTESTI_CHECK(g_cChunks > 0); + RTMemFree(pv); + g_cChunks--; + g_cbChunks -= cb; +} + + +#define RTMemContAlloc tstMemContAlloc +#define RTMemContFree tstMemContFree +#include "../VBoxGuestR0LibPhysHeap.cpp" + + +static void PrintStats(TSTHISTORYENTRY const *paHistory, size_t cHistory, const char *pszDesc) +{ + size_t cbAllocated = 0; + unsigned cLargeBlocks = 0; + unsigned cAllocated = 0; + for (size_t i = 0; i < cHistory; i++) + if (paHistory[i].pv) + { + cAllocated += 1; + cbAllocated += paHistory[i].cb; + cLargeBlocks += paHistory[i].cb > _1K; + } + + size_t const cbOverhead = g_cChunks * sizeof(VBGLPHYSHEAPCHUNK) + cAllocated * sizeof(VBGLPHYSHEAPBLOCK); + size_t const cbFragmentation = g_cbChunks - cbOverhead - cbAllocated; + RTTestIPrintf(RTTESTLVL_ALWAYS, + "%s: %'9zu bytes in %2d chunks; %'9zu bytes in %4u blocks (%2u large)\n" + " => int-frag %'9zu (%2zu.%1zu%%) overhead %'9zu (%1zu.%02zu%%)\n", + pszDesc, + g_cbChunks, g_cChunks, + cbAllocated, cAllocated, cLargeBlocks, + cbFragmentation, cbFragmentation * 100 / g_cbChunks, (cbFragmentation * 1000 / g_cbChunks) % 10, + cbOverhead, cbOverhead * 100 / g_cbChunks, (cbOverhead * 10000 / g_cbChunks) % 100); +} + + +int main(int argc, char **argv) +{ + RT_NOREF_PV(argc); RT_NOREF_PV(argv); + + /* + * Init runtime. + */ + RTTEST hTest; + int rc = RTTestInitAndCreate("tstVbglR0PhysHeap-1", &hTest); + if (rc) + return rc; + RTTestBanner(hTest); + + /* + * Arguments are taken to be random seeding. + */ + uint64_t uRandSeed = RTTimeNanoTS(); + for (int i = 1; i < argc; i++) + { + rc = RTStrToUInt64Full(argv[i], 0, &uRandSeed); + if (rc != VINF_SUCCESS) + { + RTTestIFailed("Invalid parameter: %Rrc: %s\n", rc, argv[i]); + return RTTestSummaryAndDestroy(hTest); + } + } + + /* + * Create a heap. + */ + RTTestSub(hTest, "Basics"); + RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + +#define CHECK_PHYS_ADDR(a_pv) do { \ + uint32_t const uPhys = VbglR0PhysHeapGetPhysAddr(a_pv); \ + if (uPhys == 0 || uPhys == UINT32_MAX || (uPhys & PAGE_OFFSET_MASK) != ((uintptr_t)(a_pv) & PAGE_OFFSET_MASK)) \ + RTTestIFailed("line %u: %s=%p: uPhys=%#x\n", __LINE__, #a_pv, (a_pv), uPhys); \ + } while (0) + + /* + * Try allocate. + */ + static struct TstPhysHeapOps + { + uint32_t cb; + unsigned iFreeOrder; + void *pvAlloc; + } s_aOps[] = + { + { 16, 0, NULL }, // 0 + { 16, 1, NULL }, + { 16, 2, NULL }, + { 16, 5, NULL }, + { 16, 4, NULL }, + { 32, 3, NULL }, // 5 + { 31, 6, NULL }, + { 1024, 8, NULL }, + { 1024, 10, NULL }, + { 1024, 12, NULL }, + { PAGE_SIZE, 13, NULL }, // 10 + { 1024, 9, NULL }, + { PAGE_SIZE, 11, NULL }, + { PAGE_SIZE, 14, NULL }, + { 16, 15, NULL }, + { 9, 7, NULL }, // 15 + { 16, 7, NULL }, + { 36, 7, NULL }, + { 16, 7, NULL }, + { 12344, 7, NULL }, + { 50, 7, NULL }, // 20 + { 16, 7, NULL }, + }; + uint32_t i; + //RTHeapOffsetDump(Heap, (PFNRTHEAPOFFSETPRINTF)(uintptr_t)RTPrintf); /** @todo Add some detail info output with a signature identical to RTPrintf. */ + //size_t cbBefore = VbglR0PhysHeapGetFreeSize(); + static char const s_szFill[] = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* allocate */ + for (i = 0; i < RT_ELEMENTS(s_aOps); i++) + { + s_aOps[i].pvAlloc = VbglR0PhysHeapAlloc(s_aOps[i].cb); + RTTESTI_CHECK_MSG(s_aOps[i].pvAlloc, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i)); + if (!s_aOps[i].pvAlloc) + return RTTestSummaryAndDestroy(hTest); + + memset(s_aOps[i].pvAlloc, s_szFill[i], s_aOps[i].cb); + RTTESTI_CHECK_MSG(RT_ALIGN_P(s_aOps[i].pvAlloc, sizeof(void *)) == s_aOps[i].pvAlloc, + ("VbglR0PhysHeapAlloc(%#x) -> %p\n", s_aOps[i].cb, i)); + + CHECK_PHYS_ADDR(s_aOps[i].pvAlloc); + + /* Check heap integrity: */ + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + } + + /* free and allocate the same node again. */ + for (i = 0; i < RT_ELEMENTS(s_aOps); i++) + { + if (!s_aOps[i].pvAlloc) + continue; + //RTPrintf("debug: i=%d pv=%#x cb=%#zx align=%#zx cbReal=%#zx\n", i, s_aOps[i].pvAlloc, + // s_aOps[i].cb, s_aOps[i].uAlignment, RTHeapOffsetSize(Heap, s_aOps[i].pvAlloc)); + size_t cbBeforeSub = VbglR0PhysHeapGetFreeSize(); + VbglR0PhysHeapFree(s_aOps[i].pvAlloc); + size_t cbAfterSubFree = VbglR0PhysHeapGetFreeSize(); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + + void *pv; + pv = VbglR0PhysHeapAlloc(s_aOps[i].cb); + RTTESTI_CHECK_MSG(pv, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i)); + if (!pv) + return RTTestSummaryAndDestroy(hTest); + CHECK_PHYS_ADDR(pv); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + + //RTPrintf("debug: i=%d pv=%p cbReal=%#zx cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx \n", i, pv, RTHeapOffsetSize(Heap, pv), + // cbBeforeSub, cbAfterSubFree, VbglR0PhysHeapGetFreeSize()); + + if (pv != s_aOps[i].pvAlloc) + RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: Free+Alloc returned different address. new=%p old=%p i=%d\n", pv, s_aOps[i].pvAlloc, i); + s_aOps[i].pvAlloc = pv; + size_t cbAfterSubAlloc = VbglR0PhysHeapGetFreeSize(); + if (cbBeforeSub != cbAfterSubAlloc) + { + RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx. i=%d\n", + cbBeforeSub, cbAfterSubFree, cbAfterSubAlloc, i); + //return 1; - won't work correctly until we start creating free block instead of donating memory on alignment. + } + } + + VbglR0PhysHeapTerminate(); + RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks)); + + + /* + * Use random allocation pattern + */ + RTTestSub(hTest, "Random Test"); + RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + + RTRAND hRand; + RTTESTI_CHECK_RC(rc = RTRandAdvCreateParkMiller(&hRand), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + RTRandAdvSeed(hRand, uRandSeed); + RTTestValue(hTest, "RandSeed", uRandSeed, RTTESTUNIT_NONE); + + static TSTHISTORYENTRY s_aHistory[3072]; + RT_ZERO(s_aHistory); + + for (unsigned iTest = 0; iTest < 131072; iTest++) + { + i = RTRandAdvU32Ex(hRand, 0, RT_ELEMENTS(s_aHistory) - 1); + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 8, 1024); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = 9; + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + } + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0xbb, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + } + else + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + +#if 1 + /* Check heap integrity: */ + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + int cChunks = 0; + for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext) + cChunks++; + RTTESTI_CHECK_MSG(cChunks == g_cChunks, ("g_cChunks=%u, but only %u chunks in the list!\n", g_cChunks, cChunks)); +#endif + + if ((iTest % 7777) == 7776) + { + /* exhaust the heap */ + PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-pre "); + + for (i = 0; i < RT_ELEMENTS(s_aHistory) && (VbglR0PhysHeapGetFreeSize() >= 256 || g_cChunks < TST_MAX_CHUNKS); i++) + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, VBGL_PH_CHUNKSIZE / 8, VBGL_PH_CHUNKSIZE / 2 + VBGL_PH_CHUNKSIZE / 4); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + } + + size_t cbFree = VbglR0PhysHeapGetFreeSize(); + if (cbFree) + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 1, (uint32_t)cbFree); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + while (s_aHistory[i].pv == NULL && s_aHistory[i].cb > 2) + { + s_aHistory[i].cb >>= 1; + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + } + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + + cbFree = VbglR0PhysHeapGetFreeSize(); + if (!cbFree) + break; + } + + RTTESTI_CHECK_MSG(VbglR0PhysHeapGetFreeSize() == 0, ("%zu\n", VbglR0PhysHeapGetFreeSize())); + PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-post"); + } + else if ((iTest % 7777) == 1111) + { + /* free all */ + RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-pre: cFreeBlocks=%u cAllocedBlocks=%u in %u chunk(s)\n", + g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks - g_vbgldata.cFreeBlocks, g_cChunks); + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-post: cFreeBlocks=%u in %u chunk(s)\n", g_vbgldata.cFreeBlocks, g_cChunks); + RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks)); + RTTESTI_CHECK_MSG(g_vbgldata.cFreeBlocks == g_vbgldata.cBlocks, + ("g_vbgldata.cFreeBlocks=%d cBlocks=%d\n", g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks)); + + //size_t cbAfterRand = VbglR0PhysHeapGetFreeSize(); + //RTTESTI_CHECK_MSG(cbAfterRand == cbAfter, ("cbAfterRand=%zu cbAfter=%zu\n", cbAfterRand, cbAfter)); + } + } + + /* free the rest. */ + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + + RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks)); + + VbglR0PhysHeapTerminate(); + RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks)); + + RTTESTI_CHECK_RC(rc = RTRandAdvDestroy(hRand), VINF_SUCCESS); + return RTTestSummaryAndDestroy(hTest); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/linux/Makefile b/src/VBox/Additions/common/VBoxGuest/linux/Makefile new file mode 100644 index 00000000..84e40880 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/linux/Makefile @@ -0,0 +1,213 @@ +# $Id: Makefile $ +## @file +# VirtualBox Guest Additions Module Makefile. +# + +# +# Copyright (C) 2006-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# Linux kbuild sets this to our source directory if we are called from there +obj ?= $(CURDIR) +include $(obj)/Makefile-header.gmk +VBOXGUEST_DIR = $(VBOX_MODULE_SRC_DIR) + +#VBOX_WITHOUT_COMBINED_SOURCES=1 + +VBOXMOD_NAME = vboxguest +VBOXMOD_OBJS = \ + VBoxGuest-linux.o \ + VBoxGuest-common.o +ifndef VBOX_WITHOUT_COMBINED_SOURCES +VBOXMOD_OBJS += \ + common/string/strformatrt.o \ + combined-agnostic.o \ + combined-os-specific.o +else # VBOX_WITHOUT_COMBINED_SOURCES +VBOXMOD_OBJS += \ + VBoxGuestR0LibGenericRequest.o \ + VBoxGuestR0LibHGCMInternal.o \ + VBoxGuestR0LibInit.o \ + VBoxGuestR0LibPhysHeap.o \ + VBoxGuestR0LibVMMDev.o \ + r0drv/alloc-r0drv.o \ + r0drv/initterm-r0drv.o \ + r0drv/memobj-r0drv.o \ + r0drv/mpnotification-r0drv.o \ + r0drv/powernotification-r0drv.o \ + r0drv/linux/alloc-r0drv-linux.o \ + r0drv/linux/assert-r0drv-linux.o \ + r0drv/linux/initterm-r0drv-linux.o \ + r0drv/linux/memobj-r0drv-linux.o \ + r0drv/linux/memuserkernel-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/semmutex-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 \ + r0drv/linux/RTLogWriteDebugger-r0drv-linux.o \ + r0drv/generic/semspinmutex-r0drv-generic.o \ + common/alloc/alloc.o \ + common/checksum/crc32.o \ + common/err/RTErrConvertFromErrno.o \ + common/err/RTErrConvertToErrno.o \ + common/err/errinfo.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/log/RTLogCreateEx.o \ + common/misc/RTAssertMsg1Weak.o \ + common/misc/RTAssertMsg2.o \ + common/misc/RTAssertMsg2Add.o \ + common/misc/RTAssertMsg2AddWeak.o \ + common/misc/RTAssertMsg2AddWeakV.o \ + common/misc/RTAssertMsg2Weak.o \ + common/misc/RTAssertMsg2WeakV.o \ + common/misc/assert.o \ + common/misc/thread.o \ + common/string/RTStrCat.o \ + common/string/RTStrCmp.o \ + common/string/RTStrCopy.o \ + common/string/RTStrCopyEx.o \ + common/string/RTStrCopyP.o \ + common/string/RTStrEnd.o \ + common/string/RTStrICmpAscii.o \ + common/string/RTStrNICmpAscii.o \ + common/string/RTStrNCmp.o \ + common/string/RTStrNLen.o \ + common/string/stringalloc.o \ + common/string/strformat.o \ + common/string/RTStrFormat.o \ + common/string/strformatnum.o \ + common/string/strformatrt.o \ + common/string/strformattype.o \ + common/string/strprintf.o \ + common/string/strprintf-ellipsis.o \ + common/string/strprintf2.o \ + common/string/strprintf2-ellipsis.o \ + common/string/strtonum.o \ + common/string/utf-8.o \ + common/table/avlpv.o \ + common/time/time.o \ + generic/RTAssertShouldPanic-generic.o \ + generic/RTLogWriteStdErr-stub-generic.o \ + generic/RTLogWriteStdOut-stub-generic.o \ + generic/RTMpGetCoreCount-generic.o \ + generic/RTSemEventWait-2-ex-generic.o \ + generic/RTSemEventWaitNoResume-2-ex-generic.o \ + generic/RTSemEventMultiWait-2-ex-generic.o \ + generic/RTSemEventMultiWaitNoResume-2-ex-generic.o \ + generic/rtStrFormatKernelAddress-generic.o \ + generic/errvars-generic.o \ + generic/mppresent-generic.o \ + VBox/log-vbox.o \ + VBox/logbackdoor.o \ + VBox/RTLogWriteVmm-amd64-x86.o + ifeq ($(VBOX_KBUILD_TARGET_ARCH),amd64) +VBOXMOD_OBJS += common/alloc/heapsimple.o + endif +endif # VBOX_WITHOUT_COMBINED_SOURCES +ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86) +VBOXMOD_OBJS += \ + common/math/gcc/divdi3.o \ + common/math/gcc/divmoddi4.o \ + common/math/gcc/moddi3.o \ + common/math/gcc/udivdi3.o \ + common/math/gcc/udivmoddi4.o \ + common/math/gcc/umoddi3.o \ + common/math/gcc/qdivrem.o +endif + +VBOXMOD_DEFS = \ + VBOX \ + RT_OS_LINUX \ + IN_RING0 \ + IN_RT_R0 \ + IN_GUEST \ + IN_GUEST_R0 \ + IN_MODULE \ + RT_WITH_VBOX \ + VBGL_VBOXGUEST \ + VBOX_WITH_HGCM +ifeq ($(VBOX_KBUILD_TARGET_ARCH),amd64) +VBOXMOD_DEFS += VBOX_WITH_64_BITS_GUESTS +endif +ifeq ($(KERN_VERSION),24) +VBOXMOD_DEFS += EXPORT_SYMTAB +endif + +VBOXMOD_INCL = \ + $(VBOXGUEST_DIR) \ + $(VBOXGUEST_DIR)include \ + $(VBOXGUEST_DIR)r0drv/linux + +VBOXMOD_CFLAGS := $(call VBOX_GCC_CHECK_CC,-Wno-declaration-after-statement,-Wno-declaration-after-statement,,) +VBOXMOD_CFLAGS += $(call VBOX_GCC_CHECK_CC,-fno-pie,-fno-pie,,) +ifneq ($(KERN_VERSION),24) +VBOXMOD_CFLAGS += -include $(VBOXGUEST_DIR)include/VBox/VBoxGuestMangling.h +endif + +VBOXMOD_CLEAN = \ + . \ + linux \ + r0drv \ + generic \ + r0drv/linux \ + r0drv/generic \ + VBox \ + common/alloc \ + common/err \ + common/log \ + common/math/gcc \ + common/misc \ + common/string \ + common/table \ + common/time + +include $(obj)/Makefile-footer.gmk + +check: $(VBOXMOD_NAME) + @if ! readelf -p __ksymtab_strings vboxguest.ko | grep -E "\[.*\] *(RT|g_..*RT.*)"; then \ + echo "All exported IPRT symbols are properly renamed!"; \ + else \ + echo "error: Some exported IPRT symbols was not properly renamed! See above." >&2; \ + false; \ + fi + diff --git a/src/VBox/Additions/common/VBoxGuest/linux/Makefile.kup b/src/VBox/Additions/common/VBoxGuest/linux/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/linux/Makefile.kup diff --git a/src/VBox/Additions/common/VBoxGuest/linux/combined-agnostic.c b/src/VBox/Additions/common/VBoxGuest/linux/combined-agnostic.c new file mode 100644 index 00000000..8aa409bd --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/linux/combined-agnostic.c @@ -0,0 +1,189 @@ +/* $Id: combined-agnostic.c $ */ +/** @file + * VBoxGuest - Combine a bunch of OS agnostic sources into one compile unit. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#define LOG_GROUP LOG_GROUP_DEFAULT +#include "internal/iprt.h" +#include <VBox/log.h> + +//#undef LOG_GROUP +#include "VBoxGuestR0LibGenericRequest.c" +#undef LOG_GROUP +#include "VBoxGuestR0LibHGCMInternal.c" +//#undef LOG_GROUP +#include "VBoxGuestR0LibInit.c" +//#undef LOG_GROUP +#include "VBoxGuestR0LibPhysHeap.c" +#undef LOG_GROUP +#include "VBoxGuestR0LibVMMDev.c" +#undef LOG_GROUP +#include "r0drv/alloc-r0drv.c" +#undef LOG_GROUP +#include "r0drv/initterm-r0drv.c" +#undef LOG_GROUP +#include "r0drv/memobj-r0drv.c" +#undef LOG_GROUP +#include "r0drv/mpnotification-r0drv.c" +#undef LOG_GROUP +#include "r0drv/powernotification-r0drv.c" +#undef LOG_GROUP +#include "r0drv/generic/semspinmutex-r0drv-generic.c" +#undef LOG_GROUP +#include "common/alloc/alloc.c" +#undef LOG_GROUP +#include "common/checksum/crc32.c" +#undef LOG_GROUP +#include "common/err/errinfo.c" +#undef LOG_GROUP +#include "common/log/log.c" +#undef LOG_GROUP +#include "common/log/logellipsis.c" +#undef LOG_GROUP +#include "common/log/logrel.c" +#undef LOG_GROUP +#include "common/log/logrelellipsis.c" +#undef LOG_GROUP +#include "common/log/logcom.c" +#undef LOG_GROUP +#include "common/log/logformat.c" +#undef LOG_GROUP +#include "common/log/RTLogCreateEx.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg1Weak.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2Add.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2AddWeak.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2AddWeakV.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2Weak.c" +#undef LOG_GROUP +#include "common/misc/RTAssertMsg2WeakV.c" +#undef LOG_GROUP +#include "common/misc/assert.c" +#undef LOG_GROUP +#include "common/misc/thread.c" +#undef LOG_GROUP +#include "common/string/RTStrCat.c" +#undef LOG_GROUP +#include "common/string/RTStrCmp.c" +#undef LOG_GROUP +#include "common/string/RTStrCopy.c" +#undef LOG_GROUP +#include "common/string/RTStrCopyEx.c" +#undef LOG_GROUP +#include "common/string/RTStrCopyP.c" +#undef LOG_GROUP +#include "common/string/RTStrEnd.c" +#undef LOG_GROUP +#include "common/string/RTStrICmpAscii.c" +#undef LOG_GROUP +#include "common/string/RTStrNICmpAscii.c" +#undef LOG_GROUP +#include "common/string/RTStrNCmp.c" +#undef LOG_GROUP +#include "common/string/RTStrNLen.c" +#undef LOG_GROUP +#include "common/string/stringalloc.c" +#undef LOG_GROUP +#include "common/string/strformat.c" +#undef LOG_GROUP +#include "common/string/RTStrFormat.c" +#undef LOG_GROUP +#include "common/string/strformatnum.c" +#undef LOG_GROUP +#include "common/string/strformattype.c" +#undef LOG_GROUP +#include "common/string/strprintf.c" +#undef LOG_GROUP +#include "common/string/strprintf-ellipsis.c" +#undef LOG_GROUP +#include "common/string/strprintf2.c" +#undef LOG_GROUP +#include "common/string/strprintf2-ellipsis.c" +#undef LOG_GROUP +#include "common/string/strtonum.c" +#undef LOG_GROUP +#include "common/string/utf-8.c" +#undef LOG_GROUP +#include "common/table/avlpv.c" +#undef LOG_GROUP +#include "common/time/time.c" +#undef LOG_GROUP +#include "generic/RTAssertShouldPanic-generic.c" +#undef LOG_GROUP +#include "generic/RTLogWriteStdErr-stub-generic.c" +#undef LOG_GROUP +#include "generic/RTLogWriteStdOut-stub-generic.c" +#undef LOG_GROUP +#include "generic/RTMpGetCoreCount-generic.c" +#undef LOG_GROUP +#include "generic/RTSemEventWait-2-ex-generic.c" +#undef LOG_GROUP +#include "generic/RTSemEventWaitNoResume-2-ex-generic.c" +#undef LOG_GROUP +#include "generic/RTSemEventMultiWait-2-ex-generic.c" +#undef LOG_GROUP +#include "generic/RTSemEventMultiWaitNoResume-2-ex-generic.c" +#undef LOG_GROUP +#include "generic/rtStrFormatKernelAddress-generic.c" +#undef LOG_GROUP +#include "generic/errvars-generic.c" +#undef LOG_GROUP +#include "generic/mppresent-generic.c" +#undef LOG_GROUP +#include "VBox/log-vbox.c" +#undef LOG_GROUP +#include "VBox/logbackdoor.c" +#undef LOG_GROUP +#include "VBox/RTLogWriteVmm-amd64-x86.c" + +#ifdef RT_ARCH_AMD64 +# undef LOG_GROUP +# include "common/alloc/heapsimple.c" +#endif + +#if 0 //def RT_ARCH_X86 - iprt/nocrt/limit.h clashes. +# include "common/math/gcc/divdi3.c" +# include "common/math/gcc/moddi3.c" +# include "common/math/gcc/udivdi3.c" +# include "common/math/gcc/udivmoddi4.c" +# include "common/math/gcc/umoddi3.c" +# include "common/math/gcc/qdivrem.c" +#endif + diff --git a/src/VBox/Additions/common/VBoxGuest/linux/combined-os-specific.c b/src/VBox/Additions/common/VBoxGuest/linux/combined-os-specific.c new file mode 100644 index 00000000..4f0731b5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/linux/combined-os-specific.c @@ -0,0 +1,61 @@ +/* $Id: combined-os-specific.c $ */ +/** @file + * VBoxGuest - Combine a bunch of OS specific sources into one compile unit. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +#include "the-linux-kernel.h" + +#include "r0drv/linux/alloc-r0drv-linux.c" +#include "r0drv/linux/assert-r0drv-linux.c" +#include "r0drv/linux/initterm-r0drv-linux.c" +#include "r0drv/linux/memobj-r0drv-linux.c" +#include "r0drv/linux/memuserkernel-r0drv-linux.c" +#include "r0drv/linux/mp-r0drv-linux.c" +#include "r0drv/linux/mpnotification-r0drv-linux.c" +#include "r0drv/linux/process-r0drv-linux.c" +#include "r0drv/linux/semevent-r0drv-linux.c" +#include "r0drv/linux/semeventmulti-r0drv-linux.c" +#include "r0drv/linux/semfastmutex-r0drv-linux.c" +#include "r0drv/linux/semmutex-r0drv-linux.c" +#include "r0drv/linux/spinlock-r0drv-linux.c" +#include "r0drv/linux/thread-r0drv-linux.c" +#include "r0drv/linux/thread2-r0drv-linux.c" +#undef LOG_GROUP +#include "r0drv/linux/time-r0drv-linux.c" +#include "r0drv/linux/timer-r0drv-linux.c" +#include "r0drv/linux/RTLogWriteDebugger-r0drv-linux.c" +#include "common/err/RTErrConvertFromErrno.c" +#include "common/err/RTErrConvertToErrno.c" + diff --git a/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest new file mode 100755 index 00000000..4bf6f502 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest @@ -0,0 +1,237 @@ +#!/bin/sh +# $Id: files_vboxguest $ +## @file +# Shared file between Makefile.kmk and export_modules.sh. +# + +# +# Copyright (C) 2007-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +FILES_VBOXGUEST_NOBIN=" \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \ + ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.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/crc.h=>include/iprt/crc.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/errcore.h=>include/iprt/errcore.h \ + ${PATH_ROOT}/include/iprt/errno.h=>include/iprt/errno.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/latin1.h=>include/iprt/latin1.h \ + ${PATH_ROOT}/include/iprt/list.h=>include/iprt/list.h \ + ${PATH_ROOT}/include/iprt/lockvalidator.h=>include/iprt/lockvalidator.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.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/net.h=>include/iprt/net.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/path.h=>include/iprt/path.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/uint64.h=>include/iprt/uint64.h \ + ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \ + ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \ + ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \ + ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \ + ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.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/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/include/VBox/ostypes.h=>include/VBox/ostypes.h \ + ${PATH_ROOT}/include/VBox/VMMDev.h=>include/VBox/VMMDev.h \ + ${PATH_ROOT}/include/VBox/VMMDevCoreTypes.h=>include/VBox/VMMDevCoreTypes.h \ + ${PATH_ROOT}/include/VBox/VBoxGuest.h=>include/VBox/VBoxGuest.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestCoreTypes.h=>include/VBox/VBoxGuestCoreTypes.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestLib.h=>include/VBox/VBoxGuestLib.h \ + ${PATH_ROOT}/include/VBox/VBoxGuestMangling.h=>include/VBox/VBoxGuestMangling.h \ + ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \ + ${PATH_ROOT}/include/VBox/HostServices/GuestPropertySvc.h=>include/VBox/HostServices/GuestPropertySvc.h \ + ${PATH_ROOT}/include/VBox/vmm/cpuidcall.h=>include/VBox/vmm/cpuidcall.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp=>VBoxGuest-common.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c=>VBoxGuest-linux.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h=>VBoxGuestInternal.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/linux/Makefile=>Makefile \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/linux/combined-agnostic.c=>combined-agnostic.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/linux/combined-os-specific.c=>combined-os-specific.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h=>VBoxGuestR0LibInternal.h \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp=>VBoxGuestR0LibGenericRequest.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp=>VBoxGuestR0LibHGCMInternal.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp=>VBoxGuestR0LibInit.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp=>VBoxGuestR0LibPhysHeap.c \ + ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp=>VBoxGuestR0LibVMMDev.c \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/assert.h=>include/internal/assert.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/initterm.h=>include/internal/initterm.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/iprt.h=>include/internal/iprt.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/lockvalidator.h=>include/internal/lockvalidator.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/magics.h=>include/internal/magics.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/mem.h=>include/internal/mem.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/include/internal/time.h=>include/internal/time.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/sched.h=>include/internal/sched.h \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/process.h=>include/internal/process.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/alloc/alloc.cpp=>common/alloc/alloc.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/alloc/heapsimple.cpp=>common/alloc/heapsimple.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/checksum/crc32.cpp=>common/checksum/crc32.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/err/errinfo.cpp=>common/err/errinfo.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/logformat.cpp=>common/log/logformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/log/RTLogCreateEx.cpp=>common/log/RTLogCreateEx.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>common/math/gcc/divdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divmoddi4.c=>common/math/gcc/divmoddi4.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>common/math/gcc/moddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>common/math/gcc/qdivrem.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>common/math/gcc/quad.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>common/math/gcc/udivdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>common/math/gcc/udivmoddi4.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>common/math/gcc/umoddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg1Weak.cpp=>common/misc/RTAssertMsg1Weak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2.cpp=>common/misc/RTAssertMsg2.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2Add.cpp=>common/misc/RTAssertMsg2Add.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2AddWeak.cpp=>common/misc/RTAssertMsg2AddWeak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2AddWeakV.cpp=>common/misc/RTAssertMsg2AddWeakV.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2Weak.cpp=>common/misc/RTAssertMsg2Weak.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/RTAssertMsg2WeakV.cpp=>common/misc/RTAssertMsg2WeakV.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/assert.cpp=>common/misc/assert.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/misc/thread.cpp=>common/misc/thread.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCat.cpp=>common/string/RTStrCat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCmp.cpp=>common/string/RTStrCmp.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopy.cpp=>common/string/RTStrCopy.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyEx.cpp=>common/string/RTStrCopyEx.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrCopyP.cpp=>common/string/RTStrCopyP.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrEnd.cpp=>common/string/RTStrEnd.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrICmpAscii.cpp=>common/string/RTStrICmpAscii.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNICmpAscii.cpp=>common/string/RTStrNICmpAscii.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNCmp.cpp=>common/string/RTStrNCmp.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrNLen.cpp=>common/string/RTStrNLen.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/stringalloc.cpp=>common/string/stringalloc.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/RTStrFormat.cpp=>common/string/RTStrFormat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatnum.cpp=>common/string/strformatnum.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/strprintf-ellipsis.cpp=>common/string/strprintf-ellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2.cpp=>common/string/strprintf2.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf2-ellipsis.cpp=>common/string/strprintf2-ellipsis.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/utf-8.cpp=>common/string/utf-8.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avlpv.cpp=>common/table/avlpv.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Base.cpp.h=>common/table/avl_Base.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Get.cpp.h=>common/table/avl_Get.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_GetBestFit.cpp.h=>common/table/avl_GetBestFit.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_RemoveBestFit.cpp.h=>common/table/avl_RemoveBestFit.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_DoWithAll.cpp.h=>common/table/avl_DoWithAll.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/table/avl_Destroy.cpp.h=>common/table/avl_Destroy.cpp.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/time/time.cpp=>common/time/time.c \ + ${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/RTMpGetCoreCount-generic.cpp=>generic/RTMpGetCoreCount-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp=>generic/RTSemEventWait-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp=>generic/RTSemEventWaitNoResume-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp=>generic/RTSemEventMultiWait-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp=>generic/RTSemEventMultiWaitNoResume-2-ex-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp=>generic/rtStrFormatKernelAddress-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/errvars-generic.cpp=>generic/errvars-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/generic/mppresent-generic.cpp=>generic/mppresent-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/memobj-r0drv.cpp=>r0drv/memobj-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/alloc-r0drv-linux.c=>r0drv/linux/alloc-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/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/memuserkernel-r0drv-linux.c=>r0drv/linux/memuserkernel-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/semmutex-r0drv-linux.c=>r0drv/linux/semmutex-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/linux/waitqueue-r0drv-linux.h=>r0drv/linux/waitqueue-r0drv-linux.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c=>r0drv/linux/RTLogWriteDebugger-r0drv-linux.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c=>r0drv/generic/semspinmutex-r0drv-generic.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/log-vbox.cpp=>VBox/log-vbox.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/logbackdoor.cpp=>VBox/logbackdoor.c \ + ${PATH_ROOT}/src/VBox/Runtime/VBox/RTLogWriteVmm-amd64-x86.cpp=>VBox/RTLogWriteVmm-amd64-x86.c \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ + ${PATH_OUT}/product-generated.h=>product-generated.h \ + ${PATH_OUT}/revision-generated.h=>revision-generated.h \ +" + +FILES_VBOXGUEST_BIN=" \ +" + diff --git a/src/VBox/Additions/common/VBoxGuest/netbsd/locators.h b/src/VBox/Additions/common/VBoxGuest/netbsd/locators.h new file mode 100644 index 00000000..91272794 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/netbsd/locators.h @@ -0,0 +1,8 @@ +/* $Id: locators.h $ */ +/** @file + * Placeholder "locators.h" that wsmousevar.h needs (bad hygiene). + * + * It's normally generated by config(8), but see the explanatory + * comment in vboxguest.ioconf + */ +#define WSMOUSEDEVCF_MUX 0 diff --git a/src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf b/src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf new file mode 100644 index 00000000..1b1d7b52 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf @@ -0,0 +1,66 @@ +# $Id: vboxguest.ioconf $ +## @file +# NetBSD vboxguest module configuration +# + +# +# Copyright (C) 2017-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# XXX: +# +# VBoxGuest-netbsd.c has manually edited copy of the config glue and +# we also provide stub "locators.h" in this directory. Both should +# really be generated by config(8) from this ioconf file but that runs +# into a couple of problems. +# +# We want to attach wsmouse(4) as a child, but we cannot expect the +# kernel that loads us to have "wsmouse* at wsmousedev?" attachment +# and in fact until recently x86 kernels didn't have it. +# +# But when we specify "wsmouse* at vboxguest?" attachment below +# config(8) thinks that this module defines wsmouse and generates +# CFDRIVER_DECL() for it and also includes it into cfdriver and +# cfattachinit arrays it emits. + +ioconf vboxguest + +include "conf/files" + +include "dev/i2o/files.i2o" # XXX: pci needs device iop +include "dev/pci/files.pci" + +device vboxguest: wsmousedev +attach vboxguest at pci + +pseudo-root pci* +vboxguest0 at pci? dev ? function ? + +wsmouse* at vboxguest? diff --git a/src/VBox/Additions/common/VBoxGuest/solaris/deps.asm b/src/VBox/Additions/common/VBoxGuest/solaris/deps.asm new file mode 100644 index 00000000..ca25a040 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/solaris/deps.asm @@ -0,0 +1,48 @@ +; $Id: deps.asm $ +;; @file +; Solaris kernel module dependency +; + +; +; Copyright (C) 2012-2022 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox 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. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + +%include "iprt/solaris/kmoddeps.mac" + +kmoddeps_header ; ELF header, section table and shared string table + +kmoddeps_dynstr_start ; ELF .dynstr section +kmoddeps_dynstr_string str_misc_ctf, "misc/ctf" +kmoddeps_dynstr_end + +kmoddeps_dynamic_start ; ELF .dynamic section +kmoddeps_dynamic_needed str_misc_ctf +kmoddeps_dynamic_end + diff --git a/src/VBox/Additions/common/VBoxGuest/solaris/load.sh b/src/VBox/Additions/common/VBoxGuest/solaris/load.sh new file mode 100755 index 00000000..7e9a5589 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/solaris/load.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# $Id: load.sh $ +## @file +# For GA development. +# + +# +# Copyright (C) 2006-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox 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. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +DRVNAME="vboxguest" +DRIVERS_USING_IT="vboxfs" + +DRVFILE=`dirname "$0"` +DRVFILE=`cd "$DRVFILE" && pwd` +DRVFILE="$DRVFILE/$DRVNAME" +if [ ! -f "$DRVFILE" ]; then + echo "load.sh: Cannot find $DRVFILE or it's not a file..." + exit 1; +fi + +SUDO=sudo +#set -x + +# Unload driver that may depend on the driver we're going to (re-)load +# as well as the driver itself. +for drv in $DRIVERS_USING_IT $DRVNAME; +do + LOADED=`modinfo | grep -w "$drv"` + if test -n "$LOADED"; then + MODID=`echo "$LOADED" | cut -d ' ' -f 1` + $SUDO modunload -i $MODID; + LOADED=`modinfo | grep -w "$drv"`; + if test -n "$LOADED"; then + echo "load.sh: failed to unload $drv"; + dmesg | tail + exit 1; + fi + fi +done + +# +# Update the devlink.tab file so we get a /dev/vboxguest node. +# +set -e +sed -e '/name=vboxguest/d' /etc/devlink.tab > /tmp/devlink.vbox +echo -e "type=ddi_pseudo;name=vboxguest\t\D" >> /tmp/devlink.vbox +$SUDO cp /tmp/devlink.vbox /etc/devlink.tab +$SUDO ln -fs ../devices/pci@0,0/pci80ee,cafe@4:vboxguest /dev/vboxguest +set +e + +# +# The add_drv command will load the driver, so we need to temporarily put it +# in a place that is searched in order to load it. +# +MY_RC=1 +set -e +$SUDO rm -f \ + "/usr/kernel/drv/${DRVNAME}" \ + "/usr/kernel/drv/amd64/${DRVNAME}" +sync +$SUDO cp "${DRVFILE}" /platform/i86pc/kernel/drv/amd64/ +set +e + +$SUDO rem_drv $DRVNAME +if $SUDO add_drv -ipci80ee,cafe -m"* 0666 root sys" -v $DRVNAME; then + sync + $SUDO /usr/sbin/devfsadm -i $DRVNAME + MY_RC=0 +else + dmesg | tail + echo "load.sh: add_drv failed." +fi + +$SUDO rm -f \ + "/usr/kernel/drv/${DRVNAME}" \ + "/usr/kernel/drv/amd64/${DRVNAME}" +sync + +exit $MY_RC; + diff --git a/src/VBox/Additions/common/VBoxGuest/win/Makefile.kup b/src/VBox/Additions/common/VBoxGuest/win/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/win/Makefile.kup diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf new file mode 100644 index 00000000..257af0fa --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf @@ -0,0 +1,100 @@ +; $Id: VBoxGuest.inf $ +;; @file +; INF file for installing the VirtualBox Windows guest driver. +; + +; +; Copyright (C) 2006-2022 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox 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. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + +[Version] +Signature="$WINDOWS NT$" +Provider=%ORACLE% +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Class=System +DriverPackageType=PlugAndPlay +;edit-DriverVer=08/26/2008,2.00.0000 +;cat CatalogFile=VBoxGuest.cat + +[SourceDisksNames] +1 = %VBoxGuest.MediaDesc% + +[SourceDisksFiles] +VBoxGuest.sys = 1 +VBoxControl.exe = 1 +VBoxTray.exe = 1 + +[DestinationDirs] +DefaultDestDir = 12 ; drivers +VBoxTray_CopyFiles = 11 ; system32 + +[Manufacturer] +;x86 %ORACLE%=VBoxGuest +;amd64 %ORACLE%=VBoxGuest, NTamd64 + +;x86 [VBoxGuest] +;amd64 [VBoxGuest.NTamd64] +%VBoxGuest.DeviceDesc%=VBoxGuest_Install,PCI\VEN_80ee&DEV_cafe + +[VBoxGuest_Install] +CopyFiles = VBoxGuest_CopyFiles, VBoxTray_CopyFiles +AddReg = VBoxTray_Add_Reg + +[VBoxGuest_CopyFiles] +VBoxGuest.sys + +[VBoxTray_CopyFiles] +VBoxTray.exe +VBoxControl.exe + +[VBoxGuest_Install.Services] +AddService = VBoxGuest, 0x00000002, VBoxGuest_ServiceInstallSection +DelService = VBoxTray, 0x00000004 + +[VBoxGuest_ServiceInstallSection] +DisplayName = %VBoxGuest_svcdesc% +ServiceType = 0x00000001 ; kernel driver +StartType = 0x00000000 ; boot start +ErrorControl = 0x00000001 ; normal error handling +LoadOrderGroup = Base +ServiceBinary = %12%\VBoxGuest.sys + +[VBoxTray_Add_Reg] +HKLM, SOFTWARE\Microsoft\Windows\CurrentVersion\Run, VBoxTray, 0x00020000, %%SystemRoot%%\system32\VBoxTray.exe + +[ClassInstall32] +; This should fix the error 0xe0000101 (The required section was not found in the INF). + +[Strings] +ORACLE = "Oracle Corporation" +VBoxGuest.DeviceDesc = "VirtualBox Guest Device" +VBoxGuest_svcdesc = "VirtualBox Guest Driver" +VBoxTray_svcdesc = "VirtualBox Guest Tray" +VBoxGuest.MediaDesc = "VirtualBox Guest Driver Installation Disk" diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc new file mode 100644 index 00000000..3008c1f5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc @@ -0,0 +1,72 @@ +/* $Id: VBoxGuest.rc $ */ +/** @file + * VBoxGuest - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "VirtualBox Guest Driver\0" + VALUE "InternalName", "VBoxGuest\0" + VALUE "OriginalFilename", "VBoxGuest.sys\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_GA_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +// #include <VBoxGuestMsg.rc> diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp new file mode 100644 index 00000000..f1cf28fd --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp @@ -0,0 +1,227 @@ +/* $Id: VBoxGuestInst.cpp $ */ +/** @file + * Small tool to (un)install the VBoxGuest device driver (for testing). + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/win/windows.h> + +#include <VBox/VBoxGuest.h> /* for VBOXGUEST_SERVICE_NAME */ +#include <iprt/errcore.h> +#include <iprt/message.h> +#include <iprt/stream.h> +#include <iprt/utf16.h> + + + + +static RTEXITCODE installDriver(bool fStartIt) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + SC_HANDLE hSMgrCreate = OpenSCManagerW(NULL, NULL, SERVICE_CHANGE_CONFIG); + if (!hSMgrCreate) + return RTMsgErrorExitFailure("OpenSCManager(,,create) failed: %u", GetLastError()); + + const wchar_t *pwszSlashName = L"\\VBoxGuest.sys"; + wchar_t wszDriver[MAX_PATH * 2]; + GetCurrentDirectoryW(MAX_PATH, wszDriver); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), pwszSlashName); + if (GetFileAttributesW(wszDriver) == INVALID_FILE_ATTRIBUTES) + { + GetSystemDirectoryW(wszDriver, MAX_PATH); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), L"\\drivers"); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), pwszSlashName); + + /* Try FAT name abbreviation. */ + if (GetFileAttributesW(wszDriver) == INVALID_FILE_ATTRIBUTES) + { + pwszSlashName = L"\\VBoxGst.sys"; + GetCurrentDirectoryW(MAX_PATH, wszDriver); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), pwszSlashName); + if (GetFileAttributesW(wszDriver) == INVALID_FILE_ATTRIBUTES) + { + GetSystemDirectoryW(wszDriver, MAX_PATH); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), L"\\drivers"); + RTUtf16Cat(wszDriver, RT_ELEMENTS(wszDriver), pwszSlashName); + } + } + } + + RTEXITCODE rcExit; + SC_HANDLE hService = CreateServiceW(hSMgrCreate, + RT_CONCAT(L,VBOXGUEST_SERVICE_NAME), + L"VBoxGuest Support Driver", + SERVICE_QUERY_STATUS | (fStartIt ? SERVICE_START : 0), + SERVICE_KERNEL_DRIVER, + SERVICE_BOOT_START, + SERVICE_ERROR_NORMAL, + wszDriver, + L"System", + NULL, NULL, NULL, NULL); + if (hService) + { + RTMsgInfo("Successfully created service '%s' for driver '%ls'.\n", VBOXGUEST_SERVICE_NAME, wszDriver); + rcExit = RTEXITCODE_SUCCESS; + if (fStartIt) + { + if (StartService(hService, 0, NULL)) + RTMsgInfo("successfully started driver '%ls'\n", wszDriver); + else + rcExit = RTMsgErrorExitFailure("StartService failed: %u", GetLastError()); + } + CloseServiceHandle(hService); + } + else + rcExit = RTMsgErrorExitFailure("CreateService failed! %u (wszDriver=%ls)\n", GetLastError(), wszDriver); + CloseServiceHandle(hSMgrCreate); + return rcExit; +} + + +static RTEXITCODE uninstallDriver(void) +{ + SC_HANDLE hSMgr = OpenSCManagerW(NULL, NULL, SERVICE_CHANGE_CONFIG); + if (!hSMgr) + return RTMsgErrorExitFailure("OpenSCManager(,,change_config) failed: %u", GetLastError()); + + RTEXITCODE rcExit; + SC_HANDLE hService = OpenServiceW(hSMgr, RT_CONCAT(L, VBOXGUEST_SERVICE_NAME), SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); + if (hService) + { + /* + * Try stop it if it's running. + */ + SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 }; + QueryServiceStatus(hService, &Status); + if (Status.dwCurrentState == SERVICE_STOPPED) + rcExit = RTEXITCODE_SUCCESS; + 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) + rcExit = RTEXITCODE_SUCCESS; + else + rcExit = RTMsgErrorExitFailure("Failed to stop service! Service status: %u (%#x)\n", + Status.dwCurrentState, Status.dwCurrentState); + } + else + rcExit = RTMsgErrorExitFailure("ControlService failed: %u, Service status: %u (%#x)", + GetLastError(), Status.dwCurrentState, Status.dwCurrentState); + + /* + * Delete the service. + */ + if (rcExit == RTEXITCODE_SUCCESS) + { + if (DeleteService(hService)) + RTMsgInfo("Successfully deleted the %s service\n", VBOXGUEST_SERVICE_NAME); + else + rcExit = RTMsgErrorExitFailure("DeleteService failed: %u", GetLastError()); + } + + CloseServiceHandle(hService); + } + else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + { + RTMsgInfo("Nothing to do, the service %s does not exist.\n", VBOXGUEST_SERVICE_NAME); + rcExit = RTEXITCODE_SUCCESS; + } + else + rcExit = RTMsgErrorExitFailure("OpenService failed: %u", GetLastError()); + + CloseServiceHandle(hSMgr); + return rcExit; +} + + +static RTEXITCODE performTest(void) +{ + HANDLE hDevice = CreateFileW(RT_CONCAT(L,VBOXGUEST_DEVICE_NAME), // Win2k+: VBOXGUEST_DEVICE_NAME_GLOBAL + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hDevice != INVALID_HANDLE_VALUE) + { + CloseHandle(hDevice); + RTMsgInfo("Test succeeded\n"); + return RTEXITCODE_SUCCESS; + } + return RTMsgErrorExitFailure("Test failed! Unable to open driver (CreateFileW -> %u).", GetLastError()); +} + + +static RTEXITCODE usage(const char *pszProgName) +{ + RTPrintf("\n" + "Usage: %s [install|uninstall|test]\n", pszProgName); + return RTEXITCODE_SYNTAX; +} + + +int main(int argc, char **argv) +{ + if (argc != 2) + { + RTMsgError(argc < 2 ? "Too few arguments! Expected one." : "Too many arguments! Expected only one."); + return usage(argv[0]); + } + + RTEXITCODE rcExit; + if (strcmp(argv[1], "install") == 0) + rcExit = installDriver(true); + else if (strcmp(argv[1], "uninstall") == 0) + rcExit = uninstallDriver(); + else if (strcmp(argv[1], "test") == 0) + rcExit = performTest(); + else + { + RTMsgError("Unknown argument: '%s'", argv[1]); + rcExit = usage(argv[0]); + } + return rcExit; +} + |