summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common/VBoxGuest
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest')
-rw-r--r--src/VBox/Additions/common/VBoxGuest/.scm-settings65
-rw-r--r--src/VBox/Additions/common/VBoxGuest/Makefile.kmk287
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c446
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp1393
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c799
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c471
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c588
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h248
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c1470
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c1094
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp698
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def55
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c1138
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf41
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp3481
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp4516
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm1679
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h415
-rw-r--r--src/VBox/Additions/common/VBoxGuest/darwin/Info.plist51
-rw-r--r--src/VBox/Additions/common/VBoxGuest/freebsd/Makefile202
-rw-r--r--src/VBox/Additions/common/VBoxGuest/freebsd/Makefile.kup0
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest240
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk262
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp134
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp183
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp240
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp1175
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp85
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp97
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp64
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp208
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp205
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp333
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h212
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp130
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp1197
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c716
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp51
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp485
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp363
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp130
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp83
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp2609
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp56
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp133
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp222
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp264
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp1948
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp199
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp103
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp90
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp2214
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp1032
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp119
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp235
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp226
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h129
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp94
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp135
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp180
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp90
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp118
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp209
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp432
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp79
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp55
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp618
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp64
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp52
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk52
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp415
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/Makefile213
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/combined-agnostic.c189
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/combined-os-specific.c61
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/linux/files_vboxguest237
-rw-r--r--src/VBox/Additions/common/VBoxGuest/netbsd/locators.h8
-rw-r--r--src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf66
-rw-r--r--src/VBox/Additions/common/VBoxGuest/solaris/deps.asm48
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/solaris/load.sh108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf100
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc72
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp227
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;
+}
+