summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Additions/common
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/common')
-rw-r--r--src/VBox/Additions/common/Makefile.kmk34
-rw-r--r--src/VBox/Additions/common/VBoxControl/Makefile.kmk56
-rw-r--r--src/VBox/Additions/common/VBoxControl/VBoxControl.cpp2192
-rw-r--r--src/VBox/Additions/common/VBoxControl/VBoxControl.rc51
-rw-r--r--src/VBox/Additions/common/VBoxControl/testcase/Makefile.kmk38
-rw-r--r--src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp213
-rw-r--r--src/VBox/Additions/common/VBoxGuest/.scm-settings55
-rw-r--r--src/VBox/Additions/common/VBoxGuest/Makefile.kmk269
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c436
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp1374
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c789
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c461
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c578
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h238
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c1307
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c1033
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp688
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def45
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c1129
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf31
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp3470
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp4507
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm1669
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h405
-rw-r--r--src/VBox/Additions/common/VBoxGuest/darwin/Info.plist51
-rw-r--r--src/VBox/Additions/common/VBoxGuest/freebsd/Makefile185
-rw-r--r--src/VBox/Additions/common/VBoxGuest/freebsd/Makefile.kup0
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest221
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk237
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp134
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp185
-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.cpp192
-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.h202
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp130
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp644
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c704
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp51
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp475
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp353
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp120
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp73
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp180
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp46
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp123
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp212
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp254
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp1898
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp93
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp80
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp1531
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp924
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp109
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp95
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp225
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp201
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h119
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp84
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp125
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp170
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp80
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp98
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp172
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp422
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp69
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp45
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp575
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp54
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp52
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/Makefile168
-rw-r--r--src/VBox/Additions/common/VBoxGuest/linux/Makefile.kup0
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/linux/files_vboxguest212
-rw-r--r--src/VBox/Additions/common/VBoxGuest/netbsd/locators.h8
-rw-r--r--src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf56
-rw-r--r--src/VBox/Additions/common/VBoxGuest/solaris/deps.asm38
-rwxr-xr-xsrc/VBox/Additions/common/VBoxGuest/solaris/load.sh98
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf92
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc62
-rw-r--r--src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp269
-rw-r--r--src/VBox/Additions/common/VBoxService/Makefile.kmk206
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService-os2.def23
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService-win.cpp660
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService-win.rc55
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService.cpp1270
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp2182
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp447
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp1129
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp573
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceControl.h334
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp2129
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp2453
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp644
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h252
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp790
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp429
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h56
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h27
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp736
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp815
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp1763
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.h32
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp401
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h40
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp1388
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp1689
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h32
-rw-r--r--src/VBox/Additions/common/VBoxService/testcase/Makefile.kmk34
-rw-r--r--src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp76
-rw-r--r--src/VBox/Additions/common/VBoxVideo/.scm-settings33
-rw-r--r--src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp300
-rw-r--r--src/VBox/Additions/common/VBoxVideo/HGSMIBuffers.cpp124
-rw-r--r--src/VBox/Additions/common/VBoxVideo/HGSMIHostCmd.cpp245
-rw-r--r--src/VBox/Additions/common/VBoxVideo/Modesetting.cpp419
-rw-r--r--src/VBox/Additions/common/VBoxVideo/VBVABase.cpp378
-rw-r--r--src/VBox/Additions/common/VBoxVideo/todo-create-library-from-these-files-for-windows0
-rw-r--r--src/VBox/Additions/common/crOpenGL/.scm-settings62
-rw-r--r--src/VBox/Additions/common/crOpenGL/AIX_exports.py11
-rw-r--r--src/VBox/Additions/common/crOpenGL/COPYRIGHT.LLNL61
-rw-r--r--src/VBox/Additions/common/crOpenGL/COPYRIGHT.REDHAT29
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/DD_glc.py139
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/DD_glh.py136
-rw-r--r--src/VBox/Additions/common/crOpenGL/Darwin_exports.py12
-rw-r--r--src/VBox/Additions/common/crOpenGL/FreeBSD_exports.py11
-rw-r--r--src/VBox/Additions/common/crOpenGL/IRIX64_exports.py11
-rw-r--r--src/VBox/Additions/common/crOpenGL/LICENSE32
-rw-r--r--src/VBox/Additions/common/crOpenGL/Linux_exports.py10
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/Linux_i386_exports.py96
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/Linux_i386_exports_dri.py96
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py109
-rw-r--r--src/VBox/Additions/common/crOpenGL/Makefile.kmk734
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/NULLfuncs.py58
-rw-r--r--src/VBox/Additions/common/crOpenGL/OSF1_exports.py11
-rw-r--r--src/VBox/Additions/common/crOpenGL/SunOS_exports.py11
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py98
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py98
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py103
-rw-r--r--src/VBox/Additions/common/crOpenGL/VBoxCROGL.rc70
-rw-r--r--src/VBox/Additions/common/crOpenGL/VBoxICDList.h385
-rw-r--r--src/VBox/Additions/common/crOpenGL/alias_exports.py168
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu.c944
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu.def6
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu.h46
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu.rc69
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu_config.c27
-rw-r--r--src/VBox/Additions/common/crOpenGL/array/arrayspu_init.c86
-rw-r--r--src/VBox/Additions/common/crOpenGL/context.c1463
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/cr_gl.py65
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/defs.py505
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/defs64.py485
-rw-r--r--src/VBox/Additions/common/crOpenGL/dri_drv.c1012
-rw-r--r--src/VBox/Additions/common/crOpenGL/dri_drv.h32
-rw-r--r--src/VBox/Additions/common/crOpenGL/dri_glx.h165
-rw-r--r--src/VBox/Additions/common/crOpenGL/dri_util.c1117
-rw-r--r--src/VBox/Additions/common/crOpenGL/drirenderbuffer.c215
-rw-r--r--src/VBox/Additions/common/crOpenGL/egl.c967
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/entrypoints.py180
-rw-r--r--src/VBox/Additions/common/crOpenGL/fakedri_drv.c878
-rw-r--r--src/VBox/Additions/common/crOpenGL/fakedri_drv.h123
-rw-r--r--src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h730
-rw-r--r--src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h93
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedback.def6
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/feedback/feedback.py270
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c131
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/feedback/feedback_funcs.py40
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedback_special64
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/feedback/feedback_state.py34
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedback_state_special66
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.h58
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.rc69
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_config.c44
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_init.c86
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/feedback/feedbackspu_proto.py35
-rw-r--r--src/VBox/Additions/common/crOpenGL/feedback/select_special56
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/getprocaddress.py125
-rw-r--r--src/VBox/Additions/common/crOpenGL/glx.c2113
-rw-r--r--src/VBox/Additions/common/crOpenGL/glx_c_exports.c375
-rw-r--r--src/VBox/Additions/common/crOpenGL/glx_proto.h149
-rw-r--r--src/VBox/Additions/common/crOpenGL/icd_drv.c398
-rw-r--r--src/VBox/Additions/common/crOpenGL/icd_drv.h55
-rw-r--r--src/VBox/Additions/common/crOpenGL/load.c1526
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/pack.def6
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/pack/pack.py57
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu.h185
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu.rc69
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/pack/packspu_beginend.py177
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c160
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_client.c910
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_config.c60
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_context.c617
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/pack/packspu_flush.py42
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_flush_special9
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c143
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/pack/packspu_get.py250
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c214
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_getstring.c211
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c275
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_init.c152
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c856
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_net.c284
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_pixel.c727
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/pack/packspu_proto.py50
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_special138
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_swapbuf.c102
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c70
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_unimplemented_special4
-rw-r--r--src/VBox/Additions/common/crOpenGL/pack/packspu_vertex_special28
-rw-r--r--src/VBox/Additions/common/crOpenGL/passthrough/Makefile.kup0
-rw-r--r--src/VBox/Additions/common/crOpenGL/passthrough/passthrough.def6
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/passthrough/passthrough.py40
-rw-r--r--src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.h21
-rw-r--r--src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.rc69
-rw-r--r--src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu_init.c64
-rw-r--r--src/VBox/Additions/common/crOpenGL/stub.c548
-rw-r--r--src/VBox/Additions/common/crOpenGL/stub.h370
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/stub_common.py282
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/tsfuncs.py47
-rw-r--r--src/VBox/Additions/common/crOpenGL/utils.c876
-rw-r--r--src/VBox/Additions/common/crOpenGL/vboxdri_drv.c694
-rw-r--r--src/VBox/Additions/common/crOpenGL/wgl.c1015
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/windows_exports.py98
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/windows_getprocaddress.py171
-rwxr-xr-xsrc/VBox/Additions/common/crOpenGL/windows_i386_exports.py95
-rw-r--r--src/VBox/Additions/common/crOpenGL/xfont.c244
-rw-r--r--src/VBox/Additions/common/pam/Makefile.kmk35
-rw-r--r--src/VBox/Additions/common/pam/pam_vbox.cpp898
-rw-r--r--src/VBox/Additions/common/testcase/Makefile.kmk35
-rw-r--r--src/VBox/Additions/common/testcase/tstPageFusion.cpp379
238 files changed, 87777 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/Makefile.kmk b/src/VBox/Additions/common/Makefile.kmk
new file mode 100644
index 00000000..94f99f26
--- /dev/null
+++ b/src/VBox/Additions/common/Makefile.kmk
@@ -0,0 +1,34 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the common addition code.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile.
+ifndef VBOX_ONLY_VALIDATIONKIT
+ include $(PATH_SUB_CURRENT)/VBoxControl/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/VBoxGuest/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/VBoxService/Makefile.kmk
+ ifdef VBOX_WITH_CROGL
+ include $(PATH_SUB_CURRENT)/crOpenGL/Makefile.kmk
+ endif
+ ifdef VBOX_WITH_PAM
+ include $(PATH_SUB_CURRENT)/pam/Makefile.kmk
+ endif
+endif # !VBOX_ONLY_VALIDATIONKIT
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/Additions/common/VBoxControl/Makefile.kmk b/src/VBox/Additions/common/VBoxControl/Makefile.kmk
new file mode 100644
index 00000000..f0d60036
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxControl/Makefile.kmk
@@ -0,0 +1,56 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Additions Command Line Management Interface.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# VBoxControl
+#
+PROGRAMS += VBoxControl
+if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Exe everywhere
+VBoxControl_TEMPLATE = VBoxGuestR3Exe
+else
+VBoxControl_TEMPLATE = NewVBoxGuestR3Exe
+endif
+ifeq ($(KBUILD_TARGET),win)
+ ifdef VBOX_SIGN_ADDITIONS # (See the main Windows Additions makefile.)
+ VBoxControl_INSTTYPE = none
+ VBoxControl_DEBUG_INSTTYPE = both
+ endif
+endif
+VBoxControl_DEFS += \
+ $(if $(VBOX_WITH_HGCM),VBOX_WITH_HGCM,) \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_WITH_SHARED_FOLDERS),VBOX_WITH_SHARED_FOLDERS,)
+VBoxControl_DEFS.win += \
+ $(if $(VBOX_WITH_DPC_LATENCY_CHECKER),VBOX_WITH_DPC_LATENCY_CHECKER,)
+VBoxControl_SDKS = VBOX_ZLIB_STATIC
+VBoxControl_SOURCES = \
+ VBoxControl.cpp
+VBoxControl_SOURCES.win = \
+ VBoxControl.rc
+VBoxControl_LDFLAGS.darwin = -framework IOKit
+VBoxControl_USES.win += vboximportchecker
+VBoxControl_VBOX_IMPORT_CHECKER.win.x86 = nt31
+VBoxControl_VBOX_IMPORT_CHECKER.win.amd64 = xp64
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp b/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp
new file mode 100644
index 00000000..b28e0282
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp
@@ -0,0 +1,2192 @@
+/* $Id: VBoxControl.cpp $ */
+/** @file
+ * VBoxControl - Guest Additions Command Line Management Interface.
+ */
+
+/*
+ * Copyright (C) 2008-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/alloca.h>
+#include <iprt/cpp/autores.h>
+#include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/zip.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <VBox/VBoxGuestLib.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#endif
+#ifdef VBOX_WITH_GUEST_PROPS
+# include <VBox/HostServices/GuestPropertySvc.h>
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+# include <VBox/shflsvc.h>
+# ifdef RT_OS_OS2
+# define OS2EMX_PLAIN_CHAR
+# define INCL_ERRORS
+# define INCL_DOSFILEMGR
+# include <os2emx.h>
+# endif
+#endif
+#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
+# include <VBox/VBoxGuest.h>
+# include "../VBoxGuest/lib/VBoxGuestR3LibInternal.h" /* HACK ALERT! Using vbglR3DoIOCtl directly!! */
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The program name (derived from argv[0]). */
+char const *g_pszProgName = "";
+/** The current verbosity level. */
+int g_cVerbosity = 0;
+
+
+/** @name Displays the program usage message.
+ * @{
+ */
+
+/**
+ * Helper function that does indentation.
+ *
+ * @param pszLine Text.
+ * @param pszName Program name.
+ * @param pszCommand Command/option syntax.
+ */
+static void doUsage(char const *pszLine, char const *pszName = "", char const *pszCommand = "")
+{
+ /* Allow for up to 15 characters command name length (VBoxControl.exe) with
+ * perfect column alignment. Beyond that there's at least one space between
+ * the command if there are command line parameters. */
+ RTPrintf("%s %-*s%s%s\n",
+ pszName,
+ *pszLine ? 35 - strlen(pszName) : 1, pszCommand,
+ *pszLine ? " " : "", pszLine);
+}
+
+/** Enumerate the different parts of the usage we might want to print out */
+enum VBoxControlUsage
+{
+#ifdef RT_OS_WINDOWS
+ GET_VIDEO_ACCEL,
+ SET_VIDEO_ACCEL,
+ VIDEO_FLAGS,
+ LIST_CUST_MODES,
+ ADD_CUST_MODE,
+ REMOVE_CUST_MODE,
+ SET_VIDEO_MODE,
+#endif
+#ifdef VBOX_WITH_GUEST_PROPS
+ GUEST_PROP,
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+ GUEST_SHAREDFOLDERS,
+#endif
+#if !defined(VBOX_CONTROL_TEST)
+ WRITE_CORE_DUMP,
+#endif
+ WRITE_LOG,
+ TAKE_SNAPSHOT,
+ SAVE_STATE,
+ SUSPEND,
+ POWER_OFF,
+ VERSION,
+ HELP,
+ USAGE_ALL = UINT32_MAX
+};
+
+static RTEXITCODE usage(enum VBoxControlUsage eWhich = USAGE_ALL)
+{
+ RTPrintf("Usage:\n\n");
+ doUsage("print version number and exit", g_pszProgName, "[-V|--version]");
+ doUsage("suppress the logo", g_pszProgName, "--nologo ...");
+ RTPrintf("\n");
+
+ /* Exclude the Windows bits from the test version. Anyone who needs to
+ test them can fix this. */
+#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
+ if (eWhich == GET_VIDEO_ACCEL || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "getvideoacceleration");
+ if (eWhich == SET_VIDEO_ACCEL || eWhich == USAGE_ALL)
+ doUsage("<on|off>", g_pszProgName, "setvideoacceleration");
+ if (eWhich == VIDEO_FLAGS || eWhich == USAGE_ALL)
+ doUsage("<get|set|clear|delete> [hex mask]", g_pszProgName, "videoflags");
+ if (eWhich == LIST_CUST_MODES || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "listcustommodes");
+ if (eWhich == ADD_CUST_MODE || eWhich == USAGE_ALL)
+ doUsage("<width> <height> <bpp>", g_pszProgName, "addcustommode");
+ if (eWhich == REMOVE_CUST_MODE || eWhich == USAGE_ALL)
+ doUsage("<width> <height> <bpp>", g_pszProgName, "removecustommode");
+ if (eWhich == SET_VIDEO_MODE || eWhich == USAGE_ALL)
+ doUsage("<width> <height> <bpp> <screen>", g_pszProgName, "setvideomode");
+#endif
+#ifdef VBOX_WITH_GUEST_PROPS
+ if (eWhich == GUEST_PROP || eWhich == USAGE_ALL)
+ {
+ doUsage("get <property> [--verbose]", g_pszProgName, "guestproperty");
+ doUsage("set <property> [<value> [--flags <flags>]]", g_pszProgName, "guestproperty");
+ doUsage("delete|unset <property>", g_pszProgName, "guestproperty");
+ doUsage("enumerate [--patterns <patterns>]", g_pszProgName, "guestproperty");
+ doUsage("wait <patterns>", g_pszProgName, "guestproperty");
+ doUsage("[--timestamp <last timestamp>]");
+ doUsage("[--timeout <timeout in ms>");
+ }
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+ if (eWhich == GUEST_SHAREDFOLDERS || eWhich == USAGE_ALL)
+ {
+ doUsage("list [--automount]", g_pszProgName, "sharedfolder");
+# ifdef RT_OS_OS2
+ doUsage("use <drive> <folder>", g_pszProgName, "sharedfolder");
+ doUsage("unuse <drive>", g_pszProgName, "sharedfolder");
+# endif
+ }
+#endif
+
+#if !defined(VBOX_CONTROL_TEST)
+ if (eWhich == WRITE_CORE_DUMP || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "writecoredump");
+#endif
+ if (eWhich == WRITE_LOG || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "writelog [-n|--no-newline] [--] <msg>");
+ if (eWhich == TAKE_SNAPSHOT || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "takesnapshot");
+ if (eWhich == SAVE_STATE || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "savestate");
+ if (eWhich == SUSPEND || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "suspend");
+ if (eWhich == POWER_OFF || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "poweroff");
+ if (eWhich == HELP || eWhich == USAGE_ALL)
+ doUsage("[command]", g_pszProgName, "help");
+ if (eWhich == VERSION || eWhich == USAGE_ALL)
+ doUsage("", g_pszProgName, "version");
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/** @} */
+
+
+/**
+ * Implementation of the '--version' option.
+ *
+ * @returns RTEXITCODE_SUCCESS
+ */
+static RTEXITCODE printVersion(void)
+{
+ RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Displays an error message.
+ *
+ * @returns RTEXITCODE_FAILURE.
+ * @param pszFormat The message text. No newline.
+ * @param ... Format arguments.
+ */
+static RTEXITCODE VBoxControlError(const char *pszFormat, ...)
+{
+ /** @todo prefix with current command. */
+ va_list va;
+ va_start(va, pszFormat);
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Displays a getopt error.
+ *
+ * @returns RTEXITCODE_FAILURE.
+ * @param ch The RTGetOpt return value.
+ * @param pValueUnion The RTGetOpt return data.
+ */
+static RTEXITCODE VBoxCtrlGetOptError(int ch, PCRTGETOPTUNION pValueUnion)
+{
+ /** @todo prefix with current command. */
+ return RTGetOptPrintError(ch, pValueUnion);
+}
+
+
+/**
+ * Displays an syntax error message.
+ *
+ * @returns RTEXITCODE_FAILURE.
+ * @param pszFormat The message text. No newline.
+ * @param ... Format arguments.
+ */
+static RTEXITCODE VBoxControlSyntaxError(const char *pszFormat, ...)
+{
+ /** @todo prefix with current command. */
+ va_list va;
+ va_start(va, pszFormat);
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+ return RTEXITCODE_SYNTAX;
+}
+
+#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
+
+decltype(ChangeDisplaySettingsExA) *g_pfnChangeDisplaySettingsExA;
+decltype(ChangeDisplaySettings) *g_pfnChangeDisplaySettingsA;
+decltype(EnumDisplaySettingsA) *g_pfnEnumDisplaySettingsA;
+
+static unsigned nextAdjacentRectXP(RECTL const *paRects, unsigned cRects, unsigned iRect)
+{
+ for (unsigned i = 0; i < cRects; i++)
+ if (paRects[iRect].right == paRects[i].left)
+ return i;
+ return ~0U;
+}
+
+static unsigned nextAdjacentRectXN(RECTL const *paRects, unsigned cRects, unsigned iRect)
+{
+ for (unsigned i = 0; i < cRects; i++)
+ if (paRects[iRect].left == paRects[i].right)
+ return i;
+ return ~0U;
+}
+
+static unsigned nextAdjacentRectYP(RECTL const *paRects, unsigned cRects, unsigned iRect)
+{
+ for (unsigned i = 0; i < cRects; i++)
+ if (paRects[iRect].bottom == paRects[i].top)
+ return i;
+ return ~0U;
+}
+
+unsigned nextAdjacentRectYN(RECTL const *paRects, unsigned cRects, unsigned iRect)
+{
+ for (unsigned i = 0; i < cRects; i++)
+ if (paRects[iRect].top == paRects[i].bottom)
+ return i;
+ return ~0U;
+}
+
+void resizeRect(RECTL *paRects, unsigned cRects, unsigned iPrimary, unsigned iResized, int NewWidth, int NewHeight)
+{
+ RECTL *paNewRects = (RECTL *)alloca(sizeof (RECTL) * cRects);
+ memcpy (paNewRects, paRects, sizeof(RECTL) * cRects);
+ paNewRects[iResized].right += NewWidth - (paNewRects[iResized].right - paNewRects[iResized].left);
+ paNewRects[iResized].bottom += NewHeight - (paNewRects[iResized].bottom - paNewRects[iResized].top);
+
+ /* Verify all pairs of originally adjacent rectangles for all 4 directions.
+ * If the pair has a "good" delta (that is the first rectangle intersects the second)
+ * at a direction and the second rectangle is not primary one (which can not be moved),
+ * move the second rectangle to make it adjacent to the first one.
+ */
+
+ /* X positive. */
+ unsigned iRect;
+ for (iRect = 0; iRect < cRects; iRect++)
+ {
+ /* Find the next adjacent original rect in x positive direction. */
+ unsigned iNextRect = nextAdjacentRectXP (paRects, cRects, iRect);
+ Log(("next %d -> %d\n", iRect, iNextRect));
+
+ if (iNextRect == ~0 || iNextRect == iPrimary)
+ {
+ continue;
+ }
+
+ /* Check whether there is an X intersection between these adjacent rects in the new rectangles
+ * and fix the intersection if delta is "good".
+ */
+ int delta = paNewRects[iRect].right - paNewRects[iNextRect].left;
+
+ if (delta > 0)
+ {
+ Log(("XP intersection right %d left %d, diff %d\n",
+ paNewRects[iRect].right, paNewRects[iNextRect].left,
+ delta));
+
+ paNewRects[iNextRect].left += delta;
+ paNewRects[iNextRect].right += delta;
+ }
+ }
+
+ /* X negative. */
+ for (iRect = 0; iRect < cRects; iRect++)
+ {
+ /* Find the next adjacent original rect in x negative direction. */
+ unsigned iNextRect = nextAdjacentRectXN (paRects, cRects, iRect);
+ Log(("next %d -> %d\n", iRect, iNextRect));
+
+ if (iNextRect == ~0 || iNextRect == iPrimary)
+ {
+ continue;
+ }
+
+ /* Check whether there is an X intersection between these adjacent rects in the new rectangles
+ * and fix the intersection if delta is "good".
+ */
+ int delta = paNewRects[iRect].left - paNewRects[iNextRect].right;
+
+ if (delta < 0)
+ {
+ Log(("XN intersection left %d right %d, diff %d\n",
+ paNewRects[iRect].left, paNewRects[iNextRect].right,
+ delta));
+
+ paNewRects[iNextRect].left += delta;
+ paNewRects[iNextRect].right += delta;
+ }
+ }
+
+ /* Y positive (in the computer sense, top->down). */
+ for (iRect = 0; iRect < cRects; iRect++)
+ {
+ /* Find the next adjacent original rect in y positive direction. */
+ unsigned iNextRect = nextAdjacentRectYP (paRects, cRects, iRect);
+ Log(("next %d -> %d\n", iRect, iNextRect));
+
+ if (iNextRect == ~0 || iNextRect == iPrimary)
+ {
+ continue;
+ }
+
+ /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
+ * and fix the intersection if delta is "good".
+ */
+ int delta = paNewRects[iRect].bottom - paNewRects[iNextRect].top;
+
+ if (delta > 0)
+ {
+ Log(("YP intersection bottom %d top %d, diff %d\n",
+ paNewRects[iRect].bottom, paNewRects[iNextRect].top,
+ delta));
+
+ paNewRects[iNextRect].top += delta;
+ paNewRects[iNextRect].bottom += delta;
+ }
+ }
+
+ /* Y negative (in the computer sense, down->top). */
+ for (iRect = 0; iRect < cRects; iRect++)
+ {
+ /* Find the next adjacent original rect in x negative direction. */
+ unsigned iNextRect = nextAdjacentRectYN (paRects, cRects, iRect);
+ Log(("next %d -> %d\n", iRect, iNextRect));
+
+ if (iNextRect == ~0 || iNextRect == iPrimary)
+ {
+ continue;
+ }
+
+ /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
+ * and fix the intersection if delta is "good".
+ */
+ int delta = paNewRects[iRect].top - paNewRects[iNextRect].bottom;
+
+ if (delta < 0)
+ {
+ Log(("YN intersection top %d bottom %d, diff %d\n",
+ paNewRects[iRect].top, paNewRects[iNextRect].bottom,
+ delta));
+
+ paNewRects[iNextRect].top += delta;
+ paNewRects[iNextRect].bottom += delta;
+ }
+ }
+
+ memcpy (paRects, paNewRects, sizeof (RECTL) * cRects);
+ return;
+}
+
+/* Returns TRUE to try again. */
+static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
+{
+ BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
+
+ DISPLAY_DEVICE DisplayDevice;
+ RT_ZERO(DisplayDevice);
+ DisplayDevice.cb = sizeof(DisplayDevice);
+
+ /* Find out how many display devices the system has */
+ DWORD NumDevices = 0;
+ DWORD i = 0;
+ while (EnumDisplayDevices(NULL, i, &DisplayDevice, 0))
+ {
+ Log(("[%d] %s\n", i, DisplayDevice.DeviceName));
+
+ if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ {
+ Log(("Found primary device. err %d\n", GetLastError()));
+ NumDevices++;
+ }
+ else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
+ {
+
+ Log(("Found secondary device. err %d\n", GetLastError()));
+ NumDevices++;
+ }
+
+ RT_ZERO(DisplayDevice);
+ DisplayDevice.cb = sizeof(DisplayDevice);
+ i++;
+ }
+
+ Log(("Found total %d devices. err %d\n", NumDevices, GetLastError()));
+
+ if (NumDevices == 0 || Id >= NumDevices)
+ {
+ Log(("Requested identifier %d is invalid. err %d\n", Id, GetLastError()));
+ return FALSE;
+ }
+
+ DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca(sizeof (DISPLAY_DEVICE) * NumDevices);
+ DEVMODE *paDeviceModes = (DEVMODE *)alloca(sizeof (DEVMODE) * NumDevices);
+ RECTL *paRects = (RECTL *)alloca(sizeof (RECTL) * NumDevices);
+
+ /* Fetch information about current devices and modes. */
+ DWORD DevNum = 0;
+ DWORD DevPrimaryNum = 0;
+
+ RT_ZERO(DisplayDevice);
+ DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
+
+ i = 0;
+ while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
+ {
+ Log(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
+
+ BOOL fFetchDevice = FALSE;
+
+ if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ {
+ Log(("Found primary device. err %d\n", GetLastError()));
+ DevPrimaryNum = DevNum;
+ fFetchDevice = TRUE;
+ }
+ else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
+ {
+
+ Log(("Found secondary device. err %d\n", GetLastError()));
+ fFetchDevice = TRUE;
+ }
+
+ if (fFetchDevice)
+ {
+ if (DevNum >= NumDevices)
+ {
+ Log(("%d >= %d\n", NumDevices, DevNum));
+ return FALSE;
+ }
+
+ paDisplayDevices[DevNum] = DisplayDevice;
+
+ RT_BZERO(&paDeviceModes[DevNum], sizeof(DEVMODE));
+ paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
+ if (!g_pfnEnumDisplaySettingsA((LPSTR)DisplayDevice.DeviceName, ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
+ {
+ Log(("EnumDisplaySettings err %d\n", GetLastError()));
+ return FALSE;
+ }
+
+ Log(("%dx%d at %d,%d\n",
+ paDeviceModes[DevNum].dmPelsWidth,
+ paDeviceModes[DevNum].dmPelsHeight,
+ paDeviceModes[DevNum].dmPosition.x,
+ paDeviceModes[DevNum].dmPosition.y));
+
+ paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
+ paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
+ paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
+ paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
+ DevNum++;
+ }
+
+ RT_ZERO(DisplayDevice);
+ DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
+ i++;
+ }
+
+ if (Width == 0)
+ Width = paRects[Id].right - paRects[Id].left;
+
+ if (Height == 0)
+ Height = paRects[Id].bottom - paRects[Id].top;
+
+ /* Check whether a mode reset or a change is requested. */
+ if ( !fModeReset
+ && paRects[Id].right - paRects[Id].left == (LONG)Width
+ && paRects[Id].bottom - paRects[Id].top == (LONG)Height
+ && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
+ {
+ Log(("VBoxDisplayThread : already at desired resolution.\n"));
+ return FALSE;
+ }
+
+ resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
+#ifdef LOG_ENABLED
+ for (i = 0; i < NumDevices; i++)
+ Log(("[%d]: %d,%d %dx%d\n",
+ i, paRects[i].left, paRects[i].top,
+ paRects[i].right - paRects[i].left,
+ paRects[i].bottom - paRects[i].top));
+#endif /* Log */
+
+ /* Without this, Windows will not ask the miniport for its
+ * mode table but uses an internal cache instead.
+ */
+ DEVMODE tempDevMode;
+ RT_ZERO(tempDevMode);
+ tempDevMode.dmSize = sizeof(DEVMODE);
+ g_pfnEnumDisplaySettingsA(NULL, 0xffffff, &tempDevMode);
+
+ /* Assign the new rectangles to displays. */
+ for (i = 0; i < NumDevices; i++)
+ {
+ paDeviceModes[i].dmPosition.x = paRects[i].left;
+ paDeviceModes[i].dmPosition.y = paRects[i].top;
+ paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
+ paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
+
+ paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
+
+ if ( i == Id
+ && BitsPerPixel != 0)
+ {
+ paDeviceModes[i].dmFields |= DM_BITSPERPEL;
+ paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
+ }
+ Log(("calling pfnChangeDisplaySettingsEx %p\n", g_pfnChangeDisplaySettingsExA));
+ g_pfnChangeDisplaySettingsExA((LPSTR)paDisplayDevices[i].DeviceName,
+ &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
+ Log(("ChangeDisplaySettingsEx position err %d\n", GetLastError()));
+ }
+
+ /* A second call to ChangeDisplaySettings updates the monitor. */
+ LONG status = g_pfnChangeDisplaySettingsA(NULL, 0);
+ Log(("ChangeDisplaySettings update status %d\n", status));
+ if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
+ {
+ /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
+ return FALSE;
+ }
+
+ /* Retry the request. */
+ return TRUE;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleSetVideoMode(int argc, char *argv[])
+{
+ if (argc != 3 && argc != 4)
+ {
+ usage(SET_VIDEO_MODE);
+ return RTEXITCODE_FAILURE;
+ }
+
+ DWORD xres = atoi(argv[0]);
+ DWORD yres = atoi(argv[1]);
+ DWORD bpp = atoi(argv[2]);
+ DWORD scr = 0;
+ if (argc == 4)
+ scr = atoi(argv[3]);
+
+ HMODULE hmodUser = GetModuleHandle("user32.dll");
+ if (hmodUser)
+ {
+ /* ChangeDisplaySettingsExA was probably added in W2K, whereas ChangeDisplaySettingsA
+ and EnumDisplaySettingsA was added in NT 3.51. */
+ g_pfnChangeDisplaySettingsExA = (decltype(g_pfnChangeDisplaySettingsExA))GetProcAddress(hmodUser, "ChangeDisplaySettingsExA");
+ g_pfnChangeDisplaySettingsA = (decltype(g_pfnChangeDisplaySettingsA)) GetProcAddress(hmodUser, "ChangeDisplaySettingsA");
+ g_pfnEnumDisplaySettingsA = (decltype(g_pfnEnumDisplaySettingsA)) GetProcAddress(hmodUser, "EnumDisplaySettingsA");
+
+ Log(("VBoxService: g_pfnChangeDisplaySettingsExA=%p g_pfnChangeDisplaySettingsA=%p g_pfnEnumDisplaySettingsA=%p\n",
+ g_pfnChangeDisplaySettingsExA, g_pfnChangeDisplaySettingsA, g_pfnEnumDisplaySettingsA));
+
+ if ( g_pfnChangeDisplaySettingsExA
+ && g_pfnChangeDisplaySettingsA
+ && g_pfnEnumDisplaySettingsA)
+ {
+ /* The screen index is 0 based in the ResizeDisplayDevice call. */
+ scr = scr > 0 ? scr - 1 : 0;
+
+ /* Horizontal resolution must be a multiple of 8, round down. */
+ xres &= ~0x7;
+
+ RTPrintf("Setting resolution of display %d to %dx%dx%d ...", scr, xres, yres, bpp);
+ ResizeDisplayDevice(scr, xres, yres, bpp);
+ RTPrintf("done.\n");
+ }
+ else
+ VBoxControlError("Error retrieving API for display change!");
+ }
+ else
+ VBoxControlError("Error retrieving handle to user32.dll!");
+
+ return RTEXITCODE_SUCCESS;
+}
+
+static int checkVBoxVideoKey(HKEY hkeyVideo)
+{
+ RTUTF16 wszValue[128];
+ DWORD cbValue = sizeof(wszValue);
+ DWORD dwKeyType;
+ LONG status = RegQueryValueExW(hkeyVideo, L"Device Description", NULL, &dwKeyType, (LPBYTE)wszValue, &cbValue);
+ if (status == ERROR_SUCCESS)
+ {
+ /* WDDM has additional chars after "Adapter" */
+ static char s_szDeviceDescription[] = "VirtualBox Graphics Adapter";
+ wszValue[sizeof(s_szDeviceDescription) - 1] = '\0';
+ if (RTUtf16ICmpAscii(wszValue, s_szDeviceDescription) == 0)
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+static HKEY getVideoKey(bool writable)
+{
+ HKEY hkeyDeviceMap = 0;
+ LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap);
+ if (status != ERROR_SUCCESS || !hkeyDeviceMap)
+ {
+ VBoxControlError("Error opening video device map registry key!\n");
+ return 0;
+ }
+
+ HKEY hkeyVideo = 0;
+ ULONG iDevice;
+ DWORD dwKeyType;
+
+ /*
+ * Scan all '\Device\VideoX' REG_SZ keys to find VBox video driver entry.
+ * 'ObjectNumberList' REG_BINARY is an array of 32 bit device indexes (X).
+ */
+
+ /* Get the 'ObjectNumberList' */
+ ULONG cDevices = 0;
+ DWORD adwObjectNumberList[256];
+ DWORD cbValue = sizeof(adwObjectNumberList);
+ status = RegQueryValueExA(hkeyDeviceMap, "ObjectNumberList", NULL, &dwKeyType, (LPBYTE)&adwObjectNumberList[0], &cbValue);
+
+ if ( status == ERROR_SUCCESS
+ && dwKeyType == REG_BINARY)
+ cDevices = cbValue / sizeof(DWORD);
+ else
+ {
+ /* The list might not exists. Use 'MaxObjectNumber' REG_DWORD and build a list. */
+ DWORD dwMaxObjectNumber = 0;
+ cbValue = sizeof(dwMaxObjectNumber);
+ status = RegQueryValueExA(hkeyDeviceMap, "MaxObjectNumber", NULL, &dwKeyType, (LPBYTE)&dwMaxObjectNumber, &cbValue);
+ if ( status == ERROR_SUCCESS
+ && dwKeyType == REG_DWORD)
+ {
+ /* 'MaxObjectNumber' is inclusive. */
+ cDevices = RT_MIN(dwMaxObjectNumber + 1, RT_ELEMENTS(adwObjectNumberList));
+ for (iDevice = 0; iDevice < cDevices; iDevice++)
+ adwObjectNumberList[iDevice] = iDevice;
+ }
+ }
+
+ if (cDevices == 0)
+ {
+ /* Always try '\Device\Video0' as the old code did. Enum can be used in this case in principle. */
+ adwObjectNumberList[0] = 0;
+ cDevices = 1;
+ }
+
+ /* Scan device entries */
+ for (iDevice = 0; iDevice < cDevices; iDevice++)
+ {
+ char szValueName[64];
+ RTStrPrintf(szValueName, sizeof(szValueName), "\\Device\\Video%u", adwObjectNumberList[iDevice]);
+
+ char szVideoLocation[256];
+ cbValue = sizeof(szVideoLocation);
+ status = RegQueryValueExA(hkeyDeviceMap, szValueName, NULL, &dwKeyType, (LPBYTE)&szVideoLocation[0], &cbValue);
+
+ /* This value starts with '\REGISTRY\Machine' */
+ if ( status == ERROR_SUCCESS
+ && dwKeyType == REG_SZ
+ && _strnicmp(szVideoLocation, "\\REGISTRY\\Machine", 17) == 0)
+ {
+ status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0,
+ KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo);
+ if (status == ERROR_SUCCESS)
+ {
+ int rc = checkVBoxVideoKey(hkeyVideo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Found, return hkeyVideo to the caller. */
+ break;
+ }
+
+ RegCloseKey(hkeyVideo);
+ hkeyVideo = 0;
+ }
+ }
+ }
+
+ if (hkeyVideo == 0)
+ {
+ VBoxControlError("Error opening video registry key!\n");
+ }
+
+ RegCloseKey(hkeyDeviceMap);
+ return hkeyVideo;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleGetVideoAcceleration(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv);
+ ULONG status;
+ HKEY hkeyVideo = getVideoKey(false);
+
+ if (hkeyVideo)
+ {
+ /* query the actual value */
+ DWORD fAcceleration = 1;
+ DWORD cbValue = sizeof(fAcceleration);
+ DWORD dwKeyType;
+ status = RegQueryValueExA(hkeyVideo, "EnableVideoAccel", NULL, &dwKeyType, (LPBYTE)&fAcceleration, &cbValue);
+ if (status != ERROR_SUCCESS)
+ RTPrintf("Video acceleration: default\n");
+ else
+ RTPrintf("Video acceleration: %s\n", fAcceleration ? "on" : "off");
+ RegCloseKey(hkeyVideo);
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleSetVideoAcceleration(int argc, char *argv[])
+{
+ ULONG status;
+ HKEY hkeyVideo;
+
+ /* must have exactly one argument: the new offset */
+ if ( (argc != 1)
+ || ( RTStrICmp(argv[0], "on")
+ && RTStrICmp(argv[0], "off")))
+ {
+ usage(SET_VIDEO_ACCEL);
+ return RTEXITCODE_FAILURE;
+ }
+
+ hkeyVideo = getVideoKey(true);
+
+ if (hkeyVideo)
+ {
+ int fAccel = 0;
+ if (RTStrICmp(argv[0], "on") == 0)
+ fAccel = 1;
+ /* set a new value */
+ status = RegSetValueExA(hkeyVideo, "EnableVideoAccel", 0, REG_DWORD, (LPBYTE)&fAccel, sizeof(fAccel));
+ if (status != ERROR_SUCCESS)
+ {
+ VBoxControlError("Error %d writing video acceleration status!\n", status);
+ }
+ RegCloseKey(hkeyVideo);
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) videoFlagsGet(void)
+{
+ HKEY hkeyVideo = getVideoKey(false);
+
+ if (hkeyVideo)
+ {
+ DWORD dwFlags = 0;
+ DWORD cbValue = sizeof(dwFlags);
+ DWORD dwKeyType;
+ ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &cbValue);
+ if (status != ERROR_SUCCESS)
+ RTPrintf("Video flags: default\n");
+ else
+ RTPrintf("Video flags: 0x%08X\n", dwFlags);
+ RegCloseKey(hkeyVideo);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ return RTEXITCODE_FAILURE;
+}
+
+static DECLCALLBACK(RTEXITCODE) videoFlagsDelete(void)
+{
+ HKEY hkeyVideo = getVideoKey(true);
+
+ if (hkeyVideo)
+ {
+ ULONG status = RegDeleteValueA(hkeyVideo, "VBoxVideoFlags");
+ if (status != ERROR_SUCCESS)
+ VBoxControlError("Error %d deleting video flags.\n", status);
+ RegCloseKey(hkeyVideo);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ return RTEXITCODE_FAILURE;
+}
+
+static DECLCALLBACK(RTEXITCODE) videoFlagsModify(bool fSet, int argc, char *argv[])
+{
+ if (argc != 1)
+ {
+ VBoxControlError("Mask required.\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ uint32_t u32Mask = 0;
+ int rc = RTStrToUInt32Full(argv[0], 16, &u32Mask);
+ if (RT_FAILURE(rc))
+ {
+ VBoxControlError("Invalid video flags mask.\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE exitCode = RTEXITCODE_SUCCESS;
+
+ HKEY hkeyVideo = getVideoKey(true);
+ if (hkeyVideo)
+ {
+ DWORD dwFlags = 0;
+ DWORD cbValue = sizeof(dwFlags);
+ DWORD dwKeyType;
+ ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &cbValue);
+ if (status != ERROR_SUCCESS)
+ dwFlags = 0;
+
+ dwFlags = fSet ? dwFlags | u32Mask : dwFlags & ~u32Mask;
+
+ status = RegSetValueExA(hkeyVideo, "VBoxVideoFlags", 0, REG_DWORD, (LPBYTE)&dwFlags, sizeof(dwFlags));
+ if (status != ERROR_SUCCESS)
+ {
+ VBoxControlError("Error %d writing video flags.\n", status);
+ exitCode = RTEXITCODE_FAILURE;
+ }
+
+ RegCloseKey(hkeyVideo);
+ }
+ else
+ {
+ exitCode = RTEXITCODE_FAILURE;
+ }
+
+ return exitCode;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleVideoFlags(int argc, char *argv[])
+{
+ /* Must have a keyword and optional value (32 bit hex string). */
+ if (argc != 1 && argc != 2)
+ {
+ VBoxControlError("Invalid number of arguments.\n");
+ usage(VIDEO_FLAGS);
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE exitCode = RTEXITCODE_SUCCESS;
+
+ if (RTStrICmp(argv[0], "get") == 0)
+ {
+ exitCode = videoFlagsGet();
+ }
+ else if (RTStrICmp(argv[0], "delete") == 0)
+ {
+ exitCode = videoFlagsDelete();
+ }
+ else if (RTStrICmp(argv[0], "set") == 0)
+ {
+ exitCode = videoFlagsModify(true, argc - 1, &argv[1]);
+ }
+ else if (RTStrICmp(argv[0], "clear") == 0)
+ {
+ exitCode = videoFlagsModify(false, argc - 1, &argv[1]);
+ }
+ else
+ {
+ VBoxControlError("Invalid command.\n");
+ exitCode = RTEXITCODE_FAILURE;
+ }
+
+ if (exitCode != RTEXITCODE_SUCCESS)
+ {
+ usage(VIDEO_FLAGS);
+ }
+
+ return exitCode;
+}
+
+#define MAX_CUSTOM_MODES 128
+
+/* the table of custom modes */
+struct
+{
+ DWORD xres;
+ DWORD yres;
+ DWORD bpp;
+} customModes[MAX_CUSTOM_MODES] = {0};
+
+void getCustomModes(HKEY hkeyVideo)
+{
+ ULONG status;
+ int curMode = 0;
+
+ /* null out the table */
+ RT_ZERO(customModes);
+
+ do
+ {
+ char valueName[20];
+ DWORD xres, yres, bpp = 0;
+ DWORD dwType;
+ DWORD dwLen = sizeof(DWORD);
+
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", curMode);
+ status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&xres, &dwLen);
+ if (status != ERROR_SUCCESS)
+ break;
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", curMode);
+ status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&yres, &dwLen);
+ if (status != ERROR_SUCCESS)
+ break;
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", curMode);
+ status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&bpp, &dwLen);
+ if (status != ERROR_SUCCESS)
+ break;
+
+ /* check if the mode is OK */
+ if ( (xres > (1 << 16))
+ || (yres > (1 << 16))
+ || ( (bpp != 16)
+ && (bpp != 24)
+ && (bpp != 32)))
+ break;
+
+ /* add mode to table */
+ customModes[curMode].xres = xres;
+ customModes[curMode].yres = yres;
+ customModes[curMode].bpp = bpp;
+
+ ++curMode;
+
+ if (curMode >= MAX_CUSTOM_MODES)
+ break;
+ } while(1);
+}
+
+void writeCustomModes(HKEY hkeyVideo)
+{
+ ULONG status;
+ int tableIndex = 0;
+ int modeIndex = 0;
+
+ /* first remove all values */
+ for (int i = 0; i < MAX_CUSTOM_MODES; i++)
+ {
+ char valueName[20];
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", i);
+ RegDeleteValueA(hkeyVideo, valueName);
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", i);
+ RegDeleteValueA(hkeyVideo, valueName);
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", i);
+ RegDeleteValueA(hkeyVideo, valueName);
+ }
+
+ do
+ {
+ if (tableIndex >= MAX_CUSTOM_MODES)
+ break;
+
+ /* is the table entry present? */
+ if ( (!customModes[tableIndex].xres)
+ || (!customModes[tableIndex].yres)
+ || (!customModes[tableIndex].bpp))
+ {
+ tableIndex++;
+ continue;
+ }
+
+ RTPrintf("writing mode %d (%dx%dx%d)\n", modeIndex, customModes[tableIndex].xres, customModes[tableIndex].yres, customModes[tableIndex].bpp);
+ char valueName[20];
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", modeIndex);
+ status = RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].xres,
+ sizeof(customModes[tableIndex].xres));
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", modeIndex);
+ RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].yres,
+ sizeof(customModes[tableIndex].yres));
+ RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", modeIndex);
+ RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].bpp,
+ sizeof(customModes[tableIndex].bpp));
+
+ modeIndex++;
+ tableIndex++;
+
+ } while(1);
+
+}
+
+static DECLCALLBACK(RTEXITCODE) handleListCustomModes(int argc, char *argv[])
+{
+ RT_NOREF1(argv);
+ if (argc != 0)
+ {
+ usage(LIST_CUST_MODES);
+ return RTEXITCODE_FAILURE;
+ }
+
+ HKEY hkeyVideo = getVideoKey(false);
+
+ if (hkeyVideo)
+ {
+ getCustomModes(hkeyVideo);
+ for (int i = 0; i < (sizeof(customModes) / sizeof(customModes[0])); i++)
+ {
+ if ( !customModes[i].xres
+ || !customModes[i].yres
+ || !customModes[i].bpp)
+ continue;
+
+ RTPrintf("Mode: %d x %d x %d\n",
+ customModes[i].xres, customModes[i].yres, customModes[i].bpp);
+ }
+ RegCloseKey(hkeyVideo);
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleAddCustomMode(int argc, char *argv[])
+{
+ if (argc != 3)
+ {
+ usage(ADD_CUST_MODE);
+ return RTEXITCODE_FAILURE;
+ }
+
+ DWORD xres = atoi(argv[0]);
+ DWORD yres = atoi(argv[1]);
+ DWORD bpp = atoi(argv[2]);
+
+ /** @todo better check including xres mod 8 = 0! */
+ if ( (xres > (1 << 16))
+ || (yres > (1 << 16))
+ || ( (bpp != 16)
+ && (bpp != 24)
+ && (bpp != 32)))
+ {
+ VBoxControlError("invalid mode specified!\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ HKEY hkeyVideo = getVideoKey(true);
+
+ if (hkeyVideo)
+ {
+ int i;
+ int fModeExists = 0;
+ getCustomModes(hkeyVideo);
+ for (i = 0; i < MAX_CUSTOM_MODES; i++)
+ {
+ /* mode exists? */
+ if ( customModes[i].xres == xres
+ && customModes[i].yres == yres
+ && customModes[i].bpp == bpp
+ )
+ {
+ fModeExists = 1;
+ }
+ }
+ if (!fModeExists)
+ {
+ for (i = 0; i < MAX_CUSTOM_MODES; i++)
+ {
+ /* item free? */
+ if (!customModes[i].xres)
+ {
+ customModes[i].xres = xres;
+ customModes[i].yres = yres;
+ customModes[i].bpp = bpp;
+ break;
+ }
+ }
+ writeCustomModes(hkeyVideo);
+ }
+ RegCloseKey(hkeyVideo);
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) handleRemoveCustomMode(int argc, char *argv[])
+{
+ if (argc != 3)
+ {
+ usage(REMOVE_CUST_MODE);
+ return RTEXITCODE_FAILURE;
+ }
+
+ DWORD xres = atoi(argv[0]);
+ DWORD yres = atoi(argv[1]);
+ DWORD bpp = atoi(argv[2]);
+
+ HKEY hkeyVideo = getVideoKey(true);
+
+ if (hkeyVideo)
+ {
+ getCustomModes(hkeyVideo);
+ for (int i = 0; i < MAX_CUSTOM_MODES; i++)
+ {
+ /* correct item? */
+ if ( (customModes[i].xres == xres)
+ && (customModes[i].yres == yres)
+ && (customModes[i].bpp == bpp))
+ {
+ RTPrintf("found mode at index %d\n", i);
+ RT_ZERO(customModes[i]);
+ break;
+ }
+ }
+ writeCustomModes(hkeyVideo);
+ RegCloseKey(hkeyVideo);
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+#ifdef VBOX_WITH_GUEST_PROPS
+/**
+ * Retrieves a value from the guest property store.
+ * This is accessed through the "VBoxGuestPropSvc" HGCM service.
+ *
+ * @returns Command exit code.
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE getGuestProperty(int argc, char **argv)
+{
+ bool fVerbose = false;
+ if ( argc == 2
+ && ( strcmp(argv[1], "-verbose") == 0
+ || strcmp(argv[1], "--verbose") == 0)
+ )
+ fVerbose = true;
+ else if (argc != 1)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+
+ uint32_t u32ClientId = 0;
+ int rc = VINF_SUCCESS;
+
+ rc = VbglR3GuestPropConnect(&u32ClientId);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
+
+ /*
+ * Here we actually retrieve the value from the host.
+ */
+ const char *pszName = argv[0];
+ char *pszValue = NULL;
+ uint64_t u64Timestamp = 0;
+ char *pszFlags = NULL;
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + 1024;
+ if (RT_SUCCESS(rc))
+ {
+ /* Because there is a race condition between our reading the size of a
+ * property and the guest updating it, we loop a few times here and
+ * hope. Actually this should never go wrong, as we are generous
+ * enough with buffer space. */
+ bool fFinished = false;
+ for (unsigned i = 0; i < 10 && !fFinished; ++i)
+ {
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (NULL == pvTmpBuf)
+ {
+ rc = VERR_NO_MEMORY;
+ VBoxControlError("Out of memory\n");
+ }
+ else
+ {
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cbBuf,
+ &pszValue, &u64Timestamp, &pszFlags,
+ &cbBuf);
+ }
+ if (VERR_BUFFER_OVERFLOW == rc)
+ /* Leave a bit of extra space to be safe */
+ cbBuf += 1024;
+ else
+ fFinished = true;
+ }
+ if (VERR_TOO_MUCH_DATA == rc)
+ VBoxControlError("Temporarily unable to retrieve the property\n");
+ else if (RT_FAILURE(rc) && rc != VERR_NOT_FOUND)
+ VBoxControlError("Failed to retrieve the property value, error %Rrc\n", rc);
+ }
+
+ /*
+ * And display it on the guest console.
+ */
+ if (VERR_NOT_FOUND == rc)
+ RTPrintf("No value set!\n");
+ else if (RT_SUCCESS(rc))
+ {
+ RTPrintf("Value: %s\n", pszValue);
+ if (fVerbose)
+ {
+ RTPrintf("Timestamp: %lld ns\n", u64Timestamp);
+ RTPrintf("Flags: %s\n", pszFlags);
+ }
+ }
+
+ if (u32ClientId != 0)
+ VbglR3GuestPropDisconnect(u32ClientId);
+ RTMemFree(pvBuf);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Writes a value to the guest property store.
+ * This is accessed through the "VBoxGuestPropSvc" HGCM service.
+ *
+ * @returns Command exit code.
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE setGuestProperty(int argc, char *argv[])
+{
+ /*
+ * Check the syntax. We can deduce the correct syntax from the number of
+ * arguments.
+ */
+ bool fUsageOK = true;
+ const char *pszName = NULL;
+ const char *pszValue = NULL;
+ const char *pszFlags = NULL;
+ if (2 == argc)
+ {
+ pszValue = argv[1];
+ }
+ else if (3 == argc)
+ fUsageOK = false;
+ else if (4 == argc)
+ {
+ pszValue = argv[1];
+ if ( strcmp(argv[2], "-flags") != 0
+ && strcmp(argv[2], "--flags") != 0)
+ fUsageOK = false;
+ pszFlags = argv[3];
+ }
+ else if (argc != 1)
+ fUsageOK = false;
+ if (!fUsageOK)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+ /* This is always needed. */
+ pszName = argv[0];
+
+ /*
+ * Do the actual setting.
+ */
+ uint32_t u32ClientId = 0;
+ int rc = VINF_SUCCESS;
+ rc = VbglR3GuestPropConnect(&u32ClientId);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
+ else
+ {
+ if (pszFlags != NULL)
+ rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, pszFlags);
+ else
+ rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to store the property value, error %Rrc\n", rc);
+ }
+
+ if (u32ClientId != 0)
+ VbglR3GuestPropDisconnect(u32ClientId);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Deletes a guest property from the guest property store.
+ * This is accessed through the "VBoxGuestPropSvc" HGCM service.
+ *
+ * @returns Command exit code.
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE deleteGuestProperty(int argc, char *argv[])
+{
+ /*
+ * Check the syntax. We can deduce the correct syntax from the number of
+ * arguments.
+ */
+ bool fUsageOK = true;
+ const char *pszName = NULL;
+ if (argc < 1)
+ fUsageOK = false;
+ if (!fUsageOK)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+ /* This is always needed. */
+ pszName = argv[0];
+
+ /*
+ * Do the actual setting.
+ */
+ uint32_t u32ClientId = 0;
+ int rc = VbglR3GuestPropConnect(&u32ClientId);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
+ else
+ {
+ rc = VbglR3GuestPropDelete(u32ClientId, pszName);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to delete the property value, error %Rrc\n", rc);
+ }
+
+ if (u32ClientId != 0)
+ VbglR3GuestPropDisconnect(u32ClientId);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Enumerates the properties in the guest property store.
+ * This is accessed through the "VBoxGuestPropSvc" HGCM service.
+ *
+ * @returns Command exit code.
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE enumGuestProperty(int argc, char *argv[])
+{
+ /*
+ * Check the syntax. We can deduce the correct syntax from the number of
+ * arguments.
+ */
+ char const * const *papszPatterns = NULL;
+ uint32_t cPatterns = 0;
+ if ( argc > 1
+ && ( strcmp(argv[0], "-patterns") == 0
+ || strcmp(argv[0], "--patterns") == 0))
+ {
+ papszPatterns = (char const * const *)&argv[1];
+ cPatterns = argc - 1;
+ }
+ else if (argc != 0)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Do the actual enumeration.
+ */
+ uint32_t u32ClientId = 0;
+ int rc = VbglR3GuestPropConnect(&u32ClientId);
+ if (RT_SUCCESS(rc))
+ {
+ PVBGLR3GUESTPROPENUM pHandle;
+ const char *pszName, *pszValue, *pszFlags;
+ uint64_t u64Timestamp;
+
+ rc = VbglR3GuestPropEnum(u32ClientId, papszPatterns, cPatterns, &pHandle,
+ &pszName, &pszValue, &u64Timestamp, &pszFlags);
+ if (RT_SUCCESS(rc))
+ {
+ while (RT_SUCCESS(rc) && pszName)
+ {
+ RTPrintf("Name: %s, value: %s, timestamp: %lld, flags: %s\n",
+ pszName, pszValue ? pszValue : "", u64Timestamp, pszFlags);
+
+ rc = VbglR3GuestPropEnumNext(pHandle, &pszName, &pszValue, &u64Timestamp, &pszFlags);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Error while enumerating guest properties: %Rrc\n", rc);
+ }
+
+ VbglR3GuestPropEnumFree(pHandle);
+ }
+ else if (VERR_NOT_FOUND == rc)
+ RTPrintf("No properties found.\n");
+ else
+ VBoxControlError("Failed to enumerate the guest properties! Error: %Rrc\n", rc);
+ VbglR3GuestPropDisconnect(u32ClientId);
+ }
+ else
+ VBoxControlError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Waits for notifications of changes to guest properties.
+ * This is accessed through the "VBoxGuestPropSvc" HGCM service.
+ *
+ * @returns Command exit code.
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE waitGuestProperty(int argc, char **argv)
+{
+ /*
+ * Handle arguments
+ */
+ const char *pszPatterns = NULL;
+ uint64_t u64TimestampIn = 0;
+ uint32_t u32Timeout = RT_INDEFINITE_WAIT;
+ bool fUsageOK = true;
+ if (argc < 1)
+ fUsageOK = false;
+ pszPatterns = argv[0];
+ for (int i = 1; fUsageOK && i < argc; ++i)
+ {
+ if ( strcmp(argv[i], "-timeout") == 0
+ || strcmp(argv[i], "--timeout") == 0)
+ {
+ if ( i + 1 >= argc
+ || RTStrToUInt32Full(argv[i + 1], 10, &u32Timeout)
+ != VINF_SUCCESS
+ )
+ fUsageOK = false;
+ else
+ ++i;
+ }
+ else if ( strcmp(argv[i], "-timestamp") == 0
+ || strcmp(argv[i], "--timestamp") == 0)
+ {
+ if ( i + 1 >= argc
+ || RTStrToUInt64Full(argv[i + 1], 10, &u64TimestampIn)
+ != VINF_SUCCESS
+ )
+ fUsageOK = false;
+ else
+ ++i;
+ }
+ else
+ fUsageOK = false;
+ }
+ if (!fUsageOK)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Connect to the service
+ */
+ uint32_t u32ClientId = 0;
+ int rc = VbglR3GuestPropConnect(&u32ClientId);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
+
+ /*
+ * Retrieve the notification from the host
+ */
+ char *pszName = NULL;
+ char *pszValue = NULL;
+ uint64_t u64TimestampOut = 0;
+ char *pszFlags = NULL;
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + 1024;
+ /* Because there is a race condition between our reading the size of a
+ * property and the guest updating it, we loop a few times here and
+ * hope. Actually this should never go wrong, as we are generous
+ * enough with buffer space. */
+ bool fFinished = false;
+ for (unsigned i = 0; (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW) && !fFinished && i < 10; i++)
+ {
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (NULL == pvTmpBuf)
+ {
+ rc = VERR_NO_MEMORY;
+ VBoxControlError("Out of memory\n");
+ }
+ else
+ {
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropWait(u32ClientId, pszPatterns, pvBuf, cbBuf,
+ u64TimestampIn, u32Timeout,
+ &pszName, &pszValue, &u64TimestampOut,
+ &pszFlags, &cbBuf);
+ }
+ if (VERR_BUFFER_OVERFLOW == rc)
+ /* Leave a bit of extra space to be safe */
+ cbBuf += 1024;
+ else
+ fFinished = true;
+ if (rc == VERR_TOO_MUCH_DATA)
+ VBoxControlError("Temporarily unable to get a notification\n");
+ else if (rc == VERR_INTERRUPTED)
+ VBoxControlError("The request timed out or was interrupted\n");
+ else if (RT_FAILURE(rc) && rc != VERR_NOT_FOUND)
+ VBoxControlError("Failed to get a notification, error %Rrc\n", rc);
+ }
+
+ /*
+ * And display it on the guest console.
+ */
+ if (VERR_NOT_FOUND == rc)
+ RTPrintf("No value set!\n");
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ RTPrintf("Internal error: unable to determine the size of the data!\n");
+ else if (RT_SUCCESS(rc))
+ {
+ RTPrintf("Name: %s\n", pszName);
+ RTPrintf("Value: %s\n", pszValue);
+ RTPrintf("Timestamp: %lld ns\n", u64TimestampOut);
+ RTPrintf("Flags: %s\n", pszFlags);
+ }
+
+ if (u32ClientId != 0)
+ VbglR3GuestPropDisconnect(u32ClientId);
+ RTMemFree(pvBuf);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Access the guest property store through the "VBoxGuestPropSvc" HGCM
+ * service.
+ *
+ * @returns 0 on success, 1 on failure
+ * @note see the command line API description for parameters
+ */
+static DECLCALLBACK(RTEXITCODE) handleGuestProperty(int argc, char *argv[])
+{
+ if (argc == 0)
+ {
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+ }
+ if (!strcmp(argv[0], "get"))
+ return getGuestProperty(argc - 1, argv + 1);
+ if (!strcmp(argv[0], "set"))
+ return setGuestProperty(argc - 1, argv + 1);
+ if (!strcmp(argv[0], "delete") || !strcmp(argv[0], "unset"))
+ return deleteGuestProperty(argc - 1, argv + 1);
+ if (!strcmp(argv[0], "enumerate"))
+ return enumGuestProperty(argc - 1, argv + 1);
+ if (!strcmp(argv[0], "wait"))
+ return waitGuestProperty(argc - 1, argv + 1);
+ /* unknown cmd */
+ usage(GUEST_PROP);
+ return RTEXITCODE_FAILURE;
+}
+
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+
+/**
+ * Lists the Shared Folders provided by the host.
+ */
+static RTEXITCODE sharedFolder_list(int argc, char **argv)
+{
+ bool fUsageOK = true;
+ bool fOnlyShowAutoMount = false;
+ if (argc == 1)
+ {
+ if (!strcmp(argv[0], "--automount"))
+ fOnlyShowAutoMount = true;
+ else
+ fUsageOK = false;
+ }
+ else if (argc > 1)
+ fUsageOK = false;
+ if (!fUsageOK)
+ {
+ usage(GUEST_SHAREDFOLDERS);
+ return RTEXITCODE_SYNTAX;
+ }
+
+ uint32_t u32ClientId;
+ int rc = VbglR3SharedFolderConnect(&u32ClientId);
+ if (RT_FAILURE(rc))
+ VBoxControlError("Failed to connect to the shared folder service, error %Rrc\n", rc);
+ else
+ {
+ PVBGLR3SHAREDFOLDERMAPPING paMappings;
+ uint32_t cMappings;
+ rc = VbglR3SharedFolderGetMappings(u32ClientId, fOnlyShowAutoMount, &paMappings, &cMappings);
+ if (RT_SUCCESS(rc))
+ {
+ if (fOnlyShowAutoMount)
+ RTPrintf("Auto-mounted Shared Folder mappings (%u):\n\n", cMappings);
+ else
+ RTPrintf("Shared Folder mappings (%u):\n\n", cMappings);
+
+ for (uint32_t i = 0; i < cMappings; i++)
+ {
+ char *pszName;
+ char *pszMntPt;
+ uint64_t fFlags;
+ uint32_t uRootIdVer;
+ rc = VbglR3SharedFolderQueryFolderInfo(u32ClientId, paMappings[i].u32Root, 0,
+ &pszName, &pszMntPt, &fFlags, &uRootIdVer);
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("%02u - %s [idRoot=%u", i + 1, pszName, paMappings[i].u32Root);
+ if (fFlags & SHFL_MIF_WRITABLE)
+ RTPrintf(" writable");
+ else
+ RTPrintf(" readonly");
+ if (fFlags & SHFL_MIF_AUTO_MOUNT)
+ RTPrintf(" auto-mount");
+ if (fFlags & SHFL_MIF_SYMLINK_CREATION)
+ RTPrintf(" create-symlink");
+ if (fFlags & SHFL_MIF_HOST_ICASE)
+ RTPrintf(" host-icase");
+ if (fFlags & SHFL_MIF_GUEST_ICASE)
+ RTPrintf(" guest-icase");
+ if (*pszMntPt)
+ RTPrintf(" mnt-pt=%s", pszMntPt);
+ RTPrintf("]");
+# ifdef RT_OS_OS2
+ /* Show drive letters: */
+ const char *pszOn = " on";
+ for (char chDrive = 'A'; chDrive <= 'Z'; chDrive++)
+ {
+ char szDrive[4] = { chDrive, ':', '\0', '\0' };
+ union
+ {
+ FSQBUFFER2 FsQueryBuf;
+ char achPadding[512];
+ } uBuf;
+ RT_ZERO(uBuf);
+ ULONG cbBuf = sizeof(uBuf) - 2;
+ APIRET rcOs2 = DosQueryFSAttach(szDrive, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
+ if (rcOs2 == NO_ERROR)
+ {
+ const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
+ if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
+ && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
+ {
+ const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
+ if (RTStrICmp(pszMountedName, pszName) == 0)
+ {
+ const char *pszTag = pszMountedName + strlen(pszMountedName) + 1; /* safe */
+ if (*pszTag != '\0')
+ RTPrintf("%s %s (%s)", pszOn, szDrive, pszTag);
+ else
+ RTPrintf("%s %s", pszOn, szDrive);
+ pszOn = ",";
+ }
+ }
+ }
+ }
+# endif
+ RTPrintf("\n");
+
+ RTStrFree(pszName);
+ RTStrFree(pszMntPt);
+ }
+ else
+ VBoxControlError("Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
+ paMappings[i].u32Root, rc);
+ }
+ if (!cMappings)
+ RTPrintf("No Shared Folders available.\n");
+ VbglR3SharedFolderFreeMappings(paMappings);
+ }
+ else
+ VBoxControlError("Error while getting the shared folder mappings, rc = %Rrc\n", rc);
+ VbglR3SharedFolderDisconnect(u32ClientId);
+ }
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+# ifdef RT_OS_OS2
+/**
+ * Attaches a shared folder to a drive letter.
+ */
+static RTEXITCODE sharedFolder_use(int argc, char **argv)
+{
+ /*
+ * Takes a drive letter and a share name as arguments.
+ */
+ if (argc != 2)
+ return VBoxControlSyntaxError("sharedfolder use: expected a drive letter and a shared folder name\n");
+
+ const char *pszDrive = argv[0];
+ if (!RT_C_IS_ALPHA(pszDrive[0]) || pszDrive[1] != ':' || pszDrive[2] != '\0')
+ return VBoxControlSyntaxError("sharedfolder use: not a drive letter: %s\n", pszDrive);
+
+ static const char s_szTag[] = "VBoxControl";
+ char szzNameAndTag[256];
+ const char *pszName = argv[1];
+ size_t cchName = strlen(pszName);
+ if (cchName < 1)
+ return VBoxControlSyntaxError("sharedfolder use: shared folder name cannot be empty!\n");
+ if (cchName + 1 + sizeof(s_szTag) >= sizeof(szzNameAndTag))
+ return VBoxControlSyntaxError("sharedfolder use: shared folder name is too long! (%s)\n", pszName);
+
+ /*
+ * Do the attaching.
+ */
+ memcpy(szzNameAndTag, pszName, cchName);
+ szzNameAndTag[cchName] = '\0';
+ memcpy(&szzNameAndTag[cchName + 1], s_szTag, sizeof(s_szTag));
+
+ APIRET rcOs2 = DosFSAttach(pszDrive, "VBOXSF", szzNameAndTag, cchName + 1 + sizeof(s_szTag), FS_ATTACH);
+ if (rcOs2 == NO_ERROR)
+ return RTEXITCODE_SUCCESS;
+ if (rcOs2 == ERROR_INVALID_FSD_NAME)
+ return VBoxControlError("Shared folders IFS not installed?\n");
+ return VBoxControlError("DosFSAttach/FS_ATTACH failed to attach '%s' to '%s': %u\n", pszName, pszDrive, rcOs2);
+}
+
+/**
+ * Detaches a shared folder from a drive letter.
+ */
+static RTEXITCODE sharedFolder_unuse(int argc, char **argv)
+{
+ /*
+ * Only takes a drive letter as argument.
+ */
+ if (argc != 1)
+ return VBoxControlSyntaxError("sharedfolder unuse: expected drive letter\n");
+ const char *pszDrive = argv[0];
+ if (!RT_C_IS_ALPHA(pszDrive[0]) || pszDrive[1] != ':' || pszDrive[2] != '\0')
+ return VBoxControlSyntaxError("sharedfolder unuse: not a drive letter: %s\n", pszDrive);
+
+ /*
+ * Do the detaching.
+ */
+ APIRET rcOs2 = DosFSAttach(pszDrive, "VBOXSF", NULL, 0, FS_DETACH);
+ if (rcOs2 == NO_ERROR)
+ return RTEXITCODE_SUCCESS;
+ return VBoxControlError("DosFSAttach/FS_DETACH failed on '%s': %u\n", pszDrive, rcOs2);
+}
+
+# endif /* RT_OS_OS2 */
+
+
+/**
+ * Handles Shared Folders control.
+ *
+ * @returns 0 on success, 1 on failure
+ * @note see the command line API description for parameters
+ * (r=bird: yeah, right. The API description contains nil about params)
+ */
+static DECLCALLBACK(RTEXITCODE) handleSharedFolder(int argc, char *argv[])
+{
+ if (argc == 0)
+ {
+ usage(GUEST_SHAREDFOLDERS);
+ return RTEXITCODE_FAILURE;
+ }
+ if (!strcmp(argv[0], "list"))
+ return sharedFolder_list(argc - 1, argv + 1);
+# ifdef RT_OS_OS2
+ if (!strcmp(argv[0], "use"))
+ return sharedFolder_use(argc - 1, argv + 1);
+ if (!strcmp(argv[0], "unuse"))
+ return sharedFolder_unuse(argc - 1, argv + 1);
+# endif
+
+ usage(GUEST_SHAREDFOLDERS);
+ return RTEXITCODE_FAILURE;
+}
+
+#endif
+#if !defined(VBOX_CONTROL_TEST)
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: writecoredump}
+ */
+static DECLCALLBACK(RTEXITCODE) handleWriteCoreDump(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv);
+ int rc = VbglR3WriteCoreDump();
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("Guest core dump successful.\n");
+ return RTEXITCODE_SUCCESS;
+ }
+ else
+ {
+ VBoxControlError("Error while taking guest core dump. rc=%Rrc\n", rc);
+ return RTEXITCODE_FAILURE;
+ }
+}
+
+#endif
+#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
+ */
+static DECLCALLBACK(RTEXITCODE) handleDpc(int argc, char *argv[])
+{
+ RT_NOREF(argc, argv);
+ int rc = VERR_NOT_IMPLEMENTED;
+# ifndef VBOX_CONTROL_TEST
+ for (int i = 0; i < 30; i++)
+ {
+ VBGLREQHDR Req;
+ VBGLREQHDR_INIT(&Req, DPC_LATENCY_CHECKER);
+ rc = vbglR3DoIOCtl(VBGL_IOCTL_DPC_LATENCY_CHECKER, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ RTPrintf("%d\n", i);
+ else
+ break;
+ }
+# endif
+ if (RT_FAILURE(rc))
+ return VBoxControlError("Error. rc=%Rrc\n", rc);
+ RTPrintf("Samples collection completed.\n");
+ return RTEXITCODE_SUCCESS;
+}
+#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
+
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: writelog}
+ */
+static DECLCALLBACK(RTEXITCODE) handleWriteLog(int argc, char *argv[])
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--no-newline", 'n', RTGETOPT_REQ_NOTHING },
+ };
+ bool fNoNewline = false;
+
+ RTGETOPTSTATE GetOptState;
+ int rc = RTGetOptInit(&GetOptState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
+ 0 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ if (RT_SUCCESS(rc))
+ {
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ size_t cch = strlen(ValueUnion.psz);
+ if ( fNoNewline
+ || (cch > 0 && ValueUnion.psz[cch - 1] == '\n') )
+ rc = VbglR3WriteLog(ValueUnion.psz, cch);
+ else
+ {
+ char *pszDup = (char *)RTMemDupEx(ValueUnion.psz, cch, 2);
+ if (RT_SUCCESS(rc))
+ {
+ pszDup[cch++] = '\n';
+ pszDup[cch] = '\0';
+ rc = VbglR3WriteLog(pszDup, cch);
+ RTMemFree(pszDup);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_FAILURE(rc))
+ return VBoxControlError("VbglR3WriteLog: %Rrc", rc);
+ break;
+ }
+
+ case 'n':
+ fNoNewline = true;
+ break;
+
+ case 'h': return usage(WRITE_LOG);
+ case 'V': return printVersion();
+ default:
+ return VBoxCtrlGetOptError(ch, &ValueUnion);
+ }
+ }
+ }
+ else
+ return VBoxControlError("RTGetOptInit: %Rrc", rc);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: takesnapshot}
+ */
+static DECLCALLBACK(RTEXITCODE) handleTakeSnapshot(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv); //VbglR3VmTakeSnapshot(argv[0], argv[1]);
+ return VBoxControlError("not implemented");
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: savestate}
+ */
+static DECLCALLBACK(RTEXITCODE) handleSaveState(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv); //VbglR3VmSaveState();
+ return VBoxControlError("not implemented");
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: suspend|pause}
+ */
+static DECLCALLBACK(RTEXITCODE) handleSuspend(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv); //VbglR3VmSuspend();
+ return VBoxControlError("not implemented");
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: poweroff|powerdown}
+ */
+static DECLCALLBACK(RTEXITCODE) handlePowerOff(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv); //VbglR3VmPowerOff();
+ return VBoxControlError("not implemented");
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: version}
+ */
+static DECLCALLBACK(RTEXITCODE) handleVersion(int argc, char *argv[])
+{
+ RT_NOREF1(argv);
+ if (argc)
+ return VBoxControlSyntaxError("getversion does not take any arguments");
+ return printVersion();
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
+ */
+static DECLCALLBACK(RTEXITCODE) handleHelp(int argc, char *argv[])
+{
+ RT_NOREF2(argc, argv); /* ignore arguments for now. */
+ usage();
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: ls}
+ */
+static DECLCALLBACK(RTEXITCODE) handleLs(int argc, char *argv[])
+{
+ return RTFsCmdLs(argc + 1, argv - 1);
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: tar}
+ */
+static DECLCALLBACK(RTEXITCODE) handleTar(int argc, char *argv[])
+{
+ return RTZipTarCmd(argc + 1, argv - 1);
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: tar}
+ */
+static DECLCALLBACK(RTEXITCODE) handleGzip(int argc, char *argv[])
+{
+ return RTZipGzipCmd(argc + 1, argv - 1);
+}
+
+/**
+ * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: unzip}
+ */
+static DECLCALLBACK(RTEXITCODE) handleUnzip(int argc, char *argv[])
+{
+ return RTZipUnzipCmd(argc + 1, argv - 1);
+}
+
+
+/** command handler type */
+typedef DECLCALLBACK(RTEXITCODE) FNVBOXCTRLCMDHANDLER(int argc, char *argv[]);
+typedef FNVBOXCTRLCMDHANDLER *PFNVBOXCTRLCMDHANDLER;
+
+/** The table of all registered command handlers. */
+struct COMMANDHANDLER
+{
+ const char *pszCommand;
+ PFNVBOXCTRLCMDHANDLER pfnHandler;
+ bool fNeedDevice;
+} g_aCommandHandlers[] =
+{
+#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
+ { "getvideoacceleration", handleGetVideoAcceleration, true },
+ { "setvideoacceleration", handleSetVideoAcceleration, true },
+ { "videoflags", handleVideoFlags, true },
+ { "listcustommodes", handleListCustomModes, true },
+ { "addcustommode", handleAddCustomMode, true },
+ { "removecustommode", handleRemoveCustomMode, true },
+ { "setvideomode", handleSetVideoMode, true },
+#endif
+#ifdef VBOX_WITH_GUEST_PROPS
+ { "guestproperty", handleGuestProperty, true },
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+ { "sharedfolder", handleSharedFolder, true },
+#endif
+#if !defined(VBOX_CONTROL_TEST)
+ { "writecoredump", handleWriteCoreDump, true },
+#endif
+#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
+ { "dpc", handleDpc, true },
+#endif
+ { "writelog", handleWriteLog, true },
+ { "takesnapshot", handleTakeSnapshot, true },
+ { "savestate", handleSaveState, true },
+ { "suspend", handleSuspend, true },
+ { "pause", handleSuspend, true },
+ { "poweroff", handlePowerOff, true },
+ { "powerdown", handlePowerOff, true },
+ { "getversion", handleVersion, false },
+ { "version", handleVersion, false },
+ { "help", handleHelp, false },
+ /* Hany tricks that doesn't cost much space: */
+ { "gzip", handleGzip, false },
+ { "ls", handleLs, false },
+ { "tar", handleTar, false },
+ { "unzip", handleUnzip, false },
+};
+
+/** Main function */
+int main(int argc, char **argv)
+{
+ /** The application's global return code */
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ /** An IPRT return code for local use */
+ int rrc = VINF_SUCCESS;
+ /** The index of the command line argument we are currently processing */
+ int iArg = 1;
+ /** Should we show the logo text? */
+ bool fShowLogo = true;
+ /** Should we print the usage after the logo? For the -help switch. */
+ bool fDoHelp = false;
+ /** Will we be executing a command or just printing information? */
+ bool fOnlyInfo = false;
+
+ rrc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rrc))
+ return RTMsgInitFailure(rrc);
+
+ /*
+ * Start by handling command line switches
+ */
+ /** @todo RTGetOpt conversion of the whole file. */
+ bool done = false; /**< Are we finished with handling switches? */
+ while (!done && (iArg < argc))
+ {
+ if ( !strcmp(argv[iArg], "-V")
+ || !strcmp(argv[iArg], "-v")
+ || !strcmp(argv[iArg], "--version")
+ || !strcmp(argv[iArg], "-version")
+ )
+ {
+ /* Print version number, and do nothing else. */
+ printVersion();
+ fOnlyInfo = true;
+ fShowLogo = false;
+ done = true;
+ }
+ else if ( !strcmp(argv[iArg], "-nologo")
+ || !strcmp(argv[iArg], "--nologo"))
+ fShowLogo = false;
+ else if ( !strcmp(argv[iArg], "-help")
+ || !strcmp(argv[iArg], "--help"))
+ {
+ fOnlyInfo = true;
+ fDoHelp = true;
+ done = true;
+ }
+ else
+ /* We have found an argument which isn't a switch. Exit to the
+ * command processing bit. */
+ done = true;
+ if (!done)
+ ++iArg;
+ }
+
+ /*
+ * Find the application name, show our logo if the user hasn't suppressed it,
+ * and show the usage if the user asked us to
+ */
+ g_pszProgName = RTPathFilename(argv[0]);
+ if (fShowLogo)
+ RTPrintf(VBOX_PRODUCT " Guest Additions Command Line Management Interface Version "
+ VBOX_VERSION_STRING "\n"
+ "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n\n");
+ if (fDoHelp)
+ usage();
+
+ /*
+ * Now look for an actual command in the argument list and handle it.
+ */
+ if (!fOnlyInfo && rcExit == RTEXITCODE_SUCCESS)
+ {
+ if (argc > iArg)
+ {
+ /*
+ * Try locate the command and execute it, complain if not found.
+ */
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aCommandHandlers); i++)
+ if (!strcmp(argv[iArg], g_aCommandHandlers[i].pszCommand))
+ {
+ if (g_aCommandHandlers[i].fNeedDevice)
+ {
+ rrc = VbglR3Init();
+ if (RT_FAILURE(rrc))
+ {
+ VBoxControlError("Could not contact the host system. Make sure that you are running this\n"
+ "application inside a VirtualBox guest system, and that you have sufficient\n"
+ "user permissions.\n");
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ }
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = g_aCommandHandlers[i].pfnHandler(argc - iArg - 1, argv + iArg + 1);
+ break;
+ }
+ if (i >= RT_ELEMENTS(g_aCommandHandlers))
+ {
+ usage();
+ rcExit = RTEXITCODE_SYNTAX;
+ }
+ }
+ else
+ {
+ /* The user didn't specify a command. */
+ usage();
+ rcExit = RTEXITCODE_SYNTAX;
+ }
+ }
+
+ /*
+ * And exit, returning the status
+ */
+ return rcExit;
+}
+
diff --git a/src/VBox/Additions/common/VBoxControl/VBoxControl.rc b/src/VBox/Additions/common/VBoxControl/VBoxControl.rc
new file mode 100644
index 00000000..e8c10a32
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxControl/VBoxControl.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxControl.rc $ */
+/** @file
+ * VBoxControl - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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 Additions Utility\0"
+ VALUE "InternalName", "VBoxControl\0"
+ VALUE "OriginalFilename", "VBoxControl.exe\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
diff --git a/src/VBox/Additions/common/VBoxControl/testcase/Makefile.kmk b/src/VBox/Additions/common/VBoxControl/testcase/Makefile.kmk
new file mode 100644
index 00000000..bdc3ddb6
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxControl/testcase/Makefile.kmk
@@ -0,0 +1,38 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VBoxControl testcases.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS)
+
+#
+# Dummy CLI testcase.
+#
+PROGRAMS += tstVBoxControl
+tstVBoxControl_TEMPLATE = VBOXR3TSTEXE
+tstVBoxControl_DEFS = VBOX_CONTROL_TEST
+tstVBoxControl_SOURCES = tstVBoxControl.cpp ../VBoxControl.cpp
+tstVBoxControl_LIBS = $(LIB_RUNTIME)
+tstVBoxControl_DEFS += \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS VBOX_WITH_HGCM,)
+
+endif # VBOX_WITH_TESTCASES
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp b/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp
new file mode 100644
index 00000000..e1a1f8db
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp
@@ -0,0 +1,213 @@
+/* $Id: tstVBoxControl.cpp $ */
+/** @file
+ * VBoxControl - Guest Additions Command Line Management Interface, test case
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cpp/autores.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <VBox/VBoxGuestLib.h>
+#ifdef VBOX_WITH_GUEST_PROPS
+# include <VBox/HostServices/GuestPropertySvc.h>
+#endif
+
+VBGLR3DECL(int) VbglR3Init(void)
+{
+ RTPrintf("Initialising guest library...\n");
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropConnect(HGCMCLIENTID *pidClient)
+{
+ AssertPtrReturn(pidClient, VERR_INVALID_POINTER);
+ RTPrintf("Connect to guest property service...\n");
+ *pidClient = 1;
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropDisconnect(HGCMCLIENTID idClient)
+{
+ RTPrintf("Disconnect client %d from guest property service...\n", idClient);
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropWrite(HGCMCLIENTID idClient,
+ const char *pszName,
+ const char *pszValue,
+ const char *pszFlags)
+{
+ RTPrintf("Called SET_PROP, client %d, name %s, value %s, flags %s...\n",
+ idClient, pszName, pszValue, pszFlags);
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropWriteValue(HGCMCLIENTID idClient,
+ const char *pszName,
+ const char *pszValue)
+{
+ RTPrintf("Called SET_PROP_VALUE, client %d, name %s, value %s...\n",
+ idClient, pszName, pszValue);
+ return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_GUEST_PROPS
+VBGLR3DECL(int) VbglR3GuestPropRead(HGCMCLIENTID idClient,
+ const char *pszName,
+ void *pvBuf,
+ uint32_t cbBuf,
+ char **ppszValue,
+ uint64_t *pu64Timestamp,
+ char **ppszFlags,
+ uint32_t *pcbBufActual)
+{
+ RT_NOREF2(pvBuf, cbBuf);
+ RTPrintf("Called GET_PROP, client %d, name %s...\n",
+ idClient, pszName);
+ static char szValue[] = "Value";
+ static char szFlags[] = "TRANSIENT";
+ if (VALID_PTR(ppszValue))
+ *ppszValue = szValue;
+ if (VALID_PTR(pu64Timestamp))
+ *pu64Timestamp = 12345;
+ if (VALID_PTR(ppszFlags))
+ *ppszFlags = szFlags;
+ if (VALID_PTR(pcbBufActual))
+ *pcbBufActual = 256;
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropDelete(HGCMCLIENTID idClient,
+ const char *pszName)
+{
+ RTPrintf("Called DEL_PROP, client %d, name %s...\n",
+ idClient, pszName);
+ return VINF_SUCCESS;
+}
+
+struct VBGLR3GUESTPROPENUM
+{
+ uint32_t u32;
+};
+
+VBGLR3DECL(int) VbglR3GuestPropEnum(HGCMCLIENTID idClient,
+ char const * const *ppaszPatterns,
+ uint32_t cPatterns,
+ PVBGLR3GUESTPROPENUM *ppHandle,
+ char const **ppszName,
+ char const **ppszValue,
+ uint64_t *pu64Timestamp,
+ char const **ppszFlags)
+{
+ RT_NOREF2(ppaszPatterns, cPatterns);
+ RTPrintf("Called ENUM_PROPS, client %d...\n", idClient);
+ AssertPtrReturn(ppHandle, VERR_INVALID_POINTER);
+ static VBGLR3GUESTPROPENUM Handle = { 0 };
+ static char szName[] = "Name";
+ static char szValue[] = "Value";
+ static char szFlags[] = "TRANSIENT";
+ *ppHandle = &Handle;
+ if (VALID_PTR(ppszName))
+ *ppszName = szName;
+ if (VALID_PTR(ppszValue))
+ *ppszValue = szValue;
+ if (VALID_PTR(pu64Timestamp))
+ *pu64Timestamp = 12345;
+ if (VALID_PTR(ppszFlags))
+ *ppszFlags = szFlags;
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle,
+ char const **ppszName,
+ char const **ppszValue,
+ uint64_t *pu64Timestamp,
+ char const **ppszFlags)
+{
+ RT_NOREF1(pHandle);
+ RTPrintf("Called enumerate next...\n");
+ AssertReturn(VALID_PTR(ppszName) || VALID_PTR(ppszValue) || VALID_PTR(ppszFlags),
+ VERR_INVALID_POINTER);
+ if (VALID_PTR(ppszName))
+ *ppszName = NULL;
+ if (VALID_PTR(ppszValue))
+ *ppszValue = NULL;
+ if (VALID_PTR(pu64Timestamp))
+ *pu64Timestamp = 0;
+ if (VALID_PTR(ppszFlags))
+ *ppszFlags = NULL;
+ return VINF_SUCCESS;
+}
+
+VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle)
+{
+ RT_NOREF1(pHandle);
+ RTPrintf("Called enumerate free...\n");
+}
+
+VBGLR3DECL(int) VbglR3GuestPropWait(HGCMCLIENTID idClient,
+ const char *pszPatterns,
+ void *pvBuf,
+ uint32_t cbBuf,
+ uint64_t u64Timestamp,
+ uint32_t u32Timeout,
+ char ** ppszName,
+ char **ppszValue,
+ uint64_t *pu64Timestamp,
+ char **ppszFlags,
+ uint32_t *pcbBufActual)
+{
+ RT_NOREF2(pvBuf, cbBuf);
+ if (u32Timeout == RT_INDEFINITE_WAIT)
+ RTPrintf("Called GET_NOTIFICATION, client %d, patterns %s, timestamp %llu,\n"
+ " timeout RT_INDEFINITE_WAIT...\n",
+ idClient, pszPatterns, u64Timestamp);
+ else
+ RTPrintf("Called GET_NOTIFICATION, client %d, patterns %s, timestamp %llu,\n"
+ " timeout %u...\n",
+ idClient, pszPatterns, u64Timestamp, u32Timeout);
+ static char szName[] = "Name";
+ static char szValue[] = "Value";
+ static char szFlags[] = "TRANSIENT";
+ if (VALID_PTR(ppszName))
+ *ppszName = szName;
+ if (VALID_PTR(ppszValue))
+ *ppszValue = szValue;
+ if (VALID_PTR(pu64Timestamp))
+ *pu64Timestamp = 12345;
+ if (VALID_PTR(ppszFlags))
+ *ppszFlags = szFlags;
+ if (VALID_PTR(pcbBufActual))
+ *pcbBufActual = 256;
+ return VINF_SUCCESS;
+}
+
+#endif
+
+VBGLR3DECL(int) VbglR3WriteLog(const char *pch, size_t cch)
+{
+ NOREF(pch); NOREF(cch);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/.scm-settings b/src/VBox/Additions/common/VBoxGuest/.scm-settings
new file mode 100644
index 00000000..11891b9e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/.scm-settings
@@ -0,0 +1,55 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for VBoxGuest
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+# Like the host drivers, this is dual licensed.
+--license-ose-dual
+
+-solaris.conf: --treat-as .sh
+
+/netbsd.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..b01fb72b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/Makefile.kmk
@@ -0,0 +1,269 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for the Cross Platform Guest Additions Driver.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile.
+include $(PATH_SUB_CURRENT)/lib/Makefile.kmk
+
+
+if1of ($(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/
+ ifdef 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_NT4)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_GST_NT4)_LIB)/hal.lib
+ 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
+
+
+ifeq ($(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
+ 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..d307f4d9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c
@@ -0,0 +1,436 @@
+/* $Id: VBoxDev-haiku.c $ */
+/** @file
+ * VBoxGuest kernel driver, Haiku Guest Additions, implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*
+ * 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 (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(!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(!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..8c6b08e3
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp
@@ -0,0 +1,1374 @@
+/* $Id: VBoxGuest-darwin.cpp $ */
+/** @file
+ * VBoxGuest - Darwin Specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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>
+#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)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* 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)
+DECLHIDDEN(kmod_start_func_t *) _realmain = vgdrvDarwinStart;
+DECLHIDDEN(kmod_stop_func_t *) _antimain = vgdrvDarwinStop;
+DECLHIDDEN(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(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(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 = 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 = 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..02b16566
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c
@@ -0,0 +1,789 @@
+/* $Id: VBoxGuest-freebsd.c $ */
+/** @file
+ * VirtualBox Guest Additions Driver for FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/** @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 (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(!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..ec0daa70
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c
@@ -0,0 +1,461 @@
+/* $Id: VBoxGuest-haiku-stubs.c $ */
+/** @file
+ * VBoxGuest kernel module, Haiku Guest Additions, stubs.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*
+ * 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..5c2a64fc
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c
@@ -0,0 +1,578 @@
+/* $Id: VBoxGuest-haiku.c $ */
+/** @file
+ * VBoxGuest kernel module, Haiku Guest Additions, implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*
+ * 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..f26eeb2c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h
@@ -0,0 +1,238 @@
+/* $Id: VBoxGuest-haiku.h $ */
+/** @file
+ * VBoxGuest kernel module, Haiku Guest Additions, header.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*
+ * 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..af4d3391
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c
@@ -0,0 +1,1307 @@
+/* $Rev: 127855 $ */
+/** @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-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+
+#include "the-linux-kernel.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
+# define VBOXGUEST_WITH_INPUT_DRIVER
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+# define kgid_t gid_t
+# define kuid_t uid_t
+#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 vgdrvLinuxModInit(void);
+static void 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 request for use in the IRQ
+ * handler. */
+static VMMDevReqMouseStatus *g_pMouseStatusReq;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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
+
+/**
+ * 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);
+ 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);
+}
+
+
+/**
+ * Creates the kernel input device.
+ */
+static int __init vgdrvLinuxCreateInputDevice(void)
+{
+ int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReq, sizeof(*g_pMouseStatusReq), VMMDevReq_GetMouseStatus);
+ if (RT_SUCCESS(rc))
+ {
+ g_pInputDevice = input_allocate_device();
+ if (g_pInputDevice)
+ {
+ 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 LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+ g_pInputDevice->cdev.dev = &g_pPciDev->dev;
+# else
+ g_pInputDevice->dev.parent = &g_pPciDev->dev;
+# endif
+ rc = input_register_device(g_pInputDevice);
+ if (rc == 0)
+ {
+ /* Do what one of our competitors apparently does as that works. */
+ ASMBitSet(g_pInputDevice->evbit, EV_ABS);
+ ASMBitSet(g_pInputDevice->evbit, EV_KEY);
+# ifdef EV_SYN
+ ASMBitSet(g_pInputDevice->evbit, EV_SYN);
+# endif
+ 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);
+ /** @todo this string should be in a header file somewhere. */
+ g_pInputDevice->name = "VirtualBox mouse integration";
+ return 0;
+ }
+
+ input_free_device(g_pInputDevice);
+ }
+ else
+ rc = -ENOMEM;
+ VbglR0GRFree(&g_pMouseStatusReq->header);
+ g_pMouseStatusReq = NULL;
+ }
+ else
+ rc = -ENOMEM;
+ return rc;
+}
+
+
+/**
+ * Terminates the kernel input device.
+ */
+static void vgdrvLinuxTermInputDevice(void)
+{
+ VbglR0GRFree(&g_pMouseStatusReq->header);
+ g_pMouseStatusReq = NULL;
+
+ /* 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ RTLogGroupSettings(pRelLogger, g_szLogGrp);
+ RTLogFlags(pRelLogger, g_szLogFlags);
+ RTLogDestinations(pRelLogger, g_szLogDst);
+#endif
+ RTLogRelSetDefaultInstance(pRelLogger);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_X86)
+ VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_AMD64)
+ VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_X86)
+ VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(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 ": 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 " (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 LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE >= KERNEL_VERSION(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 LINUX_VERSION_CODE < KERNEL_VERSION(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;
+}
+
+
+void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
+{
+#ifdef VBOXGUEST_WITH_INPUT_DRIVER
+ int rc;
+#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
+ /* Report events to the kernel input device */
+ g_pMouseStatusReq->mouseFeatures = 0;
+ g_pMouseStatusReq->pointerXPos = 0;
+ g_pMouseStatusReq->pointerYPos = 0;
+ rc = VbglR0GRPerform(&g_pMouseStatusReq->header);
+ if (RT_SUCCESS(rc))
+ {
+ input_report_abs(g_pInputDevice, ABS_X,
+ g_pMouseStatusReq->pointerXPos);
+ input_report_abs(g_pInputDevice, ABS_Y,
+ g_pMouseStatusReq->pointerYPos);
+# 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 LINUX_VERSION_CODE >= KERNEL_VERSION(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)
+ RTLogGetGroupSettings(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)
+ RTLogGetFlags(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)
+ RTLogGetDestinations(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..b142d7db
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c
@@ -0,0 +1,1033 @@
+/* $Id: VBoxGuest-netbsd.c $ */
+/** @file
+ * VirtualBox Guest Additions Driver for NetBSD.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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 =
+{
+ VBoxGuestNetBSDOpen,
+ noclose,
+ noread,
+ nowrite,
+ noioctl,
+ nostop,
+ notty,
+ nopoll,
+ nommap,
+ 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,
+};
+
+
+static struct wsmouse_calibcoords vboxguest_wsm_default_calib = {
+ .minx = VMMDEV_MOUSE_RANGE_MIN,
+ .miny = VMMDEV_MOUSE_RANGE_MIN,
+ .maxx = VMMDEV_MOUSE_RANGE_MAX,
+ .maxy = 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;
+
+ sc->sc_wsmousedev = config_found_ia(sc->sc_dev, "wsmousedev", &am, wsmousedevprint);
+ 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;
+
+ tpcalib_trans(&sc->sc_tpcalib,
+ sc->sc_vmmmousereq->pointerXPos,
+ sc->sc_vmmmousereq->pointerYPos,
+ &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;
+ register_t retval;
+ 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),
+ &retval, UIO_SYSSPACE);
+ if (error == EEXIST)
+ error = 0;
+ 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..499296c8
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp
@@ -0,0 +1,688 @@
+/* $Id: VBoxGuest-os2.cpp $ */
+/** @file
+ * VBoxGuest - OS/2 specifics.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ * ---------------------------------------------------------------------------
+ * 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..107aa236
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def
@@ -0,0 +1,45 @@
+; $Id: VBoxGuest-os2.def $
+;; @file
+; VBoxGuest - OS/2 definition file.
+;
+
+;
+; Copyright (C) 2007-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+
+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..082f45d6
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c
@@ -0,0 +1,1129 @@
+/* $Id: VBoxGuest-solaris.c $ */
+/** @file
+ * VirtualBox Guest Additions Driver for Solaris.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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;
+ }
+
+ int instance = ddi_get_instance(pDip);
+
+ /*
+ * 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, 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;
+ break;
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
+ 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;
+}
+
+
+/**
+ * Poll notifier for mouse poll events.
+ *
+ * @param pDevExt Pointer to the device extension.
+ *
+ * @remarks This must be called without holding any spinlocks.
+ */
+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..c751ec95
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf
@@ -0,0 +1,31 @@
+# $Id: VBoxGuest-solaris.conf $
+## @file
+# OpenSolaris Guest Driver Configuration
+
+#
+# Copyright (C) 2007-2017 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+# 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..2bb07704
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp
@@ -0,0 +1,3470 @@
+/* $Id: VBoxGuest-win.cpp $ */
+/** @file
+ * VBoxGuest - Windows specifics.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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;
+
+
+/*********************************************************************************************************************************
+* 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;
+ 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;
+ int 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;
+
+ 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 (succcess)\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;
+}
+
+
+/**
+ * Overridden routine for mouse polling events.
+ *
+ * @param pDevExt Device extension structure.
+ */
+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..63840d42
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp
@@ -0,0 +1,4507 @@
+/* $Id: VBoxGuest.cpp $ */
+/** @file
+ * VBoxGuest - Guest Additions Driver, Common Code.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/** @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.
+ */
+PFNRT 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 fLogRel = *pszName == 'd' || *pszName == 'D';
+ const char *pszSubName = &pszName[fLogRel ? 4 + 3 : 3];
+ if ( !*pszSubName
+ || RTStrICmpAscii(pszSubName, "_flags") == 0
+ || RTStrICmpAscii(pszSubName, "_dest") == 0)
+ {
+ PRTLOGGER pLogger = fLogRel ? 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.
+ */
+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_HGCMCall32:
+ case VMMDevReq_HGCMCall64:
+# else
+ case VMMDevReq_HGCMCall:
+# endif /* VBOX_WITH_64_BITS_GUESTS */
+ 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:
+ 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:
+ case VMMDevReq_ChangeMemBalloon:
+ 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_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;
+ 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();
+
+ pCallReq->Hdr.rc = 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));
+ 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..30a1f1b6
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm
@@ -0,0 +1,1669 @@
+; $Id: VBoxGuestA-os2.asm $
+;; @file
+; VBoxGuest - OS/2 assembly file, the first file in the link.
+;
+
+;
+; Copyright (C) 2007-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;-----------------------------------------------------------------------------
+; 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..bf3060c5
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h
@@ -0,0 +1,405 @@
+/* $Id: VBoxGuestInternal.h $ */
+/** @file
+ * VBoxGuest - Guest Additions Driver, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#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..097f1fb3
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile
@@ -0,0 +1,185 @@
+# $Id: Makefile $
+## @file
+# VirtualBox Guest Additions Module Makefile.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+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
+
+.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 \
+ RTStrICmpAscii.c \
+ RTStrNICmpAscii.c \
+ RTStrNCmp.c \
+ RTStrNLen.c \
+ stringalloc.c \
+ strformat.c \
+ strformatnum.c \
+ strformatrt.c \
+ strformattype.c \
+ strprintf.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
+
+.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..581de04f
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/freebsd/files_vboxguest
@@ -0,0 +1,221 @@
+#!/bin/sh
+# $Id: files_vboxguest $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+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/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}/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/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/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/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/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_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..1aa4102c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk
@@ -0,0 +1,237 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the common guest addition code library.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.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_FOLDERS),VBOX_WITH_SHARED_FOLDERS,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,)
+VBoxGuestR3Lib_SOURCES = \
+ VBoxGuestR3Lib.cpp \
+ VBoxGuestR3LibAdditions.cpp \
+ VBoxGuestR3LibAutoLogon.cpp \
+ VBoxGuestR3LibBalloon.cpp \
+ VBoxGuestR3LibClipboard.cpp \
+ VBoxGuestR3LibCoreDump.cpp \
+ VBoxGuestR3LibCpuHotPlug.cpp \
+ VBoxGuestR3LibCredentials.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_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,)
+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,)
+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..688a7004
--- /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-2019 Oracle Corporation
+ *
+ * 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..1e559e8b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp
@@ -0,0 +1,185 @@
+/* $Id: VBoxGuestR0LibGenericRequest.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Generic VMMDev request management.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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_HGCMCall32
+ || pReq->requestType == VMMDevReq_HGCMCall64
+#else
+ || pReq->requestType == VMMDevReq_HGCMCall
+#endif
+ || pReq->requestType == VMMDevReq_RegisterSharedModule
+ || pReq->requestType == VMMDevReq_ReportGuestUserState
+ || pReq->requestType == VMMDevReq_LogString
+ || pReq->requestType == VMMDevReq_SetPointerShape
+ || pReq->requestType == VMMDevReq_VideoSetVisibleRegion)
+ {
+ 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..78a554e8
--- /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-2019 Oracle Corporation
+ *
+ * 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..4eb46c2a
--- /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-2019 Oracle Corporation
+ *
+ * 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();
+ 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..7ee61d93
--- /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-2019 Oracle Corporation
+ *
+ * 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..27d04dca
--- /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-2019 Oracle Corporation
+ *
+ * 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..40bd4ab7
--- /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-2019 Oracle Corporation
+ *
+ * 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..c2c38952
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp
@@ -0,0 +1,192 @@
+/* $Id: VBoxGuestR0LibIdc-win.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Windows specific.
+ */
+
+/*
+ * Copyright (C) 2008-2019 Oracle Corporation
+ *
+ * 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.
+ */
+ 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
+ pIrp->Flags |= IRP_SYNCHRONOUS_API;
+ 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..a6cfa673
--- /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-2019 Oracle Corporation
+ *
+ * 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..3a758d70
--- /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-2019 Oracle Corporation
+ *
+ * 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..34f14ac4
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h
@@ -0,0 +1,202 @@
+/* $Id: VBoxGuestR0LibInternal.h $ */
+/** @file
+ * VBoxGuestLibR0 - Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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 _VBGLPHYSHEAPCHUNK;
+typedef struct _VBGLPHYSHEAPCHUNK VBGLPHYSHEAPCHUNK;
+
+enum VbglLibStatus
+{
+ VbglStatusNotInitialized = 0,
+ VbglStatusInitializing,
+ VbglStatusReady
+};
+
+/**
+ * Global VBGL ring-0 data.
+ * Lives in VbglR0Init.cpp.
+ */
+typedef struct VBGLDATA
+{
+ enum VbglLibStatus status;
+
+ RTIOPORT portVMMDev;
+
+ VMMDevMemory *pVMMDevMemory;
+
+ /**
+ * Physical memory heap data.
+ * @{
+ */
+
+ VBGLPHYSHEAPBLOCK *pFreeBlocksHead;
+ VBGLPHYSHEAPBLOCK *pAllocBlocksHead;
+ VBGLPHYSHEAPCHUNK *pChunkHead;
+
+ RTSEMFASTMUTEX mutexHeap;
+ /** @} */
+
+ /**
+ * 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..b41c31bb
--- /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-2019 Oracle Corporation
+ *
+ * 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..c409530e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp
@@ -0,0 +1,644 @@
+/* $Id: VBoxGuestR0LibPhysHeap.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Physical memory heap.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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/alloc.h>
+
+/* Physical memory heap consists of double linked list
+ * of chunks. Memory blocks are allocated inside these chunks
+ * and are members of Allocated and Free double linked lists.
+ *
+ * When allocating a block, we search in Free linked
+ * list for a suitable free block. If there is no such block,
+ * a new chunk is allocated and the new block is taken from
+ * the new chunk as the only chunk-sized free block.
+ * Allocated block is excluded from the Free list and goes to
+ * Alloc list.
+ *
+ * When freeing block, we check the pointer and then
+ * exclude block from Alloc list and move it to free list.
+ *
+ * For each chunk we maintain the allocated blocks counter.
+ * if 2 (or more) entire chunks are free they are immediately
+ * deallocated, so we always have at most 1 free chunk.
+ *
+ * When freeing blocks, two subsequent free blocks are always
+ * merged together. Current implementation merges blocks only
+ * when there is a block after the just freed one.
+ *
+ */
+
+#define VBGL_PH_ASSERT Assert
+#define VBGL_PH_ASSERTMsg AssertMsg
+
+// #define DUMPHEAP
+
+#ifdef DUMPHEAP
+# define VBGL_PH_dprintf(a) RTAssertMsg2Weak a
+#else
+# define VBGL_PH_dprintf(a)
+#endif
+
+/* Heap block signature */
+#define VBGL_PH_BLOCKSIGNATURE (0xADDBBBBB)
+
+
+/* Heap chunk signature */
+#define VBGL_PH_CHUNKSIGNATURE (0xADDCCCCC)
+/* Heap chunk allocation unit */
+#define VBGL_PH_CHUNKSIZE (0x10000)
+
+/* Heap block bit flags */
+#define VBGL_PH_BF_ALLOCATED (0x1)
+
+struct _VBGLPHYSHEAPBLOCK
+{
+ uint32_t u32Signature;
+
+ /* Size of user data in the block. Does not include the block header. */
+ uint32_t cbDataSize;
+
+ uint32_t fu32Flags;
+
+ struct _VBGLPHYSHEAPBLOCK *pNext;
+ struct _VBGLPHYSHEAPBLOCK *pPrev;
+
+ struct _VBGLPHYSHEAPCHUNK *pChunk;
+};
+
+struct _VBGLPHYSHEAPCHUNK
+{
+ uint32_t u32Signature;
+
+ /* Size of the chunk. Includes the chunk header. */
+ uint32_t cbSize;
+
+ /* Physical address of the chunk */
+ uint32_t physAddr;
+
+ /* Number of allocated blocks in the chunk */
+ int32_t cAllocatedBlocks;
+
+ struct _VBGLPHYSHEAPCHUNK *pNext;
+ struct _VBGLPHYSHEAPCHUNK *pPrev;
+};
+
+
+#ifndef DUMPHEAP
+#define dumpheap(a)
+#else
+void dumpheap (char *point)
+{
+ VBGL_PH_dprintf(("VBGL_PH dump at '%s'\n", point));
+
+ VBGL_PH_dprintf(("Chunks:\n"));
+
+ VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
+
+ while (pChunk)
+ {
+ VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, allocated = %8d, phys = %08X\n",
+ pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbSize, pChunk->cAllocatedBlocks, pChunk->physAddr));
+
+ pChunk = pChunk->pNext;
+ }
+
+ VBGL_PH_dprintf(("Allocated blocks:\n"));
+
+ VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pAllocBlocksHead;
+
+ while (pBlock)
+ {
+ VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
+ pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
+
+ pBlock = pBlock->pNext;
+ }
+
+ VBGL_PH_dprintf(("Free blocks:\n"));
+
+ pBlock = g_vbgldata.pFreeBlocksHead;
+
+ while (pBlock)
+ {
+ VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
+ pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
+
+ pBlock = pBlock->pNext;
+ }
+
+ VBGL_PH_dprintf(("VBGL_PH dump at '%s' done\n", point));
+}
+#endif
+
+
+DECLINLINE(void *) vbglPhysHeapBlock2Data (VBGLPHYSHEAPBLOCK *pBlock)
+{
+ return (void *)(pBlock? (char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK): NULL);
+}
+
+DECLINLINE(VBGLPHYSHEAPBLOCK *) vbglPhysHeapData2Block (void *p)
+{
+ VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)(p? (char *)p - sizeof (VBGLPHYSHEAPBLOCK): NULL);
+
+ VBGL_PH_ASSERTMsg(pBlock == NULL || pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
+ ("pBlock->u32Signature = %08X\n", pBlock->u32Signature));
+
+ return pBlock;
+}
+
+DECLINLINE(int) vbglPhysHeapEnter (void)
+{
+ int rc = RTSemFastMutexRequest(g_vbgldata.mutexHeap);
+
+ VBGL_PH_ASSERTMsg(RT_SUCCESS(rc),
+ ("Failed to request heap mutex, rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+DECLINLINE(void) vbglPhysHeapLeave (void)
+{
+ RTSemFastMutexRelease(g_vbgldata.mutexHeap);
+}
+
+
+static void vbglPhysHeapInitBlock (VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbDataSize)
+{
+ VBGL_PH_ASSERT(pBlock != NULL);
+ VBGL_PH_ASSERT(pChunk != NULL);
+
+ pBlock->u32Signature = VBGL_PH_BLOCKSIGNATURE;
+ pBlock->cbDataSize = cbDataSize;
+ pBlock->fu32Flags = 0;
+ pBlock->pNext = NULL;
+ pBlock->pPrev = NULL;
+ pBlock->pChunk = pChunk;
+}
+
+
+static void vbglPhysHeapInsertBlock (VBGLPHYSHEAPBLOCK *pInsertAfter, VBGLPHYSHEAPBLOCK *pBlock)
+{
+ VBGL_PH_ASSERTMsg(pBlock->pNext == NULL,
+ ("pBlock->pNext = %p\n", pBlock->pNext));
+ VBGL_PH_ASSERTMsg(pBlock->pPrev == NULL,
+ ("pBlock->pPrev = %p\n", pBlock->pPrev));
+
+ if (pInsertAfter)
+ {
+ pBlock->pNext = pInsertAfter->pNext;
+ pBlock->pPrev = pInsertAfter;
+
+ if (pInsertAfter->pNext)
+ {
+ pInsertAfter->pNext->pPrev = pBlock;
+ }
+
+ pInsertAfter->pNext = pBlock;
+ }
+ else
+ {
+ /* inserting to head of list */
+ pBlock->pPrev = NULL;
+
+ if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
+ {
+ pBlock->pNext = g_vbgldata.pAllocBlocksHead;
+
+ if (g_vbgldata.pAllocBlocksHead)
+ {
+ g_vbgldata.pAllocBlocksHead->pPrev = pBlock;
+ }
+
+ g_vbgldata.pAllocBlocksHead = pBlock;
+ }
+ else
+ {
+ pBlock->pNext = g_vbgldata.pFreeBlocksHead;
+
+ if (g_vbgldata.pFreeBlocksHead)
+ {
+ g_vbgldata.pFreeBlocksHead->pPrev = pBlock;
+ }
+
+ g_vbgldata.pFreeBlocksHead = pBlock;
+ }
+ }
+}
+
+static void vbglPhysHeapExcludeBlock (VBGLPHYSHEAPBLOCK *pBlock)
+{
+ if (pBlock->pNext)
+ {
+ pBlock->pNext->pPrev = pBlock->pPrev;
+ }
+ else
+ {
+ /* this is tail of list but we do not maintain tails of block lists.
+ * so do nothing.
+ */
+ ;
+ }
+
+ if (pBlock->pPrev)
+ {
+ pBlock->pPrev->pNext = pBlock->pNext;
+ }
+ else
+ {
+ /* this is head of list but we do not maintain tails of block lists. */
+ if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
+ {
+ g_vbgldata.pAllocBlocksHead = pBlock->pNext;
+ }
+ else
+ {
+ g_vbgldata.pFreeBlocksHead = pBlock->pNext;
+ }
+ }
+
+ pBlock->pNext = NULL;
+ pBlock->pPrev = NULL;
+}
+
+static VBGLPHYSHEAPBLOCK *vbglPhysHeapChunkAlloc (uint32_t cbSize)
+{
+ RTCCPHYS physAddr;
+ VBGLPHYSHEAPCHUNK *pChunk;
+ VBGLPHYSHEAPBLOCK *pBlock;
+ VBGL_PH_dprintf(("Allocating new chunk of size %d\n", cbSize));
+
+ /* Compute chunk size to allocate */
+ if (cbSize < VBGL_PH_CHUNKSIZE)
+ {
+ /* Includes case of block size 0 during initialization */
+ cbSize = VBGL_PH_CHUNKSIZE;
+ }
+ else
+ {
+ /* Round up to next chunk size, which must be power of 2 */
+ cbSize = (cbSize + (VBGL_PH_CHUNKSIZE - 1)) & ~(VBGL_PH_CHUNKSIZE - 1);
+ }
+
+ physAddr = 0;
+ /* This function allocates physical contiguous memory (below 4GB) according to the IPRT docs.
+ * Address < 4G is required for the port IO.
+ */
+ pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize);
+
+ if (!pChunk)
+ {
+ LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u contiguous bytes.\n", cbSize));
+ return NULL;
+ }
+
+ AssertRelease(physAddr < _4G && physAddr + cbSize <= _4G);
+
+ pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
+ pChunk->cbSize = cbSize;
+ pChunk->physAddr = (uint32_t)physAddr;
+ pChunk->cAllocatedBlocks = 0;
+ pChunk->pNext = g_vbgldata.pChunkHead;
+ pChunk->pPrev = NULL;
+
+ /* Initialize the free block, which now occupies entire chunk. */
+ pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK));
+
+ vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK));
+
+ vbglPhysHeapInsertBlock (NULL, pBlock);
+
+ g_vbgldata.pChunkHead = pChunk;
+
+ VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize));
+
+ return pBlock;
+}
+
+
+void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk)
+{
+ char *p;
+ VBGL_PH_ASSERT(pChunk != NULL);
+ VBGL_PH_ASSERTMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
+ ("pChunk->u32Signature = %08X\n", pChunk->u32Signature));
+
+ VBGL_PH_dprintf(("Deleting chunk %p size %x\n", pChunk, pChunk->cbSize));
+
+ /* first scan the chunk and exclude all blocks from lists */
+
+ p = (char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK);
+
+ while (p < (char *)pChunk + pChunk->cbSize)
+ {
+ VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)p;
+
+ p += pBlock->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
+
+ vbglPhysHeapExcludeBlock (pBlock);
+ }
+
+ VBGL_PH_ASSERTMsg(p == (char *)pChunk + pChunk->cbSize,
+ ("p = %p, (char *)pChunk + pChunk->cbSize = %p, pChunk->cbSize = %08X\n",
+ p, (char *)pChunk + pChunk->cbSize, pChunk->cbSize));
+
+ /* Exclude chunk from the chunk list */
+ if (pChunk->pNext)
+ {
+ pChunk->pNext->pPrev = pChunk->pPrev;
+ }
+ else
+ {
+ /* we do not maintain tail */
+ ;
+ }
+
+ if (pChunk->pPrev)
+ {
+ pChunk->pPrev->pNext = pChunk->pNext;
+ }
+ else
+ {
+ /* the chunk was head */
+ g_vbgldata.pChunkHead = pChunk->pNext;
+ }
+
+ RTMemContFree (pChunk, pChunk->cbSize);
+}
+
+
+DECLR0VBGL(void *) VbglR0PhysHeapAlloc (uint32_t cbSize)
+{
+ VBGLPHYSHEAPBLOCK *pBlock, *iter;
+ int rc = vbglPhysHeapEnter ();
+
+ if (RT_FAILURE(rc))
+ return NULL;
+
+ dumpheap ("pre alloc");
+
+ pBlock = NULL;
+
+ /* If there are free blocks in the heap, look at them. */
+ iter = g_vbgldata.pFreeBlocksHead;
+
+ /* There will be not many blocks in the heap, so
+ * linear search would be fast enough.
+ */
+
+ while (iter)
+ {
+ if (iter->cbDataSize == cbSize)
+ {
+ /* exact match */
+ pBlock = iter;
+ break;
+ }
+
+ /* Looking for a free block with nearest size */
+ if (iter->cbDataSize > cbSize)
+ {
+ if (pBlock)
+ {
+ if (iter->cbDataSize < pBlock->cbDataSize)
+ {
+ pBlock = iter;
+ }
+ }
+ else
+ {
+ pBlock = iter;
+ }
+ }
+
+ iter = iter->pNext;
+ }
+
+ if (!pBlock)
+ {
+ /* No free blocks, allocate a new chunk,
+ * the only free block of the chunk will
+ * be returned.
+ */
+ pBlock = vbglPhysHeapChunkAlloc (cbSize);
+ }
+
+ if (pBlock)
+ {
+ VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
+ ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
+ VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
+ ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
+
+ /* We have a free block, either found or allocated. */
+
+ if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
+ {
+ /* Data will occupy less than a half of the block,
+ * the block should be split.
+ */
+ iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
+
+ /* Init the new 'iter' block, initialized blocks are always marked as free. */
+ vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
+
+ pBlock->cbDataSize = cbSize;
+
+ /* Insert the new 'iter' block after the 'pBlock' in the free list */
+ vbglPhysHeapInsertBlock (pBlock, iter);
+ }
+
+ /* Exclude pBlock from free list */
+ vbglPhysHeapExcludeBlock (pBlock);
+
+ /* Mark as allocated */
+ pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
+
+ /* Insert to allocated list */
+ vbglPhysHeapInsertBlock (NULL, pBlock);
+
+ /* Adjust the chunk allocated blocks counter */
+ pBlock->pChunk->cAllocatedBlocks++;
+ }
+
+ dumpheap ("post alloc");
+
+ vbglPhysHeapLeave ();
+ VBGL_PH_dprintf(("VbglR0PhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
+
+ return vbglPhysHeapBlock2Data (pBlock);
+}
+
+DECLR0VBGL(uint32_t) VbglR0PhysHeapGetPhysAddr (void *p)
+{
+ uint32_t physAddr = 0;
+ VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p);
+
+ if (pBlock)
+ {
+ VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
+ ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
+
+ if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
+ physAddr = pBlock->pChunk->physAddr + (uint32_t)((uintptr_t)p - (uintptr_t)pBlock->pChunk);
+ }
+
+ return physAddr;
+}
+
+DECLR0VBGL(void) VbglR0PhysHeapFree(void *p)
+{
+ VBGLPHYSHEAPBLOCK *pBlock;
+ VBGLPHYSHEAPBLOCK *pNeighbour;
+
+ int rc = vbglPhysHeapEnter ();
+ if (RT_FAILURE(rc))
+ return;
+
+ dumpheap ("pre free");
+
+ pBlock = vbglPhysHeapData2Block (p);
+
+ if (!pBlock)
+ {
+ vbglPhysHeapLeave ();
+ return;
+ }
+
+ VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
+ ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
+
+ /* Exclude from allocated list */
+ vbglPhysHeapExcludeBlock (pBlock);
+
+ dumpheap ("post exclude");
+
+ VBGL_PH_dprintf(("VbglR0PhysHeapFree %x size %x\n", p, pBlock->cbDataSize));
+
+ /* Mark as free */
+ pBlock->fu32Flags &= ~VBGL_PH_BF_ALLOCATED;
+
+ /* Insert to free list */
+ vbglPhysHeapInsertBlock (NULL, pBlock);
+
+ dumpheap ("post insert");
+
+ /* Adjust the chunk allocated blocks counter */
+ pBlock->pChunk->cAllocatedBlocks--;
+
+ VBGL_PH_ASSERT(pBlock->pChunk->cAllocatedBlocks >= 0);
+
+ /* Check if we can merge 2 free blocks. To simplify heap maintenance,
+ * we will look at block after the just freed one.
+ * This will not prevent us from detecting free memory chunks.
+ * Also in most cases blocks are deallocated in reverse allocation order
+ * and in that case the merging will work.
+ */
+
+ pNeighbour = (VBGLPHYSHEAPBLOCK *)((char *)p + pBlock->cbDataSize);
+
+ if ((char *)pNeighbour < (char *)pBlock->pChunk + pBlock->pChunk->cbSize
+ && (pNeighbour->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0)
+ {
+ /* The next block is free as well. */
+
+ /* Adjust size of current memory block */
+ pBlock->cbDataSize += pNeighbour->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
+
+ /* Exclude the next neighbour */
+ vbglPhysHeapExcludeBlock (pNeighbour);
+ }
+
+ dumpheap ("post merge");
+
+ /* now check if there are 2 or more free chunks */
+ if (pBlock->pChunk->cAllocatedBlocks == 0)
+ {
+ VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
+
+ uint32_t u32FreeChunks = 0;
+
+ while (pChunk)
+ {
+ if (pChunk->cAllocatedBlocks == 0)
+ {
+ u32FreeChunks++;
+ }
+
+ pChunk = pChunk->pNext;
+ }
+
+ if (u32FreeChunks > 1)
+ {
+ /* Delete current chunk, it will also exclude all free blocks
+ * remaining in the chunk from the free list, so the pBlock
+ * will also be invalid after this.
+ */
+ vbglPhysHeapChunkDelete (pBlock->pChunk);
+ }
+ }
+
+ dumpheap ("post free");
+
+ vbglPhysHeapLeave ();
+}
+
+DECLR0VBGL(int) VbglR0PhysHeapInit (void)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Allocate the first chunk of the heap. */
+ VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapChunkAlloc (0);
+
+ if (!pBlock)
+ rc = VERR_NO_MEMORY;
+
+ RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
+
+ return rc;
+}
+
+DECLR0VBGL(void) VbglR0PhysHeapTerminate (void)
+{
+ while (g_vbgldata.pChunkHead)
+ {
+ vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
+ }
+
+ RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
+}
+
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..13db0c1e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c
@@ -0,0 +1,704 @@
+/* $Id: VBoxGuestR0LibSharedFolders.c $ */
+/** @file
+ * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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 SHFL_CPARMS_SET_UTF8 0
+#define SHFL_CPARMS_SET_SYMLINKS 0
+
+#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). */
+
+
+
+/** @todo We only need HGCM, not physical memory, so other guests should also
+ * switch to calling vbglR0HGCMInit() and vbglR0HGCMTerminate() instead
+ * of VbglR0SfInit() and VbglR0SfTerm(). */
+#ifndef RT_OS_LINUX
+DECLVBGL(int) VbglR0SfInit(void)
+{
+ return VbglR0InitClient();
+}
+
+DECLVBGL(void) VbglR0SfTerm(void)
+{
+ VbglR0TerminateClient();
+}
+#endif
+
+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;
+}
+
+/** @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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+
+/** @} */
+
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..850ee33a
--- /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-2019 Oracle Corporation
+ *
+ * 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..eab9c3e9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp
@@ -0,0 +1,475 @@
+/* $Id: VBoxGuestR3Lib.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..f3bb9a57
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp
@@ -0,0 +1,353 @@
+/* $Id: VBoxGuestR3LibAdditions.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Additions Info.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..3374326c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp
@@ -0,0 +1,120 @@
+/* $Id: VBoxGuestR3LibAutoLogon.cpp $ */
+/** @file
+ * VBoxGuestR3LibAutoLogon - Ring-3 utility functions for auto-logon modules
+ * (VBoxGINA / VBoxCredProv / pam_vbox).
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..2c192f0d
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp
@@ -0,0 +1,73 @@
+/* $Id: VBoxGuestR3LibBalloon.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Ballooning.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..f7653bcf
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp
@@ -0,0 +1,180 @@
+/* $Id: VBoxGuestR3LibClipboard.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Clipboard.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Connects to the clipboard 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 clipboard calls.
+ */
+VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient)
+{
+ int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient);
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
+ rc = VINF_PERMISSION_DENIED;
+ return rc;
+}
+
+
+/**
+ * Disconnect from the clipboard service.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ */
+VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient)
+{
+ return VbglR3HGCMDisconnect(idClient);
+}
+
+
+/**
+ * Get a host message.
+ *
+ * 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) VbglR3ClipboardGetHostMsg(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats)
+{
+ VBoxClipboardGetHostMsg Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, 2);
+ 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.
+ *
+ * @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 pv Where to store the data.
+ * @param cb The size of the buffer pointed to by pv.
+ * @param pcb The actual size of the host clipboard data. May be larger than cb.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcb)
+{
+ VBoxClipboardReadData Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_READ_DATA, 3);
+ VbglHGCMParmUInt32Set(&Msg.format, fFormat);
+ VbglHGCMParmPtrSet(&Msg.ptr, pv, cb);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbActual;
+ int rc2 = VbglHGCMParmUInt32Get(&Msg.size, &cbActual);
+ if (RT_SUCCESS(rc2))
+ {
+ *pcb = cbActual;
+ if (cbActual > cb)
+ return VINF_BUFFER_OVERFLOW;
+ return rc;
+ }
+ rc = rc2;
+ }
+ return rc;
+}
+
+
+/**
+ * Advertises guest clipboard formats to the host.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param fFormats The formats to advertise.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats)
+{
+ VBoxClipboardFormats Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_FORMATS, 1);
+ VbglHGCMParmUInt32Set(&Msg.formats, fFormats);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Send guest clipboard data to the host.
+ *
+ * This is usually called in reply to a VBOX_SHARED_CLIPBOARD_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 pv The data.
+ * @param cb The size of the data.
+ */
+VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb)
+{
+ VBoxClipboardWriteData Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, 2);
+ VbglHGCMParmUInt32Set(&Msg.format, fFormat);
+ VbglHGCMParmPtrSet(&Msg.ptr, pv, cb);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
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..3792db30
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp
@@ -0,0 +1,46 @@
+/* $Id: VBoxGuestR3LibCoreDump.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core Dumps.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..6f21aae0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp
@@ -0,0 +1,123 @@
+/* $Id: VBoxGuestR3LibCpuHotPlug.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, CPU Hot Plugging.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..63771dc7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp
@@ -0,0 +1,212 @@
+/* $Id: VBoxGuestR3LibCredentials.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, user credentials.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..f31ebc15
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp
@@ -0,0 +1,254 @@
+/** $Id: VBoxGuestR3LibDaemonize.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..1385fa52
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp
@@ -0,0 +1,1898 @@
+/* $Id: VBoxGuestR3LibDragAndDrop.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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.
+ * @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);
+
+ VBOXDNDNEXTMSGMSG Msg;
+ RT_ZERO(Msg);
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GET_NEXT_HOST_MSG, 3);
+ Msg.uMsg.SetUInt32(0);
+ Msg.cParms.SetUInt32(0);
+ Msg.fBlock.SetUInt32(fWait ? 1 : 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
+ rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
+ }
+
+ 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;
+
+ VBOXDNDHGACTIONMSG Msg;
+ RT_ZERO(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_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);
+
+ VBOXDNDHGLEAVEMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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);
+
+ VBOXDNDHGCANCELMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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);
+
+ VBOXDNDHGSENDDIRMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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);
+
+ VBOXDNDHGSENDFILEDATAMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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);
+
+ VBOXDNDHGSENDFILEHDRMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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.
+ * @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, DnDDroppedFiles *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;
+
+ /*
+ * 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. */
+
+ /*
+ * Create and query the (unique) drop target directory in the user's temporary directory.
+ */
+ int rc = pDroppedFiles->OpenTemp(0 /* fFlags */);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pvChunk);
+ return VERR_NO_MEMORY;
+ }
+
+ const char *pszDropDir = pDroppedFiles->GetDirAbs();
+ AssertPtr(pszDropDir);
+
+ /*
+ * Enter the main loop of retieving files + directories.
+ */
+ DnDURIObject objFile(DnDURIObject::Type_File);
+
+ char szPathName[RTPATH_MAX] = { 0 };
+ uint32_t cbPathName = 0;
+ uint32_t fFlags = 0;
+ uint32_t fMode = 0;
+
+ do
+ {
+ LogFlowFunc(("Wating 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_HG_SND_DIR:
+ {
+ rc = vbglR3DnDHGRecvDir(pCtx,
+ szPathName,
+ sizeof(szPathName),
+ &cbPathName,
+ &fMode);
+ LogFlowFunc(("HOST_DND_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 = pDroppedFiles->AddDir(pszPathAbs);
+
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cToRecvObjs);
+ cToRecvObjs--;
+ }
+
+ RTStrFree(pszPathAbs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ case HOST_DND_HG_SND_FILE_HDR:
+ case HOST_DND_HG_SND_FILE_DATA:
+ {
+ if (uNextMsg == HOST_DND_HG_SND_FILE_HDR)
+ {
+ rc = vbglR3DnDHGRecvFileHdr(pCtx,
+ szPathName,
+ sizeof(szPathName),
+ &fFlags,
+ &fMode,
+ &cbFileSize);
+ LogFlowFunc(("HOST_DND_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_HG_SND_FILE_DATA: "
+ "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc));
+ }
+
+ if ( RT_SUCCESS(rc)
+ && uNextMsg == HOST_DND_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 (!objFile.IsOpen())
+ {
+ RTCString strPathAbs(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_IRUSR | RTFS_UNIX_IWUSR;
+#endif
+ rc = objFile.OpenEx(strPathAbs, DnDURIObject::View_Target, fOpen, fCreationMode);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pDroppedFiles->AddFile(strPathAbs.c_str());
+ if (RT_SUCCESS(rc))
+ {
+ cbFileWritten = 0;
+ objFile.SetSize(cbFileSize);
+ }
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("ObjType=%RU32\n", objFile.GetType()));
+ rc = VERR_WRONG_ORDER;
+ }
+
+ RTStrFree(pszPathAbs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && uNextMsg == HOST_DND_HG_SND_FILE_DATA
+ && cbChunkRead)
+ {
+ uint32_t cbChunkWritten;
+ rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("HOST_DND_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 = objFile.IsComplete();
+ if (fClose)
+ {
+ Assert(cToRecvObjs);
+ cToRecvObjs--;
+ }
+
+ /* Only since protocol v2 we know the file size upfront. */
+ Assert(cbFileWritten <= cbFileSize);
+
+ if (fClose)
+ {
+ LogFlowFunc(("Closing file\n"));
+ objFile.Close();
+ }
+
+ break;
+ }
+ case HOST_DND_CANCEL:
+ {
+ rc = vbglR3DnDHGRecvCancel(pCtx);
+ if (RT_SUCCESS(rc))
+ rc = VERR_CANCELLED;
+ break;
+ }
+ default:
+ {
+ LogFlowFunc(("Message %RU32 not supported\n", 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))
+ {
+ objFile.Close();
+ pDroppedFiles->Rollback();
+ }
+ else
+ {
+ /** @todo Compare the URI 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 = pDroppedFiles->Reset(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_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));
+
+ VBOXDNDHGSENDDATAMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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->uProtocol >= 3); /* Only for protocol v3 and up. */
+
+ VBOXDNDHGSENDDATAHDRMSG Msg;
+ RT_ZERO(Msg);
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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, extended version.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pEnmType Where to store the meta data type. Optional.
+ * @param ppvData Returns the received meta data. Needs to be free'd by the caller. Optional.
+ * @param pcbData Where to store the size (in bytes) of the received meta data. Optional.
+ */
+static int vbglR3DnDHGRecvDataMainEx(PVBGLR3GUESTDNDCMDCTX pCtx,
+ VBGLR3GUESTDNDMETADATATYPE *pEnmType,
+ void **ppvData,
+ uint32_t *pcbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ /* The rest is optional. */
+
+ VBOXDNDDATAHDR dataHdr;
+ RT_ZERO(dataHdr);
+
+ AssertMsg(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"));
+
+ dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize;
+ dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
+ if (!dataHdr.pvMetaFmt)
+ return VERR_NO_MEMORY;
+
+ DnDURIList lstURI;
+ DnDDroppedFiles droppedFiles;
+
+ void *pvData = NULL;
+ uint64_t cbData = 0;
+
+ int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
+ if (RT_SUCCESS(rc))
+ {
+ /**
+ * 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. */
+ {
+ AssertPtr(pvData);
+ Assert(cbData);
+
+ rc = lstURI.SetFromURIData(pvData, cbData, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
+
+ if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
+ {
+ if (pvData)
+ {
+ /* Reuse data buffer to fill in the transformed URI file list. */
+ RTMemFree(pvData);
+ pvData = NULL;
+ }
+
+ RTCString strData = lstURI.GetRootEntries(droppedFiles.GetDirAbs());
+ Assert(!strData.isEmpty());
+
+ cbData = strData.length() + 1;
+ LogFlowFunc(("URI list has %zu bytes\n", cbData));
+
+ pvData = RTMemAlloc(cbData);
+ if (pvData)
+ {
+ memcpy(pvData, strData.c_str(), cbData);
+
+ if (pEnmType)
+ *pEnmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else /* Raw data. */
+ {
+ if (pEnmType)
+ *pEnmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
+ }
+ }
+
+ if (dataHdr.pvMetaFmt)
+ RTMemFree(dataHdr.pvMetaFmt);
+
+ if (RT_SUCCESS(rc))
+ {
+ if ( pvData
+ && cbData)
+ {
+ if (pcbData)
+ *pcbData = cbData;
+ if (ppvData)
+ *ppvData = pvData;
+ else
+ RTMemFree(pvData);
+ }
+ }
+ else if ( RT_FAILURE(rc)
+ && rc != VERR_CANCELLED)
+ {
+ if (pvData)
+ RTMemFree(pvData);
+
+ int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Main function for receiving the actual DnD data from the host.
+ *
+ * @returns IPRT status code.
+ * @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(pMeta, VERR_INVALID_POINTER);
+
+ int rc = vbglR3DnDHGRecvDataMainEx(pCtx,
+ &pMeta->enmType,
+ &pMeta->pvMeta,
+ &pMeta->cbMeta);
+ return rc;
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+/**
+ * Guest -> Host
+ * Utility function to receive the HOST_DND_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. */
+
+ VBOXDNDGHREQPENDINGMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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;
+
+ VBOXDNDGHDROPPEDMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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;
+
+ /* Set the default protocol version to use. */
+ pCtx->uProtocol = 3;
+ Assert(pCtx->uClientID);
+
+ /*
+ * 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);
+ LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
+
+ /*
+ * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT.
+ */
+ bool fSupportsConnectReq = false;
+ if (RT_SUCCESS(rc))
+ {
+ /* The guest property service might not be available. Not fatal. */
+ uint32_t uGuestPropSvcClientID;
+ rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
+ if (RT_SUCCESS(rc2))
+ {
+ char *pszHostVersion;
+ rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion);
+ if (RT_SUCCESS(rc2))
+ {
+ fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0;
+ LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq));
+ VbglR3GuestPropReadValueFree(pszHostVersion);
+ }
+
+ VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
+ }
+
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2));
+ }
+
+ if (fSupportsConnectReq)
+ {
+ /*
+ * 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.
+ */
+ VBOXDNDCONNECTMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 3);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
+ Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
+
+ rc2 = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_FAILURE(rc2))
+ fSupportsConnectReq = false;
+
+ LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
+ }
+
+ if (fSupportsConnectReq)
+ {
+ pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */
+ }
+ else /* GUEST_DND_CONNECT not supported; the user needs to upgrade the host. */
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, 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);
+ int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
+ if (RT_SUCCESS(rc))
+ pCtx->uClientID = 0;
+ 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 IPRT 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))
+ {
+ LogFlowFunc(("VM session ID changed to %RU64\n", uSessionID));
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFunc(("Handling uMsg=%RU32\n", uMsg));
+
+ switch(uMsg)
+ {
+ case HOST_DND_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_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_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_HG_EVT_LEAVE:
+ {
+ rc = vbglR3DnDHGRecvLeave(pCtx);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
+ break;
+ }
+ case HOST_DND_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_HG_SND_DIR:
+ RT_FALL_THROUGH();
+ case HOST_DND_HG_SND_FILE_DATA:
+ {
+ /*
+ * All messages in this case are handled internally
+ * by vbglR3DnDHGRecvDataMain() and must be specified
+ * by preceeding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR calls.
+ */
+ rc = VERR_WRONG_ORDER;
+ break;
+ }
+ case HOST_DND_CANCEL:
+ {
+ rc = vbglR3DnDHGRecvCancel(pCtx);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
+ break;
+ }
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ case HOST_DND_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_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))
+ {
+ VbglR3DnDEventFree(pEvent);
+ LogFlowFunc(("Failed with %Rrc\n", rc));
+ }
+ else
+ *ppEvent = pEvent;
+
+ 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;
+ if (pMeta->pvMeta)
+ {
+ Assert(pMeta->cbMeta);
+ RTMemFree(pMeta->pvMeta);
+ pMeta->cbMeta = 0;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ RTMemFree(pEvent);
+ pEvent = NULL;
+}
+
+VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBOXDNDHGACKOPMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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 */
+
+ VBOXDNDHGREQDATAMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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);
+
+ VBOXDNDHGEVTPROGRESSMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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;
+
+ VBOXDNDGHACKPENDINGMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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);
+
+ VBOXDNDGHSENDDATAHDRMSG MsgHdr;
+ RT_ZERO(MsgHdr);
+
+ VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_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))
+ {
+ VBOXDNDGHSENDDATAMSG MsgData;
+ RT_ZERO(MsgData);
+
+ VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_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;
+
+ HGCMFunctionParameter *pParm = &MsgData.u.v3.pvData;
+
+ while (cbSent < cbData)
+ {
+ cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
+ pParm->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 URI object containing the directory to send.
+ */
+static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
+{
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pObj->GetType() == DnDURIObject::Type_Directory, VERR_INVALID_PARAMETER);
+
+ RTCString strPath = pObj->GetDestPathAbs();
+ LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
+ strPath.c_str(), strPath.length(), pObj->GetMode()));
+
+ if (strPath.length() > RTPATH_MAX)
+ return VERR_INVALID_PARAMETER;
+
+ const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
+
+ VBOXDNDGHSENDDIRMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
+ Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
+ Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
+
+ 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 URI object containing the file to send.
+ */
+static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+ AssertReturn(pObj->GetType() == DnDURIObject::Type_File, VERR_INVALID_PARAMETER);
+ AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
+
+ uint32_t cbBuf = _64K; /** @todo Make this configurable? */
+ void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
+ if (!pvBuf)
+ return VERR_NO_MEMORY;
+
+ RTCString strPath = pObj->GetDestPathAbs();
+
+ LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
+ pObj->GetSize(), pObj->GetMode()));
+
+ VBOXDNDGHSENDFILEHDRMSG MsgHdr;
+ RT_ZERO(MsgHdr);
+
+ VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
+ MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
+ MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
+ MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
+ MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
+ MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
+ MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
+
+ int 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.
+ */
+ VBOXDNDGHSENDFILEDATAMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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 = pObj->GetSize();
+ uint64_t cbWrittenTotal = 0;
+ while (cbToReadTotal)
+ {
+ uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
+ uint32_t cbRead = 0;
+ if (cbToRead)
+ rc = pObj->Read(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, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
+ };
+ }
+
+ RTMemFree(pvBuf);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send an URI object from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pObj URI object to send from guest to the host.
+ */
+static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+
+ int rc;
+
+ switch (pObj->GetType())
+ {
+ case DnDURIObject::Type_Directory:
+ rc = vbglR3DnDGHSendDir(pCtx, pObj);
+ break;
+
+ case DnDURIObject::Type_File:
+ rc = vbglR3DnDGHSendFile(pCtx, pObj);
+ break;
+
+ default:
+ AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
+ 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);
+
+ /* For raw data only the total size is required to be specified. */
+ dataHdr.cbTotal = cbData;
+
+ return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send URI data from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pvData Block to URI data to send.
+ * @param cbData Size (in bytes) of URI data to send.
+ */
+static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+ RTCList<RTCString> lstPaths =
+ RTCString((const char *)pvData, cbData).split("\r\n");
+
+ /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
+ /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
+ uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
+
+ DnDURIList lstURI;
+ int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
+ * URI list the host needs to know upfront to set up the drag'n drop operation.
+ */
+ RTCString strRootDest = lstURI.GetRootEntries();
+ if (strRootDest.isNotEmpty())
+ {
+ void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
+ uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
+
+ /* The total size also contains the size of the meta data. */
+ uint64_t cbTotal = cbURLIist;
+ cbTotal += lstURI.GetTotalBytes();
+
+ /* We're going to send an URI 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 = lstURI.GetTotalCount();
+
+ rc = vbglR3DnDGHSendDataInternal(pCtx,
+ pvURIList, cbURLIist, &dataHdr);
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ while (!lstURI.IsEmpty())
+ {
+ DnDURIObject *pNextObj = lstURI.First();
+
+ rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
+ if (RT_FAILURE(rc))
+ break;
+
+ lstURI.RemoveFirst();
+ }
+ }
+
+ 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.
+ * @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));
+
+ int rc;
+ if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
+ {
+ /* Send file data. */
+ rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
+ }
+ else
+ rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
+
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = VbglR3DnDGHSendError(pCtx, rc);
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
+ }
+
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Send an error back to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param rcErr Error (IPRT-style) to send.
+ */
+VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBOXDNDGHEVTERRORMSG Msg;
+ RT_ZERO(Msg);
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_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;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
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..b96f18af
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp
@@ -0,0 +1,93 @@
+/* $Id: VBoxGuestR3LibEvent.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Events.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..583a2ea9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp
@@ -0,0 +1,80 @@
+/* $Id: VBoxGuestR3LibGR.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, GR.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..d3db9cdf
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp
@@ -0,0 +1,1531 @@
+/* $Id: VBoxGuestR3LibGuestCtrl.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest control.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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/HostServices/GuestControlSvc.h>
+
+#ifndef RT_OS_WINDOWS
+# include <signal.h>
+#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;
+}
+
+
+/**
+ * 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));
+}
+
+
+VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ int rc)
+{
+ return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */,
+ NULL /* pvPayload */, 0 /* cbPayload */);
+}
+
+
+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;
+}
+
+
+/**
+ * Asks a specific guest session to close.
+ *
+ * @return IPRT status code.
+ * @param pCtx Host context.
+ * @param fFlags Some kind of flag. Figure it out yourself.
+ ** @todo Docs!
+ */
+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));
+}
+
+
+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));
+}
+
+
+/**
+ * Retrieves a HOST_SESSION_CREATE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puProtocol,
+ char *pszUser, uint32_t cbUser,
+ char *pszPassword, uint32_t cbPassword,
+ char *pszDomain, uint32_t cbDomain,
+ uint32_t *pfFlags, uint32_t *pidSession)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(puProtocol, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDomain, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ int 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, pszUser, cbUser);
+ VbglHGCMParmPtrSet(&Msg.password, pszPassword, cbPassword);
+ VbglHGCMParmPtrSet(&Msg.domain, pszDomain, cbDomain);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.protocol.GetUInt32(puProtocol);
+ 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_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_EXEC_CMD message.
+ *
+ * @todo Move the parameters in an own struct!
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ char *pszCmd, uint32_t cbCmd,
+ uint32_t *pfFlags,
+ char *pszArgs, uint32_t cbArgs, uint32_t *pcArgs,
+ char *pszEnv, uint32_t *pcbEnv, uint32_t *pcEnvVars,
+ char *pszUser, uint32_t cbUser,
+ char *pszPassword, uint32_t cbPassword,
+ uint32_t *puTimeoutMS,
+ uint32_t *puPriority,
+ uint64_t *puAffinity, uint32_t cbAffinity, uint32_t *pcAffinity)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pszCmd, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszArgs, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcArgs, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszEnv, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbEnv, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcEnvVars, VERR_INVALID_POINTER);
+ AssertPtrReturn(puTimeoutMS, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ 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, pszCmd, cbCmd);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+ VbglHGCMParmUInt32Set(&Msg.num_args, 0);
+ VbglHGCMParmPtrSet(&Msg.args, pszArgs, cbArgs);
+ VbglHGCMParmUInt32Set(&Msg.num_env, 0);
+ VbglHGCMParmUInt32Set(&Msg.cb_env, 0);
+ VbglHGCMParmPtrSet(&Msg.env, pszEnv, *pcbEnv);
+ if (pCtx->uProtocol < 2)
+ {
+ AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
+ AssertReturn(cbUser, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
+ AssertReturn(pszPassword, VERR_INVALID_PARAMETER);
+
+ VbglHGCMParmPtrSet(&Msg.u.v1.username, pszUser, cbUser);
+ VbglHGCMParmPtrSet(&Msg.u.v1.password, pszPassword, cbPassword);
+ VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0);
+ }
+ else
+ {
+ AssertPtrReturn(puAffinity, VERR_INVALID_POINTER);
+ AssertReturn(cbAffinity, VERR_INVALID_PARAMETER);
+
+ VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0);
+ VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0);
+ VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0);
+ VbglHGCMParmPtrSet(&Msg.u.v2.affinity, puAffinity, cbAffinity);
+ }
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.flags.GetUInt32(pfFlags);
+ Msg.num_args.GetUInt32(pcArgs);
+ Msg.num_env.GetUInt32(pcEnvVars);
+ Msg.cb_env.GetUInt32(pcbEnv);
+ if (pCtx->uProtocol < 2)
+ Msg.u.v1.timeout.GetUInt32(puTimeoutMS);
+ else
+ {
+ Msg.u.v2.timeout.GetUInt32(puTimeoutMS);
+ Msg.u.v2.priority.GetUInt32(puPriority);
+ Msg.u.v2.num_affinity.GetUInt32(pcAffinity);
+ }
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Allocates and gets host data, based on the message id.
+ *
+ * This will block until data becomes available.
+ *
+ * @returns VBox status code.
+ ** @todo Docs!
+ */
+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);
+ VbglHGCMParmUInt32Set(&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);
+ VbglHGCMParmUInt32Set(&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_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;
+}
+
+
+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));
+}
+
+
+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));
+}
+
+
+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));
+}
+
+
+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));
+}
+
+
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc, uint32_t uWritten)
+{
+ 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, uWritten);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.write));
+}
+
+
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc, uint64_t uOffActual)
+{
+ 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, uOffActual);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.seek));
+}
+
+
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc, uint64_t uOffActual)
+{
+ 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, uOffActual);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.tell));
+}
+
+
+/**
+ * Callback for reporting a guest process status (along with some other stuff) to the host.
+ *
+ * @returns VBox status code.
+ ** @todo Docs!
+ */
+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.
+ ** @todo Docs!
+ */
+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.
+ ** @todo Docs!
+ */
+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..be8018e9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp
@@ -0,0 +1,924 @@
+/* $Id: VBoxGuestR3LibGuestProp.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#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/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);
+}
+
+
+/**
+ * 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;
+}
+
+#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.
+ */
+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)
+{
+ /*
+ * 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);
+ 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\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))
+ {
+ /* 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)
+ {
+ /* 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 = 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..c8fb7450
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp
@@ -0,0 +1,109 @@
+/* $Id: VBoxGuestR3LibGuestUser.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * guest user reporting / utility functions.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..c2f907a7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp
@@ -0,0 +1,95 @@
+/* $Id: VBoxGuestR3LibHGCM.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * generic HGCM.
+ */
+
+/*
+ * Copyright (C) 2015-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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)
+{
+ VBGLIOCHGCMCONNECT Info;
+ RT_ZERO(Info);
+ VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT);
+ Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
+ strcpy(Info.u.In.Loc.u.host.achName, pszServiceName);
+
+ int 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 VbglR3InfoSvcConnect().
+ */
+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..3e6ad14b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp
@@ -0,0 +1,225 @@
+/* $Id: VBoxGuestR3LibHostChannel.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Host Channel.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+#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..278b3076
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp
@@ -0,0 +1,201 @@
+/* $Id: */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, host version check.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h> /* Required for sscanf */
+#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)
+{
+ 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;
+}
+
+
+/** 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)
+{
+ Assert(idClient > 0);
+ AssertPtr(ppszVer);
+ return VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", ppszVer);
+}
+
+
+/** 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)
+{
+ Assert(idClient > 0);
+ AssertPtr(pszVer);
+ return VbglR3GuestPropWriteValue(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", pszVer);
+}
+
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..2444b7d6
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h
@@ -0,0 +1,119 @@
+/* $Id: VBoxGuestR3LibInternal.h $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 support library for the guest additions, Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#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..1663f2fe
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp
@@ -0,0 +1,84 @@
+/* $Id: VBoxGuestR3LibLog.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Logging.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..67def73a
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp
@@ -0,0 +1,125 @@
+/* $Id: VBoxGuestR3LibMisc.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Misc.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..a43bbf7c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp
@@ -0,0 +1,170 @@
+/* $Id: VBoxGuestR3LibModule.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared modules.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..8cfd8100
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp
@@ -0,0 +1,80 @@
+/* $Id: VBoxGuestR3LibMouse.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Mouse.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..c0396cbb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp
@@ -0,0 +1,108 @@
+/** $Id: VBoxGuestR3LibPidFile.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * Create a PID file.
+ */
+
+/*
+ * Copyright (C) 2015-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..e9707800
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp
@@ -0,0 +1,98 @@
+/* $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-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..78c19eb7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp
@@ -0,0 +1,172 @@
+/* $Id: VBoxGuestR3LibSeamless.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Seamless mode.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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)
+{
+ uint32_t fEvent = 0;
+ int rc;
+
+ AssertPtrReturn(pMode, VERR_INVALID_PARAMETER);
+ rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &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;
+}
+
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..0fb9a6a9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp
@@ -0,0 +1,422 @@
+/* $Id: VBoxGuestR3LibSharedFolders.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, shared folders.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..22e5caee
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp
@@ -0,0 +1,69 @@
+/* $Id: VBoxGuestR3LibStat.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Statistics.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..ecabde0e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp
@@ -0,0 +1,45 @@
+/* $Id: VBoxGuestR3LibTime.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Time.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..9757d2dc
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp
@@ -0,0 +1,575 @@
+/* $Id: VBoxGuestR3LibVideo.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Video.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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>
+
+#include <stdio.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 */
+
+#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 */
+ 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 */
+ 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))
+ {
+ unsigned cx = 0;
+ unsigned cy = 0;
+ unsigned cBits = 0;
+ unsigned x = 0;
+ unsigned y = 0;
+ unsigned fEnabled = 1;
+ char ch1 = 0;
+ char ch2 = 0;
+ int cMatches = sscanf(szModeParms, "%5ux%5ux%2u%c%5ux%5u,%1u%c", &cx, &cy, &cBits, &ch1, &x, &y, &fEnabled, &ch2);
+ if ( (cMatches == 7 && ch1 == ',')
+ || cMatches == 3)
+ {
+ 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);
+ rc = VINF_SUCCESS;
+ }
+ else if (cMatches < 0)
+ rc = VERR_READ_ERROR;
+ else
+ rc = VERR_PARSE_ERROR;
+ }
+ }
+
+ return rc;
+#else /* !VBOX_WITH_GUEST_PROPS */
+ 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..5e7099ca
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp
@@ -0,0 +1,54 @@
+/* $Id: VBoxGuestR3LibVrdp.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, VRDP.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* 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..a9c93970
--- /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-2019 Oracle Corporation
+ *
+ * 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/linux/Makefile b/src/VBox/Additions/common/VBoxGuest/linux/Makefile
new file mode 100644
index 00000000..23d32214
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/linux/Makefile
@@ -0,0 +1,168 @@
+# $Revision: 127855 $
+## @file
+# VirtualBox Guest Additions Module Makefile.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile.include.header
+
+MOD_NAME = vboxguest
+
+MOD_OBJS = \
+ VBoxGuest-linux.o \
+ VBoxGuest.o \
+ 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/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/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/RTStrICmpAscii.o \
+ common/string/RTStrNICmpAscii.o \
+ common/string/RTStrNCmp.o \
+ common/string/RTStrNLen.o \
+ common/string/stringalloc.o \
+ common/string/strformat.o \
+ common/string/strformatnum.o \
+ common/string/strformatrt.o \
+ common/string/strformattype.o \
+ common/string/strprintf.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
+ifeq ($(BUILD_TARGET_ARCH),x86)
+MOD_OBJS += \
+ common/math/gcc/divdi3.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
+ifeq ($(BUILD_TARGET_ARCH),amd64)
+MOD_OBJS += common/alloc/heapsimple.o
+endif
+
+MOD_DEFS = -DVBOX -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_GUEST \
+ -DIN_GUEST_R0 -DIN_MODULE -DRT_WITH_VBOX -DVBGL_VBOXGUEST \
+ -DVBOX_WITH_HGCM
+ifeq ($(BUILD_TARGET_ARCH),amd64)
+ MOD_DEFS += -DRT_ARCH_AMD64
+else
+ MOD_DEFS += -DRT_ARCH_X86
+endif
+ifeq ($(BUILD_TARGET_ARCH),amd64)
+ MOD_DEFS += -DVBOX_WITH_64_BITS_GUESTS
+endif
+MOD_INCL = $(addprefix -I$(KBUILD_EXTMOD),/ /include /r0drv/linux)
+MOD_INCL += $(addprefix -I$(KBUILD_EXTMOD)/vboxguest,/ /include /r0drv/linux)
+
+ifneq ($(wildcard $(KBUILD_EXTMOD)/vboxguest),)
+ MANGLING := $(KBUILD_EXTMOD)/vboxguest/include/VBox/VBoxGuestMangling.h
+else
+ MANGLING := $(KBUILD_EXTMOD)/include/VBox/VBoxGuestMangling.h
+endif
+ifeq ($(KERN_VERSION),24)
+ ## @todo move to MOD_DEFS when we have finished refactoring
+ MOD_CFLAGS = -DEXPORT_SYMTAB
+else
+ MOD_CFLAGS = -Wno-declaration-after-statement -include $(MANGLING) -fno-pie
+endif
+
+MOD_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.include.footer
+
+check: $(MOD_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/files_vboxguest b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest
new file mode 100755
index 00000000..0724653a
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest
@@ -0,0 +1,212 @@
+#!/bin/sh
+# $Id: files_vboxguest $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+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/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/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}/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp=>VBoxGuest.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/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.include.header=>Makefile.include.header \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.footer=>Makefile.include.footer \
+ ${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/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/math/gcc/divdi3.c=>common/math/gcc/divdi3.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/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/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/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_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..044013ae
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/netbsd/vboxguest.ioconf
@@ -0,0 +1,56 @@
+# $Id: vboxguest.ioconf $
+## @file
+# NetBSD vboxguest module configuration
+#
+
+#
+# Copyright (C) 2017 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+# 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..ecc08fa9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/solaris/deps.asm
@@ -0,0 +1,38 @@
+; $Id: deps.asm $
+;; @file
+; Solaris kernel module dependency
+;
+
+;
+; Copyright (C) 2012-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+%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..d578c024
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/solaris/load.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+# $Id: load.sh $
+## @file
+# For GA development.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+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..c8dca3f8
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf
@@ -0,0 +1,92 @@
+; $Id: VBoxGuest.inf $
+;; @file
+; INF file for installing the VirtualBox Windows guest driver.
+;
+
+;
+; Copyright (C) 2006-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+[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%
+2 = %VBoxControl.MediaDesc%
+3 = %VBoxTray.MediaDesc%
+
+[SourceDisksFiles]
+VBoxGuest.sys = 1
+VBoxControl.exe = 2
+VBoxTray.exe = 3
+
+[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, 0x00000000, %11%\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 Device"
+VBoxGuest_svcdesc = "VirtualBox Guest Driver"
+VBoxTray_svcdesc = "VirtualBox Guest Tray"
+
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..abbe33b0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.rc
@@ -0,0 +1,62 @@
+/* $Id: VBoxGuest.rc $ */
+/** @file
+ * VBoxGuest - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#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..5b680edb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp
@@ -0,0 +1,269 @@
+/* $Id: VBoxGuestInst.cpp $ */
+/** @file
+ * Small tool to (un)install the VBoxGuest device driver.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <VBox/VBoxGuest.h> /* for VBOXGUEST_SERVICE_NAME */
+#include <iprt/err.h>
+
+
+//#define TESTMODE
+
+
+
+static int installDriver(bool fStartIt)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ if (!hSMgrCreate)
+ {
+ printf("OpenSCManager(,,create) failed rc=%d\n", GetLastError());
+ return -1;
+ }
+
+ const char *pszSlashName = "\\VBoxGuest.sys";
+ char szDriver[MAX_PATH * 2];
+ GetCurrentDirectory(MAX_PATH, szDriver);
+ strcat(szDriver, pszSlashName);
+ if (GetFileAttributesA(szDriver) == INVALID_FILE_ATTRIBUTES)
+ {
+ GetSystemDirectory(szDriver, sizeof(szDriver));
+ strcat(strcat(szDriver, "\\drivers"), pszSlashName);
+
+ /* Try FAT name abbreviation. */
+ if (GetFileAttributesA(szDriver) == INVALID_FILE_ATTRIBUTES)
+ {
+ pszSlashName = "\\VBoxGst.sys";
+ GetCurrentDirectory(MAX_PATH, szDriver);
+ strcat(szDriver, pszSlashName);
+ if (GetFileAttributesA(szDriver) == INVALID_FILE_ATTRIBUTES)
+ {
+ GetSystemDirectory(szDriver, sizeof(szDriver));
+ strcat(strcat(szDriver, "\\drivers"), pszSlashName);
+
+ }
+ }
+ }
+
+ SC_HANDLE hService = CreateService(hSMgrCreate,
+ VBOXGUEST_SERVICE_NAME,
+ "VBoxGuest Support Driver",
+ SERVICE_QUERY_STATUS | (fStartIt ? SERVICE_START : 0),
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_BOOT_START,
+ SERVICE_ERROR_NORMAL,
+ szDriver,
+ "System",
+ NULL, NULL, NULL, NULL);
+ if (hService)
+ {
+ printf("Successfully created service '%s' for driver '%s'.\n", VBOXGUEST_SERVICE_NAME, szDriver);
+ if (fStartIt)
+ {
+ if (StartService(hService, 0, NULL))
+ printf("successfully started driver '%s'\n", szDriver);
+ else
+ printf("StartService failed: %d\n", GetLastError());
+ }
+ CloseServiceHandle(hService);
+ }
+ else
+ printf("CreateService failed! lasterr=%d (szDriver=%s)\n", GetLastError(), szDriver);
+ CloseServiceHandle(hSMgrCreate);
+ return hService ? 0 : -1;
+}
+
+static int uninstallDriver(void)
+{
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ if (!hSMgr)
+ {
+ printf("OpenSCManager(,,delete) failed rc=%d\n", GetLastError());
+ return -1;
+ }
+ SC_HANDLE hService = OpenService(hSMgr, 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)
+ rc = VINF_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)
+ rc = VINF_SUCCESS;
+ else
+ {
+ printf("Failed to stop service. status=%d (%#x)\n", Status.dwCurrentState, Status.dwCurrentState);
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if ( Status.dwCurrentState == SERVICE_STOP_PENDING
+ && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
+ rc = VERR_RESOURCE_BUSY; /* better than VERR_GENERAL_FAILURE */
+ else
+ {
+ printf("ControlService failed with dwErr=%u. status=%d (%#x)\n",
+ dwErr, Status.dwCurrentState, Status.dwCurrentState);
+ rc = -1;
+ }
+ }
+
+ /*
+ * Delete the service.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (DeleteService(hService))
+ rc = 0;
+ else
+ {
+ printf("DeleteService failed lasterr=%d\n", GetLastError());
+ rc = -1;
+ }
+ }
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ printf("OpenService failed lasterr=%d\n", GetLastError());
+ CloseServiceHandle(hSMgr);
+ return rc;
+}
+
+#ifdef TESTMODE
+
+static HANDLE openDriver(void)
+{
+ HANDLE hDevice;
+
+ hDevice = CreateFile(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)
+ {
+ printf("CreateFile did not work. GetLastError() 0x%x\n", GetLastError());
+ }
+ return hDevice;
+}
+
+static int closeDriver(HANDLE hDevice)
+{
+ CloseHandle(hDevice);
+ return 0;
+}
+
+static int performTest(void)
+{
+ int rc = 0;
+
+ HANDLE hDevice = openDriver();
+
+ if (hDevice != INVALID_HANDLE_VALUE)
+ closeDriver(hDevice);
+ else
+ printf("openDriver failed!\n");
+
+ return rc;
+}
+
+#endif /* TESTMODE */
+
+static int usage(char *programName)
+{
+ printf("error, syntax: %s [install|uninstall]\n", programName);
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ bool installMode;
+#ifdef TESTMODE
+ bool testMode = false;
+#endif
+
+ if (argc != 2)
+ return usage(argv[0]);
+
+ if (strcmp(argv[1], "install") == 0)
+ installMode = true;
+ else if (strcmp(argv[1], "uninstall") == 0)
+ installMode = false;
+#ifdef TESTMODE
+ else if (strcmp(argv[1], "test") == 0)
+ testMode = true;
+#endif
+ else
+ return usage(argv[0]);
+
+
+ int rc;
+#ifdef TESTMODE
+ if (testMode)
+ rc = performTest();
+ else
+#endif
+ if (installMode)
+ rc = installDriver(true);
+ else
+ rc = uninstallDriver();
+
+ if (rc == 0)
+ printf("operation completed successfully!\n");
+ else
+ printf("error: operation failed with status code %d\n", rc);
+
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/Makefile.kmk b/src/VBox/Additions/common/VBoxService/Makefile.kmk
new file mode 100644
index 00000000..1a6f2fce
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/Makefile.kmk
@@ -0,0 +1,206 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Cross Platform Guest Addition Services.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Incldue testcases.
+#
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# Target lists.
+#
+PROGRAMS += VBoxService
+
+# Enable the timesync service within VBoxService.
+VBOX_WITH_VBOXSERVICE_TIMESYNC := 1
+
+# Busybox-like toolbox, embedded into VBoxService.
+VBOX_WITH_VBOXSERVICE_TOOLBOX := 1
+
+# VM-management functions, like memory ballooning and statistics.
+VBOX_WITH_VBOXSERVICE_MANAGEMENT := 1
+
+if1of ($(KBUILD_TARGET), linux)
+ # CPU hotplugging.
+VBOX_WITH_VBOXSERVICE_CPUHOTPLUG := 1
+endif
+
+# Page Sharing (Page Fusion).
+if1of ($(KBUILD_TARGET), win)
+VBOX_WITH_VBOXSERVICE_PAGE_SHARING := 1
+endif
+
+ifdef VBOX_WITH_GUEST_PROPS
+VBOX_WITH_VBOXSERVICE_VMINFO := 1
+endif
+
+# Guest Control.
+ifdef VBOX_WITH_GUEST_CONTROL
+VBOX_WITH_VBOXSERVICE_CONTROL := 1
+endif
+
+#
+# VBoxService
+#
+if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Exe everywhere
+VBoxService_TEMPLATE = VBoxGuestR3Exe
+else
+VBoxService_TEMPLATE = NewVBoxGuestR3Exe
+endif
+
+# Define features to be activate.
+VBoxService_DEFS += \
+ $(if $(VBOX_WITH_VBOXSERVICE_CONTROL),VBOX_WITH_VBOXSERVICE_CONTROL,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_CPUHOTPLUG),VBOX_WITH_VBOXSERVICE_CPUHOTPLUG,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_MANAGEMENT),VBOX_WITH_VBOXSERVICE_MANAGEMENT,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_PAGE_SHARING),VBOX_WITH_VBOXSERVICE_PAGE_SHARING,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_TIMESYNC),VBOX_WITH_VBOXSERVICE_TIMESYNC,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_TOOLBOX),VBOX_WITH_VBOXSERVICE_TOOLBOX,) \
+ $(if $(VBOX_WITH_VBOXSERVICE_VMINFO),VBOX_WITH_VBOXSERVICE_VMINFO,)
+
+# Import global defines.
+VBoxService_DEFS += \
+ $(if $(VBOX_WITH_DBUS),VBOX_WITH_DBUS,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_WITH_HGCM),VBOX_WITH_HGCM,)
+
+VBoxService_DEFS += \
+ VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\"
+VBoxService_DEFS.win += _WIN32_WINNT=0x0501
+VBoxService_DEFS.os2 = VBOX_WITH_HGCM VBOX_WITH_VBOXSERVICE_CLIPBOARD
+
+VBoxService_SOURCES = \
+ VBoxService.cpp \
+ VBoxServiceUtils.cpp \
+ VBoxServiceStats.cpp
+
+ifdef VBOX_WITH_VBOXSERVICE_TIMESYNC
+VBoxService_SOURCES += \
+ VBoxServiceTimeSync.cpp
+endif
+
+ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
+VBoxService_SOURCES += \
+ VBoxServiceToolBox.cpp
+endif
+
+ifdef VBOX_WITH_VBOXSERVICE_CONTROL
+VBoxService_SOURCES += \
+ VBoxServiceControl.cpp \
+ VBoxServiceControlProcess.cpp \
+ VBoxServiceControlSession.cpp
+endif
+
+ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
+VBoxService_SOURCES += \
+ VBoxServiceBalloon.cpp
+ ifdef VBOX_WITH_MEMBALLOON
+VBoxService_DEFS += VBOX_WITH_MEMBALLOON
+ endif
+endif
+
+if1of ($(KBUILD_TARGET), win)
+VBoxService_SOURCES += \
+ VBoxServicePageSharing.cpp
+endif
+
+ifdef VBOX_WITH_VBOXSERVICE_VMINFO
+VBoxService_SOURCES.win += \
+ VBoxServiceVMInfo-win.cpp
+VBoxService_SOURCES += \
+ VBoxServiceVMInfo.cpp \
+ VBoxServicePropCache.cpp
+endif
+
+ifdef VBOX_WITH_VBOXSERVICE_CPUHOTPLUG
+VBoxService_SOURCES += \
+ VBoxServiceCpuHotPlug.cpp
+endif
+
+ifdef VBOX_WITH_SHARED_FOLDERS
+ if1of ($(KBUILD_TARGET), linux os2 solaris win)
+VBoxService_DEFS += VBOX_WITH_SHARED_FOLDERS
+VBoxService_SOURCES += \
+ VBoxServiceAutoMount.cpp
+VBoxService_SOURCES.linux += \
+ ../../linux/sharedfolders/vbsfmount.c
+VBoxService_LIBS.win += \
+ Mpr.Lib
+ endif
+endif
+
+VBoxService_SOURCES.win += \
+ VBoxService-win.rc \
+ VBoxService-win.cpp
+
+VBoxService_SOURCES.os2 = \
+ VBoxService-os2.def \
+ VBoxServiceClipboard-os2.cpp
+
+VBoxService_LDFLAGS.darwin = -framework IOKit
+
+VBoxService_LIBS += \
+ $(VBOX_LIB_IPRT_GUEST_R3) \
+ $(VBOX_LIB_VBGL_R3) \
+ $(VBOX_LIB_IPRT_GUEST_R3) # (The joy of unix linkers.)
+if1of ($(KBUILD_TARGET), linux)
+ VBoxService_LIBS += \
+ crypt
+endif
+ifdef VBOX_WITH_DBUS
+ if1of ($(KBUILD_TARGET), linux solaris) # FreeBSD?
+VBoxService_LIBS += \
+ dl
+ endif
+endif
+ifdef VBOX_WITH_GUEST_PROPS
+VBoxService_LIBS.win += \
+ Secur32.lib \
+ WtsApi32.lib \
+ Psapi.lib
+VBoxService_LIBS.solaris += \
+ nsl \
+ kstat \
+ contract
+endif
+
+VBoxServiceVMInfo.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+VBoxServiceVMInfo.cpp_DEPS = $(VBOX_SVN_REV_KMK)
+
+VBoxService_USES.win += vboximportchecker
+VBoxService_VBOX_IMPORT_CHECKER.win.x86 = nt31
+VBoxService_VBOX_IMPORT_CHECKER.win.amd64 = xp64
+
+
+#
+# The icon is configurable.
+#
+VBoxService-win.rc_INCS = $(VBoxService_0_OUTDIR)
+VBoxService-win.rc_DEPS = $(VBoxService_0_OUTDIR)/VBoxService-win-icon.rc
+VBoxService-win.rc_CLEAN = $(VBoxService_0_OUTDIR)/VBoxService-win-icon.rc
+
+# Icon include file.
+$$(VBoxService_0_OUTDIR)/VBoxService-win-icon.rc: $(VBOX_WINDOWS_ADDITIONS_ICON_FILE) $$(VBoxService_DEFPATH)/Makefile.kmk | $$(dir $$@)
+ $(RM) -f $@
+ $(APPEND) $@ 'IDI_VIRTUALBOX ICON DISCARDABLE "$(subst /,\\,$(VBOX_WINDOWS_ADDITIONS_ICON_FILE))"'
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-os2.def b/src/VBox/Additions/common/VBoxService/VBoxService-os2.def
new file mode 100644
index 00000000..ae2d6d9e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxService-os2.def
@@ -0,0 +1,23 @@
+; $Id: VBoxService-os2.def $
+;; @file
+; VBoxService - OS/2 definition file.
+;
+
+;
+; Copyright (C) 2007-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+
+
+NAME VBoxSvc
+DESCRIPTION 'VirtualBox Guest Additions Service for OS/2.'
+CODE SHARED
+DATA MULTIPLE NONSHARED
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp
new file mode 100644
index 00000000..1262cdf7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp
@@ -0,0 +1,660 @@
+/* $Id: VBoxService-win.cpp $ */
+/** @file
+ * VBoxService - Guest Additions Service Skeleton, Windows Specific Parts.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/system.h> /* For querying OS version. */
+#include <VBox/VBoxGuestLib.h>
+
+#define WIN32_NO_STATUS
+#include <iprt/win/ws2tcpip.h>
+#include <iprt/win/winsock2.h>
+#undef WIN32_NO_STATUS
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/win/iphlpapi.h>
+#include <process.h>
+#include <aclapi.h>
+#include <tlhelp32.h>
+#define _NTDEF_
+#include <Ntsecapi.h>
+
+#include "VBoxServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static DWORD g_dwWinServiceLastStatus = 0;
+SERVICE_STATUS_HANDLE g_hWinServiceStatus = NULL;
+/** The semaphore for the dummy Windows service. */
+static RTSEMEVENT g_WindowsEvent = NIL_RTSEMEVENT;
+
+static SERVICE_TABLE_ENTRY const g_aServiceTable[] =
+{
+ { VBOXSERVICE_NAME, vgsvcWinMain },
+ { NULL, NULL}
+};
+
+/** @name APIs from ADVAPI32.DLL.
+ * @{ */
+decltype(RegisterServiceCtrlHandlerExA) *g_pfnRegisterServiceCtrlHandlerExA; /**< W2K+ */
+decltype(ChangeServiceConfig2A) *g_pfnChangeServiceConfig2A; /**< W2K+ */
+decltype(GetNamedSecurityInfoA) *g_pfnGetNamedSecurityInfoA; /**< NT4+ */
+decltype(SetEntriesInAclA) *g_pfnSetEntriesInAclA; /**< NT4+ */
+decltype(SetNamedSecurityInfoA) *g_pfnSetNamedSecurityInfoA; /**< NT4+ */
+decltype(LsaNtStatusToWinError) *g_pfnLsaNtStatusToWinError; /**< NT3.51+ */
+/** @} */
+
+/** @name API from KERNEL32.DLL
+ * @{ */
+decltype(CreateToolhelp32Snapshot) *g_pfnCreateToolhelp32Snapshot; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Process32First) *g_pfnProcess32First; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Process32Next) *g_pfnProcess32Next; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Module32First) *g_pfnModule32First; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Module32Next) *g_pfnModule32Next; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(GetSystemTimeAdjustment) *g_pfnGetSystemTimeAdjustment; /**< NT 3.50+ */
+decltype(SetSystemTimeAdjustment) *g_pfnSetSystemTimeAdjustment; /**< NT 3.50+ */
+/** @} */
+
+/** @name API from NTDLL.DLL
+ * @{ */
+decltype(ZwQuerySystemInformation) *g_pfnZwQuerySystemInformation; /**< NT4 (where as NtQuerySystemInformation is W2K). */
+/** @} */
+
+/** @name API from IPHLPAPI.DLL
+ * @{ */
+decltype(GetAdaptersInfo) *g_pfnGetAdaptersInfo;
+/** @} */
+
+/** @name APIs from WS2_32.DLL
+ * @note WSAIoctl is not present in wsock32.dll, so no point in trying the
+ * fallback here.
+ * @{ */
+decltype(WSAStartup) *g_pfnWSAStartup;
+decltype(WSACleanup) *g_pfnWSACleanup;
+decltype(WSASocketA) *g_pfnWSASocketA;
+decltype(WSAIoctl) *g_pfnWSAIoctl;
+decltype(WSAGetLastError) *g_pfnWSAGetLastError;
+decltype(closesocket) *g_pfnclosesocket;
+decltype(inet_ntoa) *g_pfninet_ntoa;
+
+/** @} */
+
+/**
+ * Resolve APIs not present on older windows versions.
+ */
+void VGSvcWinResolveApis(void)
+{
+ RTLDRMOD hLdrMod;
+#define RESOLVE_SYMBOL(a_fn) do { RT_CONCAT(g_pfn, a_fn) = (decltype(a_fn) *)RTLdrGetFunction(hLdrMod, #a_fn); } while (0)
+
+ /* From ADVAPI32.DLL: */
+ int rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(RegisterServiceCtrlHandlerExA);
+ RESOLVE_SYMBOL(ChangeServiceConfig2A);
+ RESOLVE_SYMBOL(GetNamedSecurityInfoA);
+ RESOLVE_SYMBOL(SetEntriesInAclA);
+ RESOLVE_SYMBOL(SetNamedSecurityInfoA);
+ RESOLVE_SYMBOL(LsaNtStatusToWinError);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From KERNEL32.DLL: */
+ rc = RTLdrLoadSystem("kernel32.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(CreateToolhelp32Snapshot);
+ RESOLVE_SYMBOL(Process32First);
+ RESOLVE_SYMBOL(Process32Next);
+ RESOLVE_SYMBOL(Module32First);
+ RESOLVE_SYMBOL(Module32Next);
+ RESOLVE_SYMBOL(GetSystemTimeAdjustment);
+ RESOLVE_SYMBOL(SetSystemTimeAdjustment);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From NTDLL.DLL: */
+ rc = RTLdrLoadSystem("ntdll.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(ZwQuerySystemInformation);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From IPHLPAPI.DLL: */
+ rc = RTLdrLoadSystem("iphlpapi.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(GetAdaptersInfo);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From WS2_32.DLL: */
+ rc = RTLdrLoadSystem("ws2_32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(WSAStartup);
+ RESOLVE_SYMBOL(WSACleanup);
+ RESOLVE_SYMBOL(WSASocketA);
+ RESOLVE_SYMBOL(WSAIoctl);
+ RESOLVE_SYMBOL(WSAGetLastError);
+ RESOLVE_SYMBOL(closesocket);
+ RESOLVE_SYMBOL(inet_ntoa);
+ RTLdrClose(hLdrMod);
+ }
+}
+
+
+/**
+ * @todo Add full unicode support.
+ * @todo Add event log capabilities / check return values.
+ */
+static int vgsvcWinAddAceToObjectsSecurityDescriptor(LPTSTR pszObjName, SE_OBJECT_TYPE enmObjectType, const char *pszTrustee,
+ TRUSTEE_FORM enmTrusteeForm, DWORD dwAccessRights, ACCESS_MODE fAccessMode,
+ DWORD dwInheritance)
+{
+ int rc;
+ if ( g_pfnGetNamedSecurityInfoA
+ && g_pfnSetEntriesInAclA
+ && g_pfnSetNamedSecurityInfoA)
+ {
+ /* Get a pointer to the existing DACL. */
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pOldDACL = NULL;
+ DWORD rcWin = g_pfnGetNamedSecurityInfoA(pszObjName, enmObjectType, DACL_SECURITY_INFORMATION,
+ NULL, NULL, &pOldDACL, NULL, &pSD);
+ if (rcWin == ERROR_SUCCESS)
+ {
+ /* Initialize an EXPLICIT_ACCESS structure for the new ACE. */
+ EXPLICIT_ACCESSA ExplicitAccess;
+ RT_ZERO(ExplicitAccess);
+ ExplicitAccess.grfAccessPermissions = dwAccessRights;
+ ExplicitAccess.grfAccessMode = fAccessMode;
+ ExplicitAccess.grfInheritance = dwInheritance;
+ ExplicitAccess.Trustee.TrusteeForm = enmTrusteeForm;
+ ExplicitAccess.Trustee.ptstrName = (char *)pszTrustee;
+
+ /* Create a new ACL that merges the new ACE into the existing DACL. */
+ PACL pNewDACL = NULL;
+ rcWin = g_pfnSetEntriesInAclA(1, &ExplicitAccess, pOldDACL, &pNewDACL);
+ if (rcWin == ERROR_SUCCESS)
+ {
+ /* Attach the new ACL as the object's DACL. */
+ rcWin = g_pfnSetNamedSecurityInfoA(pszObjName, enmObjectType, DACL_SECURITY_INFORMATION,
+ NULL, NULL, pNewDACL, NULL);
+ if (rcWin == ERROR_SUCCESS)
+ rc = VINF_SUCCESS;
+ else
+ {
+ VGSvcError("AddAceToObjectsSecurityDescriptor: SetNamedSecurityInfo: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ if (pNewDACL)
+ LocalFree(pNewDACL);
+ }
+ else
+ {
+ VGSvcError("AddAceToObjectsSecurityDescriptor: SetEntriesInAcl: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ if (pSD)
+ LocalFree(pSD);
+ }
+ else
+ {
+ if (rcWin == ERROR_FILE_NOT_FOUND)
+ VGSvcError("AddAceToObjectsSecurityDescriptor: Object not found/installed: %s\n", pszObjName);
+ else
+ VGSvcError("AddAceToObjectsSecurityDescriptor: GetNamedSecurityInfo: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ }
+ else
+ rc = VINF_SUCCESS; /* fake it */
+ return rc;
+}
+
+
+/** Reports our current status to the SCM. */
+static BOOL vgsvcWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint)
+{
+ if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */
+ return FALSE;
+
+ VGSvcVerbose(2, "Setting service status to: %ld\n", dwStatus);
+ g_dwWinServiceLastStatus = dwStatus;
+
+ SERVICE_STATUS ss;
+ RT_ZERO(ss);
+
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ss.dwCurrentState = dwStatus;
+ /* Don't accept controls when in start pending state. */
+ if (ss.dwCurrentState != SERVICE_START_PENDING)
+ {
+ ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+
+ /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000 or earlier. This makes SCM angry. */
+ char szOSVersion[32];
+ int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion));
+ if (RT_SUCCESS(rc))
+ {
+ if (RTStrVersionCompare(szOSVersion, "5.1") >= 0)
+ ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
+ }
+ else
+ VGSvcError("Error determining OS version, rc=%Rrc\n", rc);
+ }
+
+ ss.dwWin32ExitCode = NO_ERROR;
+ ss.dwServiceSpecificExitCode = 0; /* Not used */
+ ss.dwCheckPoint = dwCheckPoint;
+ ss.dwWaitHint = 3000;
+
+ BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss);
+ if (!fStatusSet)
+ VGSvcError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n",
+ dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError());
+ return fStatusSet;
+}
+
+
+/**
+ * Reports SERVICE_STOP_PENDING to SCM.
+ *
+ * @param uCheckPoint Some number.
+ */
+void VGSvcWinSetStopPendingStatus(uint32_t uCheckPoint)
+{
+ vgsvcWinSetStatus(SERVICE_STOP_PENDING, uCheckPoint);
+}
+
+
+static RTEXITCODE vgsvcWinSetDesc(SC_HANDLE hService)
+{
+ /* On W2K+ there's ChangeServiceConfig2() which lets us set some fields
+ like a longer service description. */
+ if (g_pfnChangeServiceConfig2A)
+ {
+ /** @todo On Vista+ SERVICE_DESCRIPTION also supports localized strings! */
+ SERVICE_DESCRIPTION desc;
+ desc.lpDescription = VBOXSERVICE_DESCRIPTION;
+ if (!g_pfnChangeServiceConfig2A(hService, SERVICE_CONFIG_DESCRIPTION, &desc))
+ {
+ VGSvcError("Cannot set the service description! Error: %ld\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Installs the service.
+ */
+RTEXITCODE VGSvcWinInstall(void)
+{
+ VGSvcVerbose(1, "Installing service ...\n");
+
+ TCHAR imagePath[MAX_PATH] = { 0 };
+ GetModuleFileName(NULL, imagePath, sizeof(imagePath));
+
+ SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (hSCManager == NULL)
+ {
+ VGSvcError("Could not open SCM! Error: %ld\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE rc = RTEXITCODE_SUCCESS;
+ SC_HANDLE hService = CreateService(hSCManager,
+ VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+ imagePath, NULL, NULL, NULL, NULL, NULL);
+ if (hService != NULL)
+ VGSvcVerbose(0, "Service successfully installed!\n");
+ else
+ {
+ DWORD dwErr = GetLastError();
+ switch (dwErr)
+ {
+ case ERROR_SERVICE_EXISTS:
+ VGSvcVerbose(1, "Service already exists, just updating the service config.\n");
+ hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
+ if (hService)
+ {
+ if (ChangeServiceConfig (hService,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ imagePath,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ VBOXSERVICE_FRIENDLY_NAME))
+ VGSvcVerbose(1, "The service config has been successfully updated.\n");
+ else
+ rc = VGSvcError("Could not change service config! Error: %ld\n", GetLastError());
+ }
+ else
+ rc = VGSvcError("Could not open service! Error: %ld\n", GetLastError());
+ break;
+
+ default:
+ rc = VGSvcError("Could not create service! Error: %ld\n", dwErr);
+ break;
+ }
+ }
+
+ if (rc == RTEXITCODE_SUCCESS)
+ rc = vgsvcWinSetDesc(hService);
+
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSCManager);
+ return rc;
+}
+
+/**
+ * Uninstalls the service.
+ */
+RTEXITCODE VGSvcWinUninstall(void)
+{
+ VGSvcVerbose(1, "Uninstalling service ...\n");
+
+ SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
+ if (hSCManager == NULL)
+ {
+ VGSvcError("Could not open SCM! Error: %d\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE rcExit;
+ SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
+ if (hService != NULL)
+ {
+ if (DeleteService(hService))
+ {
+ /*
+ * ???
+ */
+ HKEY hKey = NULL;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System",
+ 0,
+ KEY_ALL_ACCESS,
+ &hKey)
+ == ERROR_SUCCESS)
+ {
+ RegDeleteKey(hKey, VBOXSERVICE_NAME);
+ RegCloseKey(hKey);
+ }
+
+ VGSvcVerbose(0, "Service successfully uninstalled!\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ rcExit = VGSvcError("Could not remove service! Error: %d\n", GetLastError());
+ CloseServiceHandle(hService);
+ }
+ else
+ rcExit = VGSvcError("Could not open service! Error: %d\n", GetLastError());
+ CloseServiceHandle(hSCManager);
+
+ return rcExit;
+}
+
+
+static int vgsvcWinStart(void)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Create a well-known SID for the "Builtin Users" group and modify the ACE
+ * for the shared folders miniport redirector DN (whatever DN means).
+ */
+ PSID pBuiltinUsersSID = NULL;
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
+ if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &pBuiltinUsersSID))
+ {
+ rc = vgsvcWinAddAceToObjectsSecurityDescriptor(TEXT("\\\\.\\VBoxMiniRdrDN"), SE_FILE_OBJECT,
+ (LPTSTR)pBuiltinUsersSID, TRUSTEE_IS_SID,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE, SET_ACCESS, NO_INHERITANCE);
+ /* If we don't find our "VBoxMiniRdrDN" (for Shared Folders) object above,
+ don't report an error; it just might be not installed. Otherwise this
+ would cause the SCM to hang on starting up the service. */
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ rc = VINF_SUCCESS;
+
+ FreeSid(pBuiltinUsersSID);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start the service.
+ */
+ vgsvcWinSetStatus(SERVICE_START_PENDING, 0);
+
+ rc = VGSvcStartServices();
+ if (RT_SUCCESS(rc))
+ {
+ vgsvcWinSetStatus(SERVICE_RUNNING, 0);
+ VGSvcMainWait();
+ }
+ else
+ {
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+#if 0 /** @todo r=bird: Enable this if SERVICE_CONTROL_STOP isn't triggered automatically */
+ VGSvcStopServices();
+#endif
+ }
+ }
+ else
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Service failed to start with rc=%Rrc!\n", rc);
+
+ return rc;
+}
+
+
+/**
+ * Call StartServiceCtrlDispatcher.
+ *
+ * The main() thread invokes this when not started in foreground mode. It
+ * won't return till the service is being shutdown (unless start up fails).
+ *
+ * @returns RTEXITCODE_SUCCESS on normal return after service shutdown.
+ * Something else on failure, error will have been reported.
+ */
+RTEXITCODE VGSvcWinEnterCtrlDispatcher(void)
+{
+ if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
+ return VGSvcError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!\n",
+ GetLastError(), g_pszProgName);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Event code to description.
+ *
+ * @returns String.
+ * @param dwEvent The event code.
+ */
+static const char *vgsvcWTSStateToString(DWORD dwEvent)
+{
+ switch (dwEvent)
+ {
+ case WTS_CONSOLE_CONNECT: return "A session was connected to the console terminal";
+ case WTS_CONSOLE_DISCONNECT: return "A session was disconnected from the console terminal";
+ case WTS_REMOTE_CONNECT: return "A session connected to the remote terminal";
+ case WTS_REMOTE_DISCONNECT: return "A session was disconnected from the remote terminal";
+ case WTS_SESSION_LOGON: return "A user has logged on to a session";
+ case WTS_SESSION_LOGOFF: return "A user has logged off the session";
+ case WTS_SESSION_LOCK: return "A session has been locked";
+ case WTS_SESSION_UNLOCK: return "A session has been unlocked";
+ case WTS_SESSION_REMOTE_CONTROL: return "A session has changed its remote controlled status";
+#ifdef WTS_SESSION_CREATE
+ case WTS_SESSION_CREATE: return "A session has been created";
+#endif
+#ifdef WTS_SESSION_TERMINATE
+ case WTS_SESSION_TERMINATE: return "The session has been terminated";
+#endif
+ default: return "Uknonwn state";
+ }
+}
+
+
+/**
+ * Common control handler.
+ *
+ * @returns Return code for NT5+.
+ * @param dwControl The control code.
+ */
+static DWORD vgsvcWinCtrlHandlerCommon(DWORD dwControl)
+{
+ DWORD rcRet = NO_ERROR;
+ switch (dwControl)
+ {
+ case SERVICE_CONTROL_INTERROGATE:
+ vgsvcWinSetStatus(g_dwWinServiceLastStatus, 0);
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ {
+ vgsvcWinSetStatus(SERVICE_STOP_PENDING, 0);
+
+ int rc2 = VGSvcStopServices();
+ if (RT_FAILURE(rc2))
+ rcRet = ERROR_GEN_FAILURE;
+ else
+ {
+ rc2 = VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
+ AssertRC(rc2);
+ }
+
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+ break;
+ }
+
+ default:
+ VGSvcVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
+ rcRet = ERROR_CALL_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Callback registered by RegisterServiceCtrlHandler on NT4 and earlier.
+ */
+static VOID WINAPI vgsvcWinCtrlHandlerNt4(DWORD dwControl)
+{
+ VGSvcVerbose(2, "Control handler (NT4): dwControl=%#x\n", dwControl);
+ vgsvcWinCtrlHandlerCommon(dwControl);
+}
+
+
+/**
+ * Callback registered by RegisterServiceCtrlHandler on NT5 and later.
+ */
+static DWORD WINAPI vgsvcWinCtrlHandlerNt5Plus(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
+{
+ VGSvcVerbose(2, "Control handler: dwControl=%#x, dwEventType=%#x\n", dwControl, dwEventType);
+ RT_NOREF1(lpContext);
+
+ switch (dwControl)
+ {
+ default:
+ return vgsvcWinCtrlHandlerCommon(dwControl);
+
+ case SERVICE_CONTROL_SESSIONCHANGE: /* Only Windows 2000 and up. */
+ {
+ AssertPtr(lpEventData);
+ PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
+ Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));
+
+ VGSvcVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
+ vgsvcWTSStateToString(dwEventType), pNotify->dwSessionId, dwEventType);
+
+ /* Handle all events, regardless of dwEventType. */
+ int rc2 = VGSvcVMInfoSignal();
+ AssertRC(rc2);
+
+ return NO_ERROR;
+ }
+ }
+}
+
+
+static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv)
+{
+ RT_NOREF2(argc, argv);
+ VGSvcVerbose(2, "Registering service control handler ...\n");
+ if (g_pfnRegisterServiceCtrlHandlerExA)
+ g_hWinServiceStatus = g_pfnRegisterServiceCtrlHandlerExA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt5Plus, NULL);
+ else
+ g_hWinServiceStatus = RegisterServiceCtrlHandlerA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt4);
+ if (g_hWinServiceStatus != NULL)
+ {
+ VGSvcVerbose(2, "Service control handler registered.\n");
+ vgsvcWinStart();
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ switch (dwErr)
+ {
+ case ERROR_INVALID_NAME:
+ VGSvcError("Invalid service name!\n");
+ break;
+ case ERROR_SERVICE_DOES_NOT_EXIST:
+ VGSvcError("Service does not exist!\n");
+ break;
+ default:
+ VGSvcError("Could not register service control handle! Error: %ld\n", dwErr);
+ break;
+ }
+ }
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-win.rc b/src/VBox/Additions/common/VBoxService/VBoxService-win.rc
new file mode 100644
index 00000000..0b5185ab
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxService-win.rc
@@ -0,0 +1,55 @@
+/* $Id: VBoxService-win.rc $ */
+/** @file
+ * VBoxService - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2008-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+#include "VBoxServiceResource-win.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 Additions Service\0"
+ VALUE "InternalName", "VBoxService\0"
+ VALUE "OriginalFilename", "VBoxService.exe\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 "VBoxService-win-icon.rc"
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService.cpp b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
new file mode 100644
index 00000000..d152b322
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxService.cpp
@@ -0,0 +1,1270 @@
+/* $Id: VBoxService.cpp $ */
+/** @file
+ * VBoxService - Guest Additions Service Skeleton.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc VBoxService
+ *
+ * VBoxService is a root daemon for implementing guest additions features.
+ *
+ * It is structured as one binary that contains many sub-services. The reason
+ * for this is partially historical and partially practical. The practical
+ * reason is that the VBoxService binary is typically statically linked, at
+ * least with IPRT and the guest library, so we save quite a lot of space having
+ * on single binary instead individual binaries for each sub-service and their
+ * helpers (currently up to 9 subservices and 8 helpers). The historical is
+ * simply that it started its life on OS/2 dreaming of conquring Windows next,
+ * so it kind of felt natural to have it all in one binary.
+ *
+ * Even if it's structured as a single binary, it is possible, by using command
+ * line options, to start each subservice as an individual process.
+ *
+ * Subservices:
+ * - @subpage pg_vgsvc_timesync "Time Synchronization"
+ * - @subpage pg_vgsvc_vminfo "VM Information"
+ * - @subpage pg_vgsvc_vmstats "VM Statistics"
+ * - @subpage pg_vgsvc_gstctrl "Guest Control"
+ * - @subpage pg_vgsvc_pagesharing "Page Sharing"
+ * - @subpage pg_vgsvc_memballoon "Memory Balooning"
+ * - @subpage pg_vgsvc_cpuhotplug "CPU Hot-Plugging"
+ * - @subpage pg_vgsvc_automount "Shared Folder Automounting"
+ * - @subpage pg_vgsvc_clipboard "Clipboard (OS/2 only)"
+ *
+ * Now, since the service predates a lot of stuff, including RTGetOpt, we're
+ * currently doing our own version of argument parsing here, which is kind of
+ * stupid. That will hopefully be cleaned up eventually.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+/** @todo LOG_GROUP*/
+#ifndef _MSC_VER
+# include <unistd.h>
+#endif
+#include <errno.h>
+#ifndef RT_OS_WINDOWS
+# include <signal.h>
+# ifdef RT_OS_OS2
+# define pthread_sigmask sigprocmask
+# endif
+#endif
+#ifdef RT_OS_FREEBSD
+# include <pthread.h>
+#endif
+
+#include <package-generated.h>
+#include "product-generated.h"
+
+#include <iprt/asm.h>
+#include <iprt/buildconfig.h>
+#include <iprt/initterm.h>
+#include <iprt/file.h>
+#ifdef DEBUG
+# include <iprt/memtracker.h>
+#endif
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include "VBoxServiceInternal.h"
+#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
+# include "VBoxServiceControl.h"
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
+# include "VBoxServiceToolBox.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The program name (derived from argv[0]). */
+char *g_pszProgName = (char *)"";
+/** The current verbosity level. */
+unsigned g_cVerbosity = 0;
+char g_szLogFile[RTPATH_MAX + 128] = "";
+char g_szPidFile[RTPATH_MAX] = "";
+/** Logging parameters. */
+/** @todo Make this configurable later. */
+static PRTLOGGER g_pLoggerRelease = NULL;
+static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
+static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
+static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
+/** Critical section for (debug) logging. */
+#ifdef DEBUG
+ RTCRITSECT g_csLog;
+#endif
+/** The default service interval (the -i | --interval) option). */
+uint32_t g_DefaultInterval = 0;
+#ifdef RT_OS_WINDOWS
+/** Signal shutdown to the Windows service thread. */
+static bool volatile g_fWindowsServiceShutdown;
+/** Event the Windows service thread waits for shutdown. */
+static RTSEMEVENT g_hEvtWindowsService;
+#endif
+
+/**
+ * The details of the services that has been compiled in.
+ */
+static struct
+{
+ /** Pointer to the service descriptor. */
+ PCVBOXSERVICE pDesc;
+ /** The worker thread. NIL_RTTHREAD if it's the main thread. */
+ RTTHREAD Thread;
+ /** Whether Pre-init was called. */
+ bool fPreInited;
+ /** Shutdown indicator. */
+ bool volatile fShutdown;
+ /** Indicator set by the service thread exiting. */
+ bool volatile fStopped;
+ /** Whether the service was started or not. */
+ bool fStarted;
+ /** Whether the service is enabled or not. */
+ bool fEnabled;
+} g_aServices[] =
+{
+#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
+ { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_TIMESYNC
+ { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_CLIPBOARD
+ { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_VMINFO
+ { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_CPUHOTPLUG
+ { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
+# ifdef VBOX_WITH_MEMBALLOON
+ { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
+# endif
+ { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#if defined(VBOX_WITH_VBOXSERVICE_PAGE_SHARING)
+ { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+ { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
+#endif
+};
+
+
+/*
+ * Default call-backs for services which do not need special behaviour.
+ */
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnPreInit, Default Implementation}
+ */
+DECLCALLBACK(int) VGSvcDefaultPreInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnOption, Default Implementation}
+ */
+DECLCALLBACK(int) VGSvcDefaultOption(const char **ppszShort, int argc,
+ char **argv, int *pi)
+{
+ NOREF(ppszShort);
+ NOREF(argc);
+ NOREF(argv);
+ NOREF(pi);
+
+ return -1;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit, Default Implementation}
+ */
+DECLCALLBACK(int) VGSvcDefaultInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm, Default Implementation}
+ */
+DECLCALLBACK(void) VGSvcDefaultTerm(void)
+{
+ return;
+}
+
+
+/**
+ * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
+ */
+static DECLCALLBACK(void) vgsvcLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
+{
+ /* Some introductory information. */
+ static RTTIMESPEC s_TimeSpec;
+ char szTmp[256];
+ if (enmPhase == RTLOGPHASE_BEGIN)
+ RTTimeNow(&s_TimeSpec);
+ RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
+
+ switch (enmPhase)
+ {
+ case RTLOGPHASE_BEGIN:
+ {
+ pfnLog(pLoggerRelease,
+ "VBoxService %s r%s (verbosity: %u) %s (%s %s) release log\n"
+ "Log opened %s\n",
+ RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity, VBOX_BUILD_TARGET,
+ __DATE__, __TIME__, szTmp);
+
+ int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
+
+ /* the package type is interesting for Linux distributions */
+ char szExecName[RTPATH_MAX];
+ char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+ pfnLog(pLoggerRelease,
+ "Executable: %s\n"
+ "Process ID: %u\n"
+ "Package type: %s"
+#ifdef VBOX_OSE
+ " (OSE)"
+#endif
+ "\n",
+ pszExecName ? pszExecName : "unknown",
+ RTProcSelf(),
+ VBOX_PACKAGE_STRING);
+ break;
+ }
+
+ case RTLOGPHASE_PREROTATE:
+ pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_POSTROTATE:
+ pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_END:
+ pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
+ break;
+
+ default:
+ /* nothing */
+ break;
+ }
+}
+
+
+/**
+ * Creates the default release logger outputting to the specified file.
+ *
+ * Pass NULL to disabled logging.
+ *
+ * @return IPRT status code.
+ * @param pszLogFile Filename for log output. NULL disables logging
+ * (r=bird: No, it doesn't!).
+ */
+int VGSvcLogCreate(const char *pszLogFile)
+{
+ /* Create release logger (stdout + file). */
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
+#ifdef DEBUG
+ "VBOXSERVICE_LOG",
+#else
+ "VBOXSERVICE_RELEASE_LOG",
+#endif
+ RT_ELEMENTS(s_apszGroups), s_apszGroups,
+ RTLOGDEST_STDOUT | RTLOGDEST_USER,
+ vgsvcLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
+ NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
+ if (RT_SUCCESS(rc))
+ {
+ /* register this logger as the release logger */
+ RTLogRelSetDefaultInstance(g_pLoggerRelease);
+
+ /* Explicitly flush the log in case of VBOXSERVICE_RELEASE_LOG=buffered. */
+ RTLogFlush(g_pLoggerRelease);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Logs a verbose message.
+ *
+ * @param pszFormat The message text.
+ * @param va Format arguments.
+ */
+void VGSvcLogV(const char *pszFormat, va_list va)
+{
+#ifdef DEBUG
+ int rc = RTCritSectEnter(&g_csLog);
+ if (RT_SUCCESS(rc))
+ {
+#endif
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, va);
+
+ AssertPtr(psz);
+ LogRel(("%s", psz));
+
+ RTStrFree(psz);
+#ifdef DEBUG
+ RTCritSectLeave(&g_csLog);
+ }
+#endif
+}
+
+
+/**
+ * Destroys the currently active logging instance.
+ */
+void VGSvcLogDestroy(void)
+{
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+}
+
+
+/**
+ * Displays the program usage message.
+ *
+ * @returns 1.
+ */
+static int vgsvcUsage(void)
+{
+ RTPrintf("Usage:\n"
+ " %-12s [-f|--foreground] [-v|--verbose] [-l|--logfile <file>]\n"
+ " [-p|--pidfile <file>] [-i|--interval <seconds>]\n"
+ " [--disable-<service>] [--enable-<service>]\n"
+ " [--only-<service>] [-h|-?|--help]\n", g_pszProgName);
+#ifdef RT_OS_WINDOWS
+ RTPrintf(" [-r|--register] [-u|--unregister]\n");
+#endif
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ if (g_aServices[j].pDesc->pszUsage)
+ RTPrintf("%s\n", g_aServices[j].pDesc->pszUsage);
+ RTPrintf("\n"
+ "Options:\n"
+ " -i | --interval The default interval.\n"
+ " -f | --foreground Don't daemonize the program. For debugging.\n"
+ " -l | --logfile <file> Enables logging to a file.\n"
+ " -p | --pidfile <file> Write the process ID to a file.\n"
+ " -v | --verbose Increment the verbosity level. For debugging.\n"
+ " -V | --version Show version information.\n"
+ " -h | -? | --help Show this message and exit with status 1.\n"
+ );
+#ifdef RT_OS_WINDOWS
+ RTPrintf(" -r | --register Installs the service.\n"
+ " -u | --unregister Uninstall service.\n");
+#endif
+
+ RTPrintf("\n"
+ "Service-specific options:\n");
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ RTPrintf(" --enable-%-14s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
+ RTPrintf(" --disable-%-13s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
+ RTPrintf(" --only-%-16s Only enables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
+ if (g_aServices[j].pDesc->pszOptions)
+ RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
+ }
+ RTPrintf("\n"
+ " Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
+
+ return 1;
+}
+
+
+/**
+ * Displays an error message.
+ *
+ * @returns RTEXITCODE_FAILURE.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+RTEXITCODE VGSvcError(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ LogRel(("Error: %s", psz));
+
+ RTStrFree(psz);
+
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Displays a verbose message based on the currently
+ * set global verbosity level.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+void VGSvcVerbose(unsigned iLevel, const char *pszFormat, ...)
+{
+ if (iLevel <= g_cVerbosity)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ VGSvcLogV(pszFormat, va);
+ va_end(va);
+ }
+}
+
+
+/**
+ * Reports the current VBoxService 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.
+ */
+int VGSvcReportStatus(VBoxGuestFacilityStatus enmStatus)
+{
+ /*
+ * VBoxGuestFacilityStatus_Failed is sticky.
+ */
+ static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
+ VGSvcVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
+ if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
+ {
+ int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService, enmStatus, 0 /* Flags */);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
+ return rc;
+ }
+ s_enmLastStatus = enmStatus;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets a 32-bit value argument.
+ * @todo Get rid of this and VGSvcArgString() as soon as we have RTOpt handling.
+ *
+ * @returns 0 on success, non-zero exit code on error.
+ * @param argc The argument count.
+ * @param argv The argument vector
+ * @param psz Where in *pi to start looking for the value argument.
+ * @param pi Where to find and perhaps update the argument index.
+ * @param pu32 Where to store the 32-bit value.
+ * @param u32Min The minimum value.
+ * @param u32Max The maximum value.
+ */
+int VGSvcArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
+{
+ if (*psz == ':' || *psz == '=')
+ psz++;
+ if (!*psz)
+ {
+ if (*pi + 1 >= argc)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing value for the '%s' argument\n", argv[*pi]);
+ psz = argv[++*pi];
+ }
+
+ char *pszNext;
+ int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
+ if (RT_FAILURE(rc) || *pszNext)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to convert interval '%s' to a number\n", psz);
+ if (*pu32 < u32Min || *pu32 > u32Max)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The timesync interval of %RU32 seconds is out of range [%RU32..%RU32]\n",
+ *pu32, u32Min, u32Max);
+ return 0;
+}
+
+
+/** @todo Get rid of this and VGSvcArgUInt32() as soon as we have RTOpt handling. */
+static int vgsvcArgString(int argc, char **argv, const char *psz, int *pi, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+
+ if (*psz == ':' || *psz == '=')
+ psz++;
+ if (!*psz)
+ {
+ if (*pi + 1 >= argc)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing string for the '%s' argument\n", argv[*pi]);
+ psz = argv[++*pi];
+ }
+
+ if (!RTStrPrintf(pszBuf, cbBuf, "%s", psz))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "String for '%s' argument too big\n", argv[*pi]);
+ return 0;
+}
+
+
+/**
+ * The service thread.
+ *
+ * @returns Whatever the worker function returns.
+ * @param ThreadSelf My thread handle.
+ * @param pvUser The service index.
+ */
+static DECLCALLBACK(int) vgsvcThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+ const unsigned i = (uintptr_t)pvUser;
+
+#ifndef RT_OS_WINDOWS
+ /*
+ * Block all signals for this thread. Only the main thread will handle signals.
+ */
+ sigset_t signalMask;
+ sigfillset(&signalMask);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+#endif
+
+ int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
+ ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
+ RTThreadUserSignal(ThreadSelf);
+ return rc;
+}
+
+
+/**
+ * Lazily calls the pfnPreInit method on each service.
+ *
+ * @returns VBox status code, error message displayed.
+ */
+static RTEXITCODE vgsvcLazyPreInit(void)
+{
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ if (!g_aServices[j].fPreInited)
+ {
+ int rc = g_aServices[j].pDesc->pfnPreInit();
+ if (RT_FAILURE(rc))
+ return VGSvcError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
+ g_aServices[j].fPreInited = true;
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Count the number of enabled services.
+ */
+static unsigned vgsvcCountEnabledServices(void)
+{
+ unsigned cEnabled = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
+ cEnabled += g_aServices[i].fEnabled;
+ return cEnabled;
+}
+
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Console control event callback.
+ *
+ * @returns TRUE if handled, FALSE if not.
+ * @param dwCtrlType The control event type.
+ *
+ * @remarks This is generally called on a new thread, so we're racing every
+ * other thread in the process.
+ */
+static BOOL WINAPI vgsvcWinConsoleControlHandler(DWORD dwCtrlType)
+{
+ int rc = VINF_SUCCESS;
+ bool fEventHandled = FALSE;
+ switch (dwCtrlType)
+ {
+ /* User pressed CTRL+C or CTRL+BREAK or an external event was sent
+ * via GenerateConsoleCtrlEvent(). */
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_C_EVENT:
+ VGSvcVerbose(2, "ControlHandler: Received break/close event\n");
+ rc = VGSvcStopServices();
+ fEventHandled = TRUE;
+ break;
+ default:
+ break;
+ /** @todo Add other events here. */
+ }
+
+ if (RT_FAILURE(rc))
+ VGSvcError("ControlHandler: Event %ld handled with error rc=%Rrc\n",
+ dwCtrlType, rc);
+ return fEventHandled;
+}
+#endif /* RT_OS_WINDOWS */
+
+
+/**
+ * Starts the service.
+ *
+ * @returns VBox status code, errors are fully bitched.
+ *
+ * @remarks Also called from VBoxService-win.cpp, thus not static.
+ */
+int VGSvcStartServices(void)
+{
+ int rc;
+
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Init);
+
+ /*
+ * Initialize the services.
+ */
+ VGSvcVerbose(2, "Initializing services ...\n");
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ if (g_aServices[j].fEnabled)
+ {
+ rc = g_aServices[j].pDesc->pfnInit();
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_SERVICE_DISABLED)
+ {
+ VGSvcError("Service '%s' failed to initialize: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
+ return rc;
+ }
+
+ g_aServices[j].fEnabled = false;
+ VGSvcVerbose(0, "Service '%s' was disabled because of missing functionality\n", g_aServices[j].pDesc->pszName);
+ }
+ }
+
+ /*
+ * Start the service(s).
+ */
+ VGSvcVerbose(2, "Starting services ...\n");
+ rc = VINF_SUCCESS;
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ if (!g_aServices[j].fEnabled)
+ continue;
+
+ VGSvcVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
+ rc = RTThreadCreate(&g_aServices[j].Thread, vgsvcThread, (void *)(uintptr_t)j, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("RTThreadCreate failed, rc=%Rrc\n", rc);
+ break;
+ }
+ g_aServices[j].fStarted = true;
+
+ /* Wait for the thread to initialize. */
+ /** @todo There is a race between waiting and checking
+ * the fShutdown flag of a thread here and processing
+ * the thread's actual worker loop. If the thread decides
+ * to exit the loop before we skipped the fShutdown check
+ * below the service will fail to start! */
+ /** @todo This presumably means either a one-shot service or that
+ * something has gone wrong. In the second case treating it as failure
+ * to start is probably right, so we need a way to signal the first
+ * rather than leaving the idle thread hanging around. A flag in the
+ * service description? */
+ RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
+ if (g_aServices[j].fShutdown)
+ {
+ VGSvcError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(1, "All services started.\n");
+ else
+ {
+ VGSvcError("An error occcurred while the services!\n");
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
+ }
+ return rc;
+}
+
+
+/**
+ * Stops and terminates the services.
+ *
+ * This should be called even when VBoxServiceStartServices fails so it can
+ * clean up anything that we succeeded in starting.
+ *
+ * @remarks Also called from VBoxService-win.cpp, thus not static.
+ */
+int VGSvcStopServices(void)
+{
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Terminating);
+
+ /*
+ * Signal all the services.
+ */
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
+
+ /*
+ * Do the pfnStop callback on all running services.
+ */
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ if (g_aServices[j].fStarted)
+ {
+ VGSvcVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
+ g_aServices[j].pDesc->pfnStop();
+ }
+
+ VGSvcVerbose(3, "All stop functions for services called\n");
+
+ /*
+ * Wait for all the service threads to complete.
+ */
+ int rc = VINF_SUCCESS;
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
+ continue;
+ if (g_aServices[j].Thread != NIL_RTTHREAD)
+ {
+ VGSvcVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
+ int rc2 = VINF_SUCCESS;
+ for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
+ {
+ rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
+ if (RT_SUCCESS(rc2))
+ break;
+#ifdef RT_OS_WINDOWS
+ /* Notify SCM that it takes a bit longer ... */
+ VGSvcWinSetStopPendingStatus(i + j*32);
+#endif
+ }
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
+ rc = rc2;
+ }
+ }
+ VGSvcVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
+ g_aServices[j].pDesc->pfnTerm();
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Wake up and tell the main() thread that we're shutting down (it's
+ * sleeping in VBoxServiceMainWait).
+ */
+ ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
+ if (g_hEvtWindowsService != NIL_RTSEMEVENT)
+ {
+ VGSvcVerbose(3, "Stopping the main thread...\n");
+ int rc2 = RTSemEventSignal(g_hEvtWindowsService);
+ AssertRC(rc2);
+ }
+#endif
+
+ VGSvcVerbose(2, "Stopping services returning: %Rrc\n", rc);
+ VGSvcReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
+ return rc;
+}
+
+
+/**
+ * Block the main thread until the service shuts down.
+ *
+ * @remarks Also called from VBoxService-win.cpp, thus not static.
+ */
+void VGSvcMainWait(void)
+{
+ int rc;
+
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Active);
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Wait for the semaphore to be signalled.
+ */
+ VGSvcVerbose(1, "Waiting in main thread\n");
+ rc = RTSemEventCreate(&g_hEvtWindowsService);
+ AssertRC(rc);
+ while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown))
+ {
+ rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+ RTSemEventDestroy(g_hEvtWindowsService);
+ g_hEvtWindowsService = NIL_RTSEMEVENT;
+#else
+ /*
+ * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking
+ * all important signals.
+ *
+ * The annoying EINTR/ERESTART loop is for the benefit of Solaris where
+ * sigwait returns when we receive a SIGCHLD. Kind of makes sense since
+ * the signal has to be delivered... Anyway, darwin (10.9.5) has a much
+ * worse way of dealing with SIGCHLD, apparently it'll just return any
+ * of the signals we're waiting on when SIGCHLD becomes pending on this
+ * thread. So, we wait for SIGCHLD here and ignores it.
+ */
+ sigset_t signalMask;
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, SIGHUP);
+ sigaddset(&signalMask, SIGINT);
+ sigaddset(&signalMask, SIGQUIT);
+ sigaddset(&signalMask, SIGABRT);
+ sigaddset(&signalMask, SIGTERM);
+ sigaddset(&signalMask, SIGCHLD);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ int iSignal;
+ do
+ {
+ iSignal = -1;
+ rc = sigwait(&signalMask, &iSignal);
+ }
+ while ( rc == EINTR
+# ifdef ERESTART
+ || rc == ERESTART
+# endif
+ || iSignal == SIGCHLD
+ );
+
+ VGSvcVerbose(3, "VGSvcMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
+#endif /* !RT_OS_WINDOWS */
+}
+
+
+int main(int argc, char **argv)
+{
+ RTEXITCODE rcExit;
+
+ /*
+ * Init globals and such.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ g_pszProgName = RTPathFilename(argv[0]);
+#ifdef RT_OS_WINDOWS
+ VGSvcWinResolveApis();
+#endif
+#ifdef DEBUG
+ rc = RTCritSectInit(&g_csLog);
+ AssertRC(rc);
+#endif
+
+#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
+ /*
+ * Run toolbox code before all other stuff since these things are simpler
+ * shell/file/text utility like programs that just happens to be inside
+ * VBoxService and shouldn't be subject to /dev/vboxguest, pid-files and
+ * global mutex restrictions.
+ */
+ if (VGSvcToolboxMain(argc, argv, &rcExit))
+ return rcExit;
+#endif
+
+ bool fUserSession = false;
+#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
+ /*
+ * Check if we're the specially spawned VBoxService.exe process that
+ * handles a guest control session.
+ */
+ if ( argc >= 2
+ && !RTStrICmp(argv[1], "guestsession"))
+ fUserSession = true;
+#endif
+
+ /*
+ * Connect to the kernel part before daemonizing so we can fail and
+ * complain if there is some kind of problem. We need to initialize the
+ * guest lib *before* we do the pre-init just in case one of services needs
+ * do to some initial stuff with it.
+ */
+ if (fUserSession)
+ rc = VbglR3InitUser();
+ else
+ rc = VbglR3Init();
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_ACCESS_DENIED)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Insufficient privileges to start %s! Please start with Administrator/root privileges!\n",
+ g_pszProgName);
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "VbglR3Init failed with rc=%Rrc\n", rc);
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Check if we're the specially spawned VBoxService.exe process that
+ * handles page fusion. This saves an extra statically linked executable.
+ */
+ if ( argc == 2
+ && !RTStrICmp(argv[1], "pagefusion"))
+ return VGSvcPageSharingWorkerChild();
+#endif
+
+#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
+ /*
+ * Check if we're the specially spawned VBoxService.exe process that
+ * handles a guest control session.
+ */
+ if (fUserSession)
+ return VGSvcGstCtrlSessionSpawnInit(argc, argv);
+#endif
+
+ /*
+ * Parse the arguments.
+ *
+ * Note! This code predates RTGetOpt, thus the manual parsing.
+ */
+ bool fDaemonize = true;
+ bool fDaemonized = false;
+ for (int i = 1; i < argc; i++)
+ {
+ const char *psz = argv[i];
+ if (*psz != '-')
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'\n", psz);
+ psz++;
+
+ /* translate long argument to short */
+ if (*psz == '-')
+ {
+ psz++;
+ size_t cch = strlen(psz);
+#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
+ && !memcmp(psz, strconst, sizeof(strconst) - 1) )
+ if (MATCHES("foreground"))
+ psz = "f";
+ else if (MATCHES("verbose"))
+ psz = "v";
+ else if (MATCHES("version"))
+ psz = "V";
+ else if (MATCHES("help"))
+ psz = "h";
+ else if (MATCHES("interval"))
+ psz = "i";
+#ifdef RT_OS_WINDOWS
+ else if (MATCHES("register"))
+ psz = "r";
+ else if (MATCHES("unregister"))
+ psz = "u";
+#endif
+ else if (MATCHES("logfile"))
+ psz = "l";
+ else if (MATCHES("pidfile"))
+ psz = "p";
+ else if (MATCHES("daemonized"))
+ {
+ fDaemonized = true;
+ continue;
+ }
+ else
+ {
+ bool fFound = false;
+
+ if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-")))
+ for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+ if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
+ g_aServices[j].fEnabled = true;
+
+ if (cch > sizeof("disable-") && !memcmp(psz, RT_STR_TUPLE("disable-")))
+ for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+ if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
+ g_aServices[j].fEnabled = false;
+
+ if (cch > sizeof("only-") && !memcmp(psz, RT_STR_TUPLE("only-")))
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName);
+ if (g_aServices[j].fEnabled)
+ fFound = true;
+ }
+
+ if (!fFound)
+ {
+ rcExit = vgsvcLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+ {
+ rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
+ fFound = rc == VINF_SUCCESS;
+ if (fFound)
+ break;
+ if (rc != -1)
+ return rc;
+ }
+ }
+ if (!fFound)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%s'\n", argv[i]);
+ continue;
+ }
+#undef MATCHES
+ }
+
+ /* handle the string of short options. */
+ do
+ {
+ switch (*psz)
+ {
+ case 'i':
+ rc = VGSvcArgUInt32(argc, argv, psz + 1, &i,
+ &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
+ if (rc)
+ return rc;
+ psz = NULL;
+ break;
+
+ case 'f':
+ fDaemonize = false;
+ break;
+
+ case 'v':
+ g_cVerbosity++;
+ break;
+
+ case 'V':
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return RTEXITCODE_SUCCESS;
+
+ case 'h':
+ case '?':
+ return vgsvcUsage();
+
+#ifdef RT_OS_WINDOWS
+ case 'r':
+ return VGSvcWinInstall();
+
+ case 'u':
+ return VGSvcWinUninstall();
+#endif
+
+ case 'l':
+ {
+ rc = vgsvcArgString(argc, argv, psz + 1, &i,
+ g_szLogFile, sizeof(g_szLogFile));
+ if (rc)
+ return rc;
+ psz = NULL;
+ break;
+ }
+
+ case 'p':
+ {
+ rc = vgsvcArgString(argc, argv, psz + 1, &i,
+ g_szPidFile, sizeof(g_szPidFile));
+ if (rc)
+ return rc;
+ psz = NULL;
+ break;
+ }
+
+ default:
+ {
+ rcExit = vgsvcLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ bool fFound = false;
+ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+ {
+ rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
+ fFound = rc == VINF_SUCCESS;
+ if (fFound)
+ break;
+ if (rc != -1)
+ return rc;
+ }
+ if (!fFound)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%c' (%s)\n", *psz, argv[i]);
+ break;
+ }
+ }
+ } while (psz && *++psz);
+ }
+
+ /* Check that at least one service is enabled. */
+ if (vgsvcCountEnabledServices() == 0)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n");
+
+ rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log '%s', rc=%Rrc\n",
+ g_szLogFile[0] ? g_szLogFile : "<None>", rc);
+
+ /* Call pre-init if we didn't do it already. */
+ rcExit = vgsvcLazyPreInit();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Make sure only one instance of VBoxService runs at a time. Create a
+ * global mutex for that.
+ *
+ * Note! The \\Global\ namespace was introduced with Win2K, thus the
+ * version check.
+ * Note! If the mutex exists CreateMutex will open it and set last error to
+ * ERROR_ALREADY_EXISTS.
+ */
+ OSVERSIONINFOEX OSInfoEx;
+ RT_ZERO(OSInfoEx);
+ OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ SetLastError(NO_ERROR);
+ HANDLE hMutexAppRunning;
+ if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
+ && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */)
+ hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME);
+ else
+ hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
+ if (hMutexAppRunning == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ if ( dwErr == ERROR_ALREADY_EXISTS
+ || dwErr == ERROR_ACCESS_DENIED)
+ {
+ VGSvcError("%s is already running! Terminating.\n", g_pszProgName);
+ return RTEXITCODE_FAILURE;
+ }
+
+ VGSvcError("CreateMutex failed with last error %u! Terminating.\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+
+#else /* !RT_OS_WINDOWS */
+ /** @todo Add PID file creation here? */
+#endif /* !RT_OS_WINDOWS */
+
+ VGSvcVerbose(0, "%s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
+
+ /*
+ * Daemonize if requested.
+ */
+ if (fDaemonize && !fDaemonized)
+ {
+#ifdef RT_OS_WINDOWS
+ VGSvcVerbose(2, "Starting service dispatcher ...\n");
+ rcExit = VGSvcWinEnterCtrlDispatcher();
+#else
+ VGSvcVerbose(1, "Daemonizing...\n");
+ rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */,
+ false /* fRespawn */, NULL /* pcRespawn */);
+ if (RT_FAILURE(rc))
+ return VGSvcError("Daemon failed: %Rrc\n", rc);
+ /* in-child */
+#endif
+ }
+#ifdef RT_OS_WINDOWS
+ else
+#endif
+ {
+ /*
+ * Windows: We're running the service as a console application now. Start the
+ * services, enter the main thread's run loop and stop them again
+ * when it returns.
+ *
+ * POSIX: This is used for both daemons and console runs. Start all services
+ * and return immediately.
+ */
+#ifdef RT_OS_WINDOWS
+# ifndef RT_OS_NT4 /** @todo r=bird: What's RT_OS_NT4??? */
+ /* Install console control handler. */
+ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)vgsvcWinConsoleControlHandler, TRUE /* Add handler */))
+ {
+ VGSvcError("Unable to add console control handler, error=%ld\n", GetLastError());
+ /* Just skip this error, not critical. */
+ }
+# endif /* !RT_OS_NT4 */
+#endif /* RT_OS_WINDOWS */
+ rc = VGSvcStartServices();
+ RTFILE hPidFile = NIL_RTFILE;
+ if (RT_SUCCESS(rc))
+ if (g_szPidFile[0])
+ rc = VbglR3PidFile(g_szPidFile, &hPidFile);
+ rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ if (RT_SUCCESS(rc))
+ VGSvcMainWait();
+ if (g_szPidFile[0] && hPidFile != NIL_RTFILE)
+ VbglR3ClosePidFile(g_szPidFile, hPidFile);
+#ifdef RT_OS_WINDOWS
+# ifndef RT_OS_NT4
+ /* Uninstall console control handler. */
+ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
+ {
+ VGSvcError("Unable to remove console control handler, error=%ld\n", GetLastError());
+ /* Just skip this error, not critical. */
+ }
+# endif /* !RT_OS_NT4 */
+#else /* !RT_OS_WINDOWS */
+ /* On Windows - since we're running as a console application - we already stopped all services
+ * through the console control handler. So only do the stopping of services here on other platforms
+ * where the break/shutdown/whatever signal was just received. */
+ VGSvcStopServices();
+#endif /* RT_OS_WINDOWS */
+ }
+ VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Cleanup mutex.
+ */
+ CloseHandle(hMutexAppRunning);
+#endif
+
+ VGSvcVerbose(0, "Ended.\n");
+
+#ifdef DEBUG
+ RTCritSectDelete(&g_csLog);
+ //RTMemTrackerDumpAllToStdOut();
+#endif
+
+ VGSvcLogDestroy();
+
+ return rcExit;
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp
new file mode 100644
index 00000000..8439f063
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp
@@ -0,0 +1,2182 @@
+/* $Id: VBoxServiceAutoMount.cpp $ */
+/** @file
+ * VBoxService - Auto-mounting for Shared Folders, only Linux & Solaris atm.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc_automount VBoxService - Shared Folder Automounter
+ *
+ * The Shared Folder Automounter subservice mounts shared folders upon request
+ * from the host.
+ *
+ * This retrieves shared folder automount requests from Main via the VMMDev.
+ * The current implemention only does this once, for some inexplicable reason,
+ * so the run-time addition of automounted shared folders are not heeded.
+ *
+ * This subservice is only used on linux and solaris. On Windows the current
+ * thinking is this is better of done from VBoxTray, some one argue that for
+ * drive letter assigned shared folders it would be better to do some magic here
+ * (obviously not involving NDAddConnection).
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <VBox/err.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/shflsvc.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+
+#ifdef RT_OS_WINDOWS
+#elif defined(RT_OS_OS2)
+# define INCL_DOSFILEMGR
+# define INCL_ERRORS
+# define OS2EMX_PLAIN_CHAR
+# include <os2emx.h>
+#else
+# include <errno.h>
+# include <grp.h>
+# include <sys/mount.h>
+# ifdef RT_OS_SOLARIS
+# include <sys/mntent.h>
+# include <sys/mnttab.h>
+# include <sys/vfs.h>
+RT_C_DECLS_BEGIN /* Only needed for old code.*/
+# include "../../linux/sharedfolders/vbsfmount.h"
+RT_C_DECLS_END
+# elif defined(RT_OS_LINUX)
+# include <mntent.h>
+# include <paths.h>
+RT_C_DECLS_BEGIN
+# include "../../linux/sharedfolders/vbsfmount.h"
+RT_C_DECLS_END
+# else
+# error "Port me!"
+# endif
+# include <unistd.h>
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
+ * Default mount directory (unix only).
+ */
+#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
+# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/media"
+#endif
+
+/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
+ * Default mount prefix (unix only).
+ */
+#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
+# define VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX "sf_"
+#endif
+
+#ifndef _PATH_MOUNTED
+# ifdef RT_OS_SOLARIS
+# define _PATH_MOUNTED "/etc/mnttab"
+# else
+# define _PATH_MOUNTED "/etc/mtab"
+# endif
+#endif
+
+/** @def VBOXSERVICE_AUTOMOUNT_MIQF
+ * The drive letter / path mount point flag. */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_DRIVE_LETTER
+#else
+# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_PATH
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Automounter mount table entry.
+ *
+ * This holds the information returned by SHFL_FN_QUERY_MAP_INFO and
+ * additional mount state info. We only keep entries for mounted mappings.
+ */
+typedef struct VBSVCAUTOMOUNTERENTRY
+{
+ /** The root ID. */
+ uint32_t idRoot;
+ /** The root ID version. */
+ uint32_t uRootIdVersion;
+ /** Map info flags, SHFL_MIF_XXX. */
+ uint64_t fFlags;
+ /** The shared folder (mapping) name. */
+ char *pszName;
+ /** The configured mount point, NULL if none. */
+ char *pszMountPoint;
+ /** The actual mount point, NULL if not mount. */
+ char *pszActualMountPoint;
+} VBSVCAUTOMOUNTERENTRY;
+/** Pointer to an automounter entry. */
+typedef VBSVCAUTOMOUNTERENTRY *PVBSVCAUTOMOUNTERENTRY;
+
+/** Automounter mount table. */
+typedef struct VBSVCAUTOMOUNTERTABLE
+{
+ /** Current number of entries in the array. */
+ uint32_t cEntries;
+ /** Max number of entries the array can hold w/o growing it. */
+ uint32_t cAllocated;
+ /** Pointer to an array of entry pointers. */
+ PVBSVCAUTOMOUNTERENTRY *papEntries;
+} VBSVCAUTOMOUNTERTABLE;
+/** Pointer to an automounter mount table. */
+typedef VBSVCAUTOMOUNTERTABLE *PVBSVCAUTOMOUNTERTABLE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
+/** The Shared Folders service client ID. */
+static uint32_t g_idClientSharedFolders = 0;
+/** Set if we can wait on changes to the mappings. */
+static bool g_fHostSupportsWaitAndInfoQuery = false;
+
+#ifdef RT_OS_OS2
+/** The attachment tag we use to identify attchments that belongs to us. */
+static char const g_szTag[] = "VBoxAutomounter";
+#elif defined(RT_OS_LINUX)
+/** Tag option value that lets us identify mounts that belongs to us. */
+static char const g_szTag[] = "VBoxAutomounter";
+#elif defined(RT_OS_SOLARIS)
+/** Dummy mount option that lets us identify mounts that belongs to us. */
+static char const g_szTag[] = "VBoxAutomounter";
+#endif
+
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vbsvcAutomounterInit(void)
+{
+ VGSvcVerbose(3, "vbsvcAutomounterInit\n");
+
+ int rc = RTSemEventMultiCreate(&g_hAutoMountEvent);
+ AssertRCReturn(rc, rc);
+
+ rc = VbglR3SharedFolderConnect(&g_idClientSharedFolders);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterInit: Service Client ID: %#x\n", g_idClientSharedFolders);
+ g_fHostSupportsWaitAndInfoQuery = RT_SUCCESS(VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders));
+ }
+ else
+ {
+ /* If the service was not found, we disable this service without
+ causing VBoxService to fail. */
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
+ {
+ VGSvcVerbose(0, "vbsvcAutomounterInit: Shared Folders service is not available\n");
+ rc = VERR_SERVICE_DISABLED;
+ }
+ else
+ VGSvcError("Control: Failed to connect to the Shared Folders service! Error: %Rrc\n", rc);
+ RTSemEventMultiDestroy(g_hAutoMountEvent);
+ g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
+ }
+
+ return rc;
+}
+
+
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) /* The old code: */
+
+/**
+ * @todo Integrate into RTFsQueryMountpoint()?
+ */
+static bool vbsvcAutoMountShareIsMountedOld(const char *pszShare, char *pszMountPoint, size_t cbMountPoint)
+{
+ AssertPtrReturn(pszShare, false);
+ AssertPtrReturn(pszMountPoint, false);
+ AssertReturn(cbMountPoint, false);
+
+ bool fMounted = false;
+
+# if defined(RT_OS_SOLARIS)
+ /** @todo What to do if we have a relative path in mtab instead
+ * of an absolute one ("temp" vs. "/media/temp")?
+ * procfs contains the full path but not the actual share name ...
+ * FILE *pFh = setmntent("/proc/mounts", "r+t"); */
+ FILE *pFh = fopen(_PATH_MOUNTED, "r");
+ if (!pFh)
+ VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
+ else
+ {
+ mnttab mntTab;
+ while ((getmntent(pFh, &mntTab)))
+ {
+ if (!RTStrICmp(mntTab.mnt_special, pszShare))
+ {
+ fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp)
+ ? true : false;
+ break;
+ }
+ }
+ fclose(pFh);
+ }
+# elif defined(RT_OS_LINUX)
+ FILE *pFh = setmntent(_PATH_MOUNTED, "r+t"); /** @todo r=bird: why open it for writing? (the '+') */
+ if (pFh == NULL)
+ VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
+ else
+ {
+ mntent *pMntEnt;
+ while ((pMntEnt = getmntent(pFh)))
+ {
+ if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare))
+ {
+ fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir)
+ ? true : false;
+ break;
+ }
+ }
+ endmntent(pFh);
+ }
+# else
+# error "PORTME!"
+# endif
+
+ VGSvcVerbose(4, "vbsvcAutoMountShareIsMountedOld: Share '%s' at mount point '%s' = %s\n",
+ pszShare, fMounted ? pszMountPoint : "<None>", fMounted ? "Yes" : "No");
+ return fMounted;
+}
+
+
+/**
+ * Unmounts a shared folder.
+ *
+ * @returns VBox status code
+ * @param pszMountPoint The shared folder mount point.
+ */
+static int vbsvcAutoMountUnmountOld(const char *pszMountPoint)
+{
+ AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ uint8_t uTries = 0;
+ int r;
+ while (uTries++ < 3)
+ {
+ r = umount(pszMountPoint);
+ if (r == 0)
+ break;
+/** @todo r=bird: Why do sleep 5 seconds after the final retry?
+ * May also be a good idea to check for EINVAL or other signs that someone
+ * else have already unmounted the share. */
+ RTThreadSleep(5000); /* Wait a while ... */
+ }
+ if (r == -1) /** @todo r=bird: RTThreadSleep set errno. */
+ rc = RTErrConvertFromErrno(errno);
+ return rc;
+}
+
+
+/**
+ * Prepares a mount point (create it, set group and mode).
+ *
+ * @returns VBox status code
+ * @param pszMountPoint The mount point.
+ * @param pszShareName Unused.
+ * @param pOpts For getting the group ID.
+ */
+static int vbsvcAutoMountPrepareMountPointOld(const char *pszMountPoint, const char *pszShareName, vbsf_mount_opts *pOpts)
+{
+ AssertPtrReturn(pOpts, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER);
+
+ RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */
+ int rc = RTDirCreateFullPath(pszMountPoint, fMode);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, pOpts->gid, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathSetMode(pszMountPoint, fMode);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_WRITE_PROTECT)
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountPrepareMountPointOld: Mount directory '%s' already is used/mounted\n",
+ pszMountPoint);
+ rc = VINF_SUCCESS;
+ }
+ else
+ VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set mode %RTfmode for mount directory '%s', rc = %Rrc\n",
+ fMode, pszMountPoint, rc);
+ }
+ }
+ else
+ VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set permissions for mount directory '%s', rc = %Rrc\n",
+ pszMountPoint, rc);
+ }
+ else
+ VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not create mount directory '%s' with mode %RTfmode, rc = %Rrc\n",
+ pszMountPoint, fMode, rc);
+ return rc;
+}
+
+
+/**
+ * Mounts a shared folder.
+ *
+ * @returns VBox status code reflecting unmount and mount point preparation
+ * results, but not actual mounting
+ *
+ * @param pszShareName The shared folder name.
+ * @param pszMountPoint The mount point.
+ */
+static int vbsvcAutoMountSharedFolderOld(const char *pszShareName, const char *pszMountPoint)
+{
+ /*
+ * Linux and solaris share the same mount structure.
+ */
+ struct group *grp_vboxsf = getgrnam("vboxsf");
+ if (!grp_vboxsf)
+ {
+ VGSvcError("vbsvcAutoMountWorker: Group 'vboxsf' does not exist\n");
+ return VINF_SUCCESS;
+ }
+
+ struct vbsf_mount_opts Opts =
+ {
+ 0, /* uid */
+ (int)grp_vboxsf->gr_gid, /* gid */
+ 0, /* ttl */
+ 0770, /* dmode, owner and group "vboxsf" have full access */
+ 0770, /* fmode, owner and group "vboxsf" have full access */
+ 0, /* dmask */
+ 0, /* fmask */
+ 0, /* ronly */
+ 0, /* sloppy */
+ 0, /* noexec */
+ 0, /* nodev */
+ 0, /* nosuid */
+ 0, /* remount */
+ "\0", /* nls_name */
+ NULL, /* convertcp */
+ };
+
+ int rc = vbsvcAutoMountPrepareMountPointOld(pszMountPoint, pszShareName, &Opts);
+ if (RT_SUCCESS(rc))
+ {
+# ifdef RT_OS_SOLARIS
+ int fFlags = 0;
+ if (Opts.ronly)
+ fFlags |= MS_RDONLY;
+ char szOptBuf[MAX_MNTOPT_STR] = { '\0', };
+ RTStrPrintf(szOptBuf, sizeof(szOptBuf), "uid=%d,gid=%d,dmode=%0o,fmode=%0o,dmask=%0o,fmask=%0o",
+ Opts.uid, Opts.gid, Opts.dmode, Opts.fmode, Opts.dmask, Opts.fmask);
+ int r = mount(pszShareName,
+ pszMountPoint,
+ fFlags | MS_OPTIONSTR,
+ "vboxfs",
+ NULL, /* char *dataptr */
+ 0, /* int datalen */
+ szOptBuf,
+ sizeof(szOptBuf));
+ if (r == 0)
+ VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
+ else if (errno != EBUSY) /* Share is already mounted? Then skip error msg. */
+ VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s', error = %s\n",
+ pszShareName, pszMountPoint, strerror(errno));
+
+# else /* RT_OS_LINUX */
+ unsigned long fFlags = MS_NODEV;
+
+ /*const char *szOptions = { "rw" }; - ??? */
+ struct vbsf_mount_info_new mntinf;
+ RT_ZERO(mntinf);
+
+ mntinf.nullchar = '\0';
+ mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
+ mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
+ mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
+ mntinf.length = sizeof(mntinf);
+
+ mntinf.uid = Opts.uid;
+ mntinf.gid = Opts.gid;
+ mntinf.ttl = Opts.ttl;
+ mntinf.dmode = Opts.dmode;
+ mntinf.fmode = Opts.fmode;
+ mntinf.dmask = Opts.dmask;
+ mntinf.fmask = Opts.fmask;
+ mntinf.tag[0] = '\0';
+
+ strcpy(mntinf.name, pszShareName);
+ strcpy(mntinf.nls_name, "\0");
+
+ int r = mount(pszShareName,
+ pszMountPoint,
+ "vboxsf",
+ fFlags,
+ &mntinf);
+ if (r == 0)
+ {
+ VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
+
+ r = vbsfmount_complete(pszShareName, pszMountPoint, fFlags, &Opts);
+ switch (r)
+ {
+ case 0: /* Success. */
+ errno = 0; /* Clear all errors/warnings. */
+ break;
+
+ case 1:
+ VGSvcError("vbsvcAutoMountWorker: Could not update mount table (failed to create memstream): %s\n",
+ strerror(errno));
+ break;
+
+ case 2:
+ VGSvcError("vbsvcAutoMountWorker: Could not open mount table for update: %s\n", strerror(errno));
+ break;
+
+ case 3:
+ /* VGSvcError("vbsvcAutoMountWorker: Could not add an entry to the mount table: %s\n", strerror(errno)); */
+ errno = 0;
+ break;
+
+ default:
+ VGSvcError("vbsvcAutoMountWorker: Unknown error while completing mount operation: %d\n", r);
+ break;
+ }
+ }
+ else /* r == -1, we got some error in errno. */
+ {
+ if (errno == EPROTO)
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Messed up share name, re-trying ...\n");
+
+ /** @todo r=bird: What on earth is going on here????? Why can't you
+ * strcpy(mntinf.name, pszShareName) to fix it again? */
+
+ /* Sometimes the mount utility messes up the share name. Try to
+ * un-mangle it again. */
+ char szCWD[RTPATH_MAX];
+ size_t cchCWD;
+ if (!getcwd(szCWD, sizeof(szCWD)))
+ {
+ VGSvcError("vbsvcAutoMountWorker: Failed to get the current working directory\n");
+ szCWD[0] = '\0';
+ }
+ cchCWD = strlen(szCWD);
+ if (!strncmp(pszMountPoint, szCWD, cchCWD))
+ {
+ while (pszMountPoint[cchCWD] == '/')
+ ++cchCWD;
+ /* We checked before that we have enough space */
+ strcpy(mntinf.name, pszMountPoint + cchCWD);
+ }
+ r = mount(mntinf.name, pszMountPoint, "vboxsf", fFlags, &mntinf);
+ }
+ if (r == -1) /* Was there some error from one of the tries above? */
+ {
+ switch (errno)
+ {
+ /* If we get EINVAL here, the system already has mounted the Shared Folder to another
+ * mount point. */
+ case EINVAL:
+ VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' already is mounted!\n", pszShareName);
+ /* Ignore this error! */
+ break;
+ case EBUSY:
+ /* Ignore these errors! */
+ break;
+
+ default:
+ VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s': %s (%d)\n",
+ pszShareName, pszMountPoint, strerror(errno), errno);
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+ }
+ }
+# endif
+ }
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Mounting returned with rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Processes shared folder mappings retrieved from the host.
+ *
+ * @returns VBox status code.
+ * @param paMappings The mappings.
+ * @param cMappings The number of mappings.
+ * @param pszMountDir The mount directory.
+ * @param pszSharePrefix The share prefix.
+ * @param uClientID The shared folder service (HGCM) client ID.
+ */
+static int vbsvcAutoMountProcessMappingsOld(PCVBGLR3SHAREDFOLDERMAPPING paMappings, uint32_t cMappings,
+ const char *pszMountDir, const char *pszSharePrefix, uint32_t uClientID)
+{
+ if (cMappings == 0)
+ return VINF_SUCCESS;
+ AssertPtrReturn(paMappings, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszMountDir, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszSharePrefix, VERR_INVALID_PARAMETER);
+ AssertReturn(uClientID > 0, VERR_INVALID_PARAMETER);
+
+ /** @todo r=bird: Why is this loop schitzoid about status codes? It quits if
+ * RTPathJoin fails (i.e. if the user specifies a very long name), but happily
+ * continues if RTStrAPrintf failes (mem alloc).
+ *
+ * It also happily continues if the 'vboxsf' group is missing, which is a waste
+ * of effort... In fact, retrieving the group ID could probably be done up
+ * front, outside the loop. */
+ int rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
+ {
+ char *pszShareName = NULL;
+ rc = VbglR3SharedFolderGetName(uClientID, paMappings[i].u32Root, &pszShareName);
+ if ( RT_SUCCESS(rc)
+ && *pszShareName)
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Connecting share %u (%s) ...\n", i+1, pszShareName);
+
+ /** @todo r=bird: why do you copy things twice here and waste heap space?
+ * szMountPoint has a fixed size.
+ * @code
+ * char szMountPoint[RTPATH_MAX];
+ * rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, *pszSharePrefix ? pszSharePrefix : pszShareName);
+ * if (RT_SUCCESS(rc) && *pszSharePrefix)
+ * rc = RTStrCat(szMountPoint, sizeof(szMountPoint), pszShareName);
+ * @endcode */
+ char *pszShareNameFull = NULL;
+ if (RTStrAPrintf(&pszShareNameFull, "%s%s", pszSharePrefix, pszShareName) > 0)
+ {
+ char szMountPoint[RTPATH_MAX];
+ rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, pszShareNameFull);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "vbsvcAutoMountWorker: Processing mount point '%s'\n", szMountPoint);
+
+ /*
+ * Already mounted?
+ */
+ /** @todo r-bird: this does not take into account that a shared folder could
+ * be mounted twice... We're really just interested in whether the
+ * folder is mounted on 'szMountPoint', no where else... */
+ bool fSkip = false;
+ char szAlreadyMountedOn[RTPATH_MAX];
+ if (vbsvcAutoMountShareIsMountedOld(pszShareName, szAlreadyMountedOn, sizeof(szAlreadyMountedOn)))
+ {
+ /* Do if it not mounted to our desired mount point */
+ if (RTStrICmp(szMountPoint, szAlreadyMountedOn))
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', unmounting ...\n",
+ pszShareName, szAlreadyMountedOn);
+ rc = vbsvcAutoMountUnmountOld(szAlreadyMountedOn);
+ if (RT_SUCCESS(rc))
+ fSkip = false;
+ else
+ VGSvcError("vbsvcAutoMountWorker: Failed to unmount '%s', %s (%d)! (rc=%Rrc)\n",
+ szAlreadyMountedOn, strerror(errno), errno, rc); /** @todo errno isn't reliable at this point */
+ }
+ if (fSkip)
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', skipping\n",
+ pszShareName, szAlreadyMountedOn);
+ }
+ if (!fSkip)
+ {
+ /*
+ * Mount it.
+ */
+ rc = vbsvcAutoMountSharedFolderOld(pszShareName, szMountPoint);
+ }
+ }
+ else
+ VGSvcError("vbsvcAutoMountWorker: Unable to join mount point/prefix/shrae, rc = %Rrc\n", rc);
+ RTStrFree(pszShareNameFull);
+ }
+ else
+ VGSvcError("vbsvcAutoMountWorker: Unable to allocate full share name\n");
+ RTStrFree(pszShareName);
+ }
+ else
+ VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
+ paMappings[i].u32Root, rc);
+ } /* for cMappings. */
+ return rc;
+}
+
+#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) - the old code*/
+
+
+/**
+ * Service worker function for old host.
+ *
+ * This only mount stuff on startup.
+ *
+ * @returns VBox status code.
+ * @param pfShutdown Shutdown indicator.
+ */
+static int vbsvcAutoMountWorkerOld(bool volatile *pfShutdown)
+{
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
+ /*
+ * We only do a single pass here.
+ */
+ uint32_t cMappings;
+ PVBGLR3SHAREDFOLDERMAPPING paMappings;
+ int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /* Only process auto-mounted folders */,
+ &paMappings, &cMappings);
+ if ( RT_SUCCESS(rc)
+ && cMappings)
+ {
+ char *pszMountDir;
+ rc = VbglR3SharedFolderGetMountDir(&pszMountDir);
+ if (rc == VERR_NOT_FOUND)
+ rc = RTStrDupEx(&pszMountDir, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount dir set to '%s'\n", pszMountDir);
+
+ char *pszSharePrefix;
+ rc = VbglR3SharedFolderGetMountPrefix(&pszSharePrefix);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount prefix set to '%s'\n", pszSharePrefix);
+# ifdef USE_VIRTUAL_SHARES
+ /* Check for a fixed/virtual auto-mount share. */
+ if (VbglR3SharedFolderExists(g_idClientSharedFolders, "vbsfAutoMount"))
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Host supports auto-mount root\n");
+ else
+ {
+# endif
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: Got %u shared folder mappings\n", cMappings);
+ rc = vbsvcAutoMountProcessMappingsOld(paMappings, cMappings, pszMountDir, pszSharePrefix,
+ g_idClientSharedFolders);
+# ifdef USE_VIRTUAL_SHARES
+ }
+# endif
+ RTStrFree(pszSharePrefix);
+ } /* Mount share prefix. */
+ else
+ VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mount prefix, rc = %Rrc\n", rc);
+ RTStrFree(pszMountDir);
+ }
+ else
+ VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder directory, rc = %Rrc\n", rc);
+ VbglR3SharedFolderFreeMappings(paMappings);
+ }
+ else if (RT_FAILURE(rc))
+ VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mappings, rc = %Rrc\n", rc);
+ else
+ VGSvcVerbose(3, "vbsvcAutoMountWorker: No shared folder mappings found\n");
+
+#else
+ int rc = VINF_SUCCESS;
+#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) */
+
+
+ /*
+ * Wait on shutdown (this used to be a silly RTThreadSleep(500) loop).
+ */
+ while (!*pfShutdown)
+ {
+ rc = RTSemEventMultiWait(g_hAutoMountEvent, RT_MS_1MIN);
+ if (rc != VERR_TIMEOUT)
+ break;
+ }
+
+ VGSvcVerbose(3, "vbsvcAutoMountWorkerOld: Finished with rc=%Rrc\n", rc);
+ return rc;
+}
+
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+/**
+ * Assembles the mount directory and prefix into @a pszDst.
+ *
+ * Will fall back on defaults if we have trouble with the configuration from the
+ * host. This ASSUMES that @a cbDst is rather large and won't cause trouble
+ * with the default.
+ *
+ * @returns IPRT status code.
+ * @param pszDst Where to return the prefix.
+ * @param cbDst The size of the prefix buffer.
+ */
+static int vbsvcAutomounterQueryMountDirAndPrefix(char *pszDst, size_t cbDst)
+{
+ /*
+ * Query the config first.
+ */
+ /* Mount directory: */
+ const char *pszDir = VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR;
+ char *pszCfgDir;
+ int rc = VbglR3SharedFolderGetMountDir(&pszCfgDir);
+ if (RT_SUCCESS(rc))
+ {
+ if (*pszCfgDir == '/')
+ pszDir = pszCfgDir;
+ }
+ else
+ pszCfgDir = NULL;
+
+ /* Prefix: */
+ const char *pszPrefix = VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX;
+ char *pszCfgPrefix;
+ rc = VbglR3SharedFolderGetMountPrefix(&pszCfgPrefix);
+ if (RT_SUCCESS(rc))
+ {
+ if ( strchr(pszCfgPrefix, '/') == NULL
+ && strchr(pszCfgPrefix, '\\') == NULL
+ && strcmp(pszCfgPrefix, "..") != 0)
+ pszPrefix = pszCfgPrefix;
+ }
+ else
+ pszCfgPrefix = NULL;
+
+ /*
+ * Try combine the two.
+ */
+ rc = RTPathAbs(pszDir, pszDst, cbDst);
+ if (RT_SUCCESS(rc))
+ {
+ if (*pszPrefix)
+ {
+ rc = RTPathAppend(pszDst, cbDst, pszPrefix);
+ if (RT_FAILURE(rc))
+ VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAppend(%s,,%s) -> %Rrc\n", pszDst, pszPrefix, rc);
+ }
+ else
+ {
+ rc = RTPathEnsureTrailingSeparator(pszDst, cbDst);
+ if (RT_FAILURE(rc))
+ VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathEnsureTrailingSeparator(%s) -> %Rrc\n", pszDst, rc);
+ }
+ }
+ else
+ VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAbs(%s) -> %Rrc\n", pszDir, rc);
+
+
+ /*
+ * Return the default dir + prefix if the above failed.
+ */
+ if (RT_FAILURE(rc))
+ {
+ rc = RTStrCopy(pszDst, cbDst, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/" VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX);
+ AssertRC(rc);
+ }
+
+ RTStrFree(pszCfgDir);
+ RTStrFree(pszCfgPrefix);
+ return rc;
+}
+#endif /* !RT_OS_WINDOW && !RT_OS_OS2 */
+
+
+/**
+ * @callback_method_impl{FNRTSORTCMP, For sorting mount table by root ID. }
+ */
+static DECLCALLBACK(int) vbsvcAutomounterCompareEntry(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ RT_NOREF_PV(pvUser);
+ PVBSVCAUTOMOUNTERENTRY pEntry1 = (PVBSVCAUTOMOUNTERENTRY)pvElement1;
+ PVBSVCAUTOMOUNTERENTRY pEntry2 = (PVBSVCAUTOMOUNTERENTRY)pvElement2;
+ return pEntry1->idRoot < pEntry2->idRoot ? -1
+ : pEntry1->idRoot > pEntry2->idRoot ? 1 : 0;
+}
+
+
+/**
+ * Worker for vbsvcAutomounterPopulateTable for adding discovered entries.
+ *
+ * This is puts dummies in for missing values, depending on
+ * vbsvcAutomounterPopulateTable to query them later.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY;
+ * @param pMountTable The mount table to add an entry to.
+ * @param pszName The shared folder name.
+ * @param pszMountPoint The mount point.
+ */
+static int vbsvcAutomounterAddEntry(PVBSVCAUTOMOUNTERTABLE pMountTable, const char *pszName, const char *pszMountPoint)
+{
+ VGSvcVerbose(2, "vbsvcAutomounterAddEntry: %s -> %s\n", pszMountPoint, pszName);
+ PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
+ pEntry->idRoot = UINT32_MAX;
+ pEntry->uRootIdVersion = UINT32_MAX;
+ pEntry->fFlags = UINT64_MAX;
+ pEntry->pszName = RTStrDup(pszName);
+ pEntry->pszMountPoint = NULL;
+ pEntry->pszActualMountPoint = RTStrDup(pszMountPoint);
+ if (pEntry->pszName && pEntry->pszActualMountPoint)
+ {
+ if (pMountTable->cEntries + 1 <= pMountTable->cAllocated)
+ {
+ pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
+ return VINF_SUCCESS;
+ }
+
+ void *pvNew = RTMemRealloc(pMountTable->papEntries, (pMountTable->cAllocated + 8) * sizeof(pMountTable->papEntries[0]));
+ if (pvNew)
+ {
+ pMountTable->cAllocated += 8;
+ pMountTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvNew;
+
+ pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
+ return VINF_SUCCESS;
+ }
+ }
+ RTMemFree(pEntry->pszActualMountPoint);
+ RTMemFree(pEntry->pszName);
+ RTMemFree(pEntry);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Populates the mount table as best we can with existing automount entries.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY;
+ * @param pMountTable The mount table (empty).
+ */
+static int vbsvcAutomounterPopulateTable(PVBSVCAUTOMOUNTERTABLE pMountTable)
+{
+ int rc;
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Loop thru the drive letters and check out each of them using QueryDosDeviceW.
+ */
+ static const char s_szDevicePath[] = "\\Device\\VBoxMiniRdr\\;";
+ for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
+ {
+ RTUTF16 const wszMountPoint[4] = { chDrive, ':', '\0', '\0' };
+ RTUTF16 wszTargetPath[RTPATH_MAX];
+ DWORD const cwcResult = QueryDosDeviceW(wszMountPoint, wszTargetPath, RT_ELEMENTS(wszTargetPath));
+ if ( cwcResult > sizeof(s_szDevicePath)
+ && RTUtf16NICmpAscii(wszTargetPath, RT_STR_TUPLE(s_szDevicePath)) == 0)
+ {
+ PCRTUTF16 pwsz = &wszTargetPath[RT_ELEMENTS(s_szDevicePath) - 1];
+ Assert(pwsz[-1] == ';');
+ if ( (pwsz[0] & ~(RTUTF16)0x20) == chDrive
+ && pwsz[1] == ':'
+ && pwsz[2] == '\\')
+ {
+ /* For now we'll just use the special capitalization of the
+ "server" name to identify it as our work. We could check
+ if the symlink is from \Global?? or \??, but that trick does
+ work for older OS versions (<= XP) or when running the
+ service manually for testing/wathever purposes. */
+ /** @todo Modify the windows shared folder driver to allow tagging drives.*/
+ if (RTUtf16NCmpAscii(&pwsz[3], RT_STR_TUPLE("VBoxSvr\\")) == 0)
+ {
+ pwsz += 3 + 8;
+ if (*pwsz != '\\' && *pwsz)
+ {
+ /* The shared folder name should follow immediately after the server prefix. */
+ char *pszMountedName = NULL;
+ rc = RTUtf16ToUtf8(pwsz, &pszMountedName);
+ if (RT_SUCCESS(rc))
+ {
+ char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
+ rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
+ RTStrFree(pszMountedName);
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Malformed, not ours: %ls -> %ls\n",
+ wszMountPoint, wszTargetPath);
+ }
+ else
+ VGSvcVerbose(3, "vbsvcAutomounterPopulateTable: Not ours: %ls -> %ls\n", wszMountPoint, wszTargetPath);
+ }
+ }
+ }
+
+#elif defined(RT_OS_OS2)
+ /*
+ * Just loop thru the drive letters and check the attachment of each.
+ */
+ for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
+ {
+ char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
+ union
+ {
+ FSQBUFFER2 FsQueryBuf;
+ char achPadding[1024];
+ } uBuf;
+ RT_ZERO(uBuf);
+ ULONG cbBuf = sizeof(uBuf) - 2;
+ APIRET rcOs2 = DosQueryFSAttach(szMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
+ if (rcOs2 == NO_ERROR)
+ {
+ const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
+ if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
+ && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
+ {
+ const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
+ const char *pszTag = pszMountedName + strlen(pszMountedName) + 1; /* (Safe. Always two trailing zero bytes, see above.) */
+ if (strcmp(pszTag, g_szTag) == 0)
+ {
+ rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ }
+ }
+
+#elif defined(RT_OS_LINUX)
+ /*
+ * Scan the mount table file for the mount point and then match file system
+ * and device/share. We identify our mounts by mount path + prefix for now,
+ * but later we may use the same approach as on solaris.
+ */
+ FILE *pFile = setmntent("/proc/mounts", "r");
+ int iErrMounts = errno;
+ if (!pFile)
+ pFile = setmntent("/etc/mtab", "r");
+ if (pFile)
+ {
+ rc = VWRN_NOT_FOUND;
+ struct mntent *pEntry;
+ while ((pEntry = getmntent(pFile)) != NULL)
+ if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
+ if (strstr(pEntry->mnt_opts, g_szTag) != NULL)
+ {
+ rc = vbsvcAutomounterAddEntry(pMountTable, pEntry->mnt_fsname, pEntry->mnt_dir);
+ if (RT_FAILURE(rc))
+ {
+ endmntent(pFile);
+ return rc;
+ }
+ }
+ endmntent(pFile);
+ }
+ else
+ VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d) or '/proc/mounts' (errno=%d)\n",
+ _PATH_MOUNTED, errno, iErrMounts);
+
+#elif defined(RT_OS_SOLARIS)
+ /*
+ * Look thru the system mount table and inspect the vboxsf mounts.
+ */
+ FILE *pFile = fopen(_PATH_MOUNTED, "r");
+ if (pFile)
+ {
+ rc = VINF_SUCCESS;
+ struct mnttab Entry;
+ while (getmntent(pFile, &Entry) == 0)
+ if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
+ {
+ /* Look for the dummy automounter option. */
+ if ( Entry.mnt_mntopts != NULL
+ && strstr(Entry.mnt_mntopts, g_szTag) != NULL)
+ {
+ rc = vbsvcAutomounterAddEntry(pMountTable, Entry.mnt_special, Entry.mnt_mountp);
+ if (RT_FAILURE(rc))
+ {
+ fclose(pFile);
+ return rc;
+ }
+ }
+ }
+ fclose(pFile);
+ }
+ else
+ VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
+
+#else
+# error "PORTME!"
+#endif
+
+ /*
+ * Try reconcile the detected folders with data from the host.
+ */
+ uint32_t cMappings = 0;
+ PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
+ rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
+ {
+ uint32_t const idRootSrc = paMappings[i].u32Root;
+
+ uint32_t uRootIdVer = UINT32_MAX;
+ uint64_t fFlags = 0;
+ char *pszName = NULL;
+ char *pszMntPt = NULL;
+ int rc2 = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
+ &pszName, &pszMntPt, &fFlags, &uRootIdVer);
+ if (RT_SUCCESS(rc2))
+ {
+ uint32_t iPrevHit = UINT32_MAX;
+ for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
+ {
+ PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
+ if (RTStrICmp(pEntry->pszName, pszName) == 0)
+ {
+ VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Identified %s -> %s: idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
+ pEntry->pszActualMountPoint, pEntry->pszName, idRootSrc, uRootIdVer, fFlags, pszMntPt);
+ pEntry->fFlags = fFlags;
+ pEntry->idRoot = idRootSrc;
+ pEntry->uRootIdVersion = uRootIdVer;
+ RTStrFree(pEntry->pszMountPoint);
+ pEntry->pszMountPoint = RTStrDup(pszMntPt);
+ if (!pEntry->pszMountPoint)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ /* If multiple mappings of the same folder, pick the first or the one
+ with matching mount point. */
+ if (iPrevHit == UINT32_MAX)
+ iPrevHit = iTable;
+ else if (RTPathCompare(pszMntPt, pEntry->pszActualMountPoint) == 0)
+ {
+ if (iPrevHit != UINT32_MAX)
+ pMountTable->papEntries[iPrevHit]->uRootIdVersion -= 1;
+ iPrevHit = iTable;
+ }
+ else
+ pEntry->uRootIdVersion -= 1;
+ }
+ }
+
+ RTStrFree(pszName);
+ RTStrFree(pszMntPt);
+ }
+ else
+ VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderQueryFolderInfo(%u) failed: %Rrc\n", idRootSrc, rc2);
+ }
+
+ VbglR3SharedFolderFreeMappings(paMappings);
+
+ /*
+ * Sort the table by root ID.
+ */
+ if (pMountTable->cEntries > 1)
+ RTSortApvShell((void **)pMountTable->papEntries, pMountTable->cEntries, vbsvcAutomounterCompareEntry, NULL);
+
+ for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
+ {
+ PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
+ if (pMountTable->papEntries[iTable]->idRoot != UINT32_MAX)
+ VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
+ iTable, pEntry->pszActualMountPoint, pEntry->pszName, pEntry->idRoot, pEntry->uRootIdVersion,
+ pEntry->fFlags, pEntry->pszMountPoint);
+ else
+ VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s - not identified!\n",
+ iTable, pEntry->pszActualMountPoint, pEntry->pszName);
+ }
+ }
+ else
+ VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Checks whether the shared folder @a pszName is mounted on @a pszMountPoint.
+ *
+ * @returns Exactly one of the following IPRT status codes;
+ * @retval VINF_SUCCESS if mounted
+ * @retval VWRN_NOT_FOUND if nothing is mounted at @a pszMountPoint.
+ * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
+ * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
+ * there.
+ *
+ * @param pszMountPoint The mount point to check.
+ * @param pszName The name of the shared folder (mapping).
+ */
+static int vbsvcAutomounterQueryMountPoint(const char *pszMountPoint, const char *pszName)
+{
+ VGSvcVerbose(4, "vbsvcAutomounterQueryMountPoint: pszMountPoint=%s pszName=%s\n", pszMountPoint, pszName);
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * We could've used RTFsQueryType here but would then have to
+ * calling RTFsQueryLabel for the share name hint, ending up
+ * doing the same work twice. We could also use QueryDosDeviceW,
+ * but output is less clear...
+ */
+ PRTUTF16 pwszMountPoint = NULL;
+ int rc = RTStrToUtf16(pszMountPoint, &pwszMountPoint);
+ if (RT_SUCCESS(rc))
+ {
+ DWORD uSerial = 0;
+ DWORD cchCompMax = 0;
+ DWORD fFlags = 0;
+ RTUTF16 wszLabel[512];
+ RTUTF16 wszFileSystem[256];
+ RT_ZERO(wszLabel);
+ RT_ZERO(wszFileSystem);
+ if (GetVolumeInformationW(pwszMountPoint, wszLabel, RT_ELEMENTS(wszLabel) - 1, &uSerial, &cchCompMax, &fFlags,
+ wszFileSystem, RT_ELEMENTS(wszFileSystem) - 1))
+ {
+ if (RTUtf16ICmpAscii(wszFileSystem, "VBoxSharedFolderFS") == 0)
+ {
+ char *pszLabel = NULL;
+ rc = RTUtf16ToUtf8(wszLabel, &pszLabel);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszMountedName = pszLabel;
+ if (RTStrStartsWith(pszMountedName, "VBOX_"))
+ pszMountedName += sizeof("VBOX_") - 1;
+ if (RTStrICmp(pszMountedName, pszName) == 0)
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
+ pszName, pszMountPoint);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
+ pszMountedName, pszMountPoint, pszName);
+ rc = VERR_RESOURCE_BUSY;
+ }
+ RTStrFree(pszLabel);
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: RTUtf16ToUtf8(%ls,) failed: %Rrc\n", wszLabel, rc);
+ rc = VERR_RESOURCE_BUSY;
+ }
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%ls' with label '%ls' mount at '%s', not '%s'...\n",
+ wszFileSystem, wszLabel, pszMountPoint, pszName);
+ rc = VERR_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ rc = GetLastError();
+ if (rc != ERROR_PATH_NOT_FOUND || g_cVerbosity >= 4)
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: GetVolumeInformationW('%ls',,,,) failed: %u\n", pwszMountPoint, rc);
+ rc = VWRN_NOT_FOUND;
+ }
+ RTUtf16Free(pwszMountPoint);
+ }
+ else
+ {
+ VGSvcError("vbsvcAutomounterQueryMountPoint: RTStrToUtf16(%s,) -> %Rrc\n", pszMountPoint, rc);
+ rc = VWRN_NOT_FOUND;
+ }
+ return rc;
+
+#elif defined(RT_OS_OS2)
+ /*
+ * Query file system attachment info for the given drive letter.
+ */
+ union
+ {
+ FSQBUFFER2 FsQueryBuf;
+ char achPadding[512];
+ } uBuf;
+ RT_ZERO(uBuf);
+
+ ULONG cbBuf = sizeof(uBuf);
+ APIRET rcOs2 = DosQueryFSAttach(pszMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
+ int rc;
+ if (rcOs2 == NO_ERROR)
+ {
+ const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
+ if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
+ && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
+ {
+ const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
+ if (RTStrICmp(pszMountedName, pszName) == 0)
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
+ pszName, pszMountPoint);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
+ pszMountedName, pszMountPoint, pszName);
+ rc = VERR_RESOURCE_BUSY;
+ }
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' type %u mount at '%s', not '%s'...\n",
+ pszFsdName, uBuf.FsQueryBuf.iType, pszMountPoint, pszName);
+ rc = VERR_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ rc = VWRN_NOT_FOUND;
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: DosQueryFSAttach(%s) -> %u\n", pszMountPoint, rcOs2);
+ AssertMsgStmt(rcOs2 != ERROR_BUFFER_OVERFLOW && rcOs2 != ERROR_INVALID_PARAMETER,
+ ("%s -> %u\n", pszMountPoint, rcOs2), rc = VERR_ACCESS_DENIED);
+ }
+ return rc;
+
+#elif defined(RT_OS_LINUX)
+ /*
+ * Scan one of the mount table file for the mount point and then
+ * match file system and device/share.
+ */
+ FILE *pFile = setmntent("/proc/mounts", "r");
+ int rc = errno;
+ if (!pFile)
+ pFile = setmntent(_PATH_MOUNTED, "r");
+ if (pFile)
+ {
+ rc = VWRN_NOT_FOUND;
+ struct mntent *pEntry;
+ while ((pEntry = getmntent(pFile)) != NULL)
+ if (RTPathCompare(pEntry->mnt_dir, pszMountPoint) == 0)
+ {
+ if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
+ {
+ if (RTStrICmp(pEntry->mnt_fsname, pszName) == 0)
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
+ pszName, pszMountPoint);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
+ pEntry->mnt_fsname, pszMountPoint, pszName);
+ rc = VERR_RESOURCE_BUSY;
+ }
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
+ pEntry->mnt_type, pEntry->mnt_fsname, pszMountPoint, pszName);
+ rc = VERR_ACCESS_DENIED;
+ }
+ /* We continue searching in case of stacked mounts, we want the last one. */
+ }
+ endmntent(pFile);
+ }
+ else
+ {
+ VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '/proc/mounts' (errno=%d) or '%s' (errno=%d)\n",
+ rc, _PATH_MOUNTED, errno);
+ rc = VERR_ACCESS_DENIED;
+ }
+ return rc;
+
+#elif defined(RT_OS_SOLARIS)
+ /*
+ * Similar to linux.
+ */
+ int rc;
+ FILE *pFile = fopen(_PATH_MOUNTED, "r");
+ if (pFile)
+ {
+ rc = VWRN_NOT_FOUND;
+ struct mnttab Entry;
+ while (getmntent(pFile, &Entry) == 0)
+ if (RTPathCompare(Entry.mnt_mountp, pszMountPoint) == 0)
+ {
+ if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
+ {
+ if (RTStrICmp(Entry.mnt_special, pszName) == 0)
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
+ pszName, pszMountPoint);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
+ Entry.mnt_special, pszMountPoint, pszName);
+ rc = VERR_RESOURCE_BUSY;
+ }
+ }
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
+ Entry.mnt_fstype, Entry.mnt_special, pszMountPoint, pszName);
+ rc = VERR_ACCESS_DENIED;
+ }
+ /* We continue searching in case of stacked mounts, we want the last one. */
+ }
+ fclose(pFile);
+ }
+ else
+ {
+ VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
+ rc = VERR_ACCESS_DENIED;
+ }
+ return rc;
+#else
+# error "PORTME"
+#endif
+}
+
+
+/**
+ * Worker for vbsvcAutomounterMountNewEntry that does the OS mounting.
+ *
+ * @returns IPRT status code.
+ * @param pEntry The entry to try mount.
+ */
+static int vbsvcAutomounterMountIt(PVBSVCAUTOMOUNTERENTRY pEntry)
+{
+ VGSvcVerbose(3, "vbsvcAutomounterMountIt: Trying to mount '%s' (idRoot=%#x) on '%s'...\n",
+ pEntry->pszName, pEntry->idRoot, pEntry->pszActualMountPoint);
+#ifdef RT_OS_WINDOWS
+ /*
+ * Attach the shared folder using WNetAddConnection2W.
+ *
+ * According to google we should get a drive symlink in \\GLOBAL?? when
+ * we are running under the system account. Otherwise it will a session
+ * local link (\\??).
+ */
+ Assert(RT_C_IS_UPPER(pEntry->pszActualMountPoint[0]) && pEntry->pszActualMountPoint[1] == ':' && pEntry->pszActualMountPoint[2] == '\0');
+ RTUTF16 wszDrive[4] = { pEntry->pszActualMountPoint[0], ':', '\0', '\0' };
+
+ RTUTF16 wszPrefixedName[RTPATH_MAX];
+ int rc = RTUtf16CopyAscii(wszPrefixedName, RT_ELEMENTS(wszPrefixedName), "\\\\VBoxSvr\\");
+ AssertRC(rc);
+
+ size_t const offName = RTUtf16Len(wszPrefixedName);
+ PRTUTF16 pwszName = &wszPrefixedName[offName];
+ rc = RTStrToUtf16Ex(pEntry->pszName, RTSTR_MAX, &pwszName, sizeof(wszPrefixedName) - offName, NULL);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("vbsvcAutomounterMountIt: RTStrToUtf16Ex failed on '%s': %Rrc\n", pEntry->pszName, rc);
+ return rc;
+ }
+
+ NETRESOURCEW NetRsrc;
+ RT_ZERO(NetRsrc);
+ NetRsrc.dwType = RESOURCETYPE_DISK;
+ NetRsrc.lpLocalName = wszDrive;
+ NetRsrc.lpRemoteName = wszPrefixedName;
+ NetRsrc.lpProvider = L"VirtualBox Shared Folders"; /* Only try our provider. */
+ NetRsrc.lpComment = pwszName;
+
+ DWORD dwErr = WNetAddConnection2W(&NetRsrc, NULL /*pwszPassword*/, NULL /*pwszUserName*/, 0 /*dwFlags*/);
+ if (dwErr == NO_ERROR)
+ {
+ VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
+ pEntry->pszName, pEntry->pszActualMountPoint);
+ return VINF_SUCCESS;
+ }
+ VGSvcError("vbsvcAutomounterMountIt: Failed to attach '%s' to '%s': %u\n",
+ pEntry->pszName, pEntry->pszActualMountPoint, rc);
+ return VERR_OPEN_FAILED;
+
+#elif defined(RT_OS_OS2)
+ /*
+ * It's a rather simple affair on OS/2.
+ *
+ * In order to be able to detect our mounts we add a 2nd string after
+ * the folder name that tags the attachment. The IFS will remember this
+ * and return it when DosQueryFSAttach is called.
+ *
+ * Note! Kernel currently accepts limited 7-bit ASCII names. We could
+ * change that to UTF-8 if we like as that means no extra string
+ * encoding conversion fun here.
+ */
+ char szzNameAndTag[256];
+ size_t cchName = strlen(pEntry->pszName);
+ if (cchName + 1 + sizeof(g_szTag) <= sizeof(szzNameAndTag))
+ {
+ memcpy(szzNameAndTag, pEntry->pszName, cchName);
+ szzNameAndTag[cchName] = '\0';
+ memcpy(&szzNameAndTag[cchName + 1], g_szTag, sizeof(g_szTag));
+
+ APIRET rc = DosFSAttach(pEntry->pszActualMountPoint, "VBOXSF", szzNameAndTag, cchName + 1 + sizeof(g_szTag), FS_ATTACH);
+ if (rc == NO_ERROR)
+ {
+ VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
+ pEntry->pszName, pEntry->pszActualMountPoint);
+ return VINF_SUCCESS;
+ }
+ VGSvcError("vbsvcAutomounterMountIt: DosFSAttach failed to attach '%s' to '%s': %u\n",
+ pEntry->pszName, pEntry->pszActualMountPoint, rc);
+ }
+ else
+ VGSvcError("vbsvcAutomounterMountIt: Share name for attach to '%s' is too long: %u chars - '%s'\n",
+ pEntry->pszActualMountPoint, cchName, pEntry->pszName);
+ return VERR_OPEN_FAILED;
+
+#else
+ /*
+ * Common work for unix-like systems: Get group, make sure mount directory exist.
+ */
+ int rc = RTDirCreateFullPath(pEntry->pszActualMountPoint,
+ RTFS_UNIX_IRWXU | RTFS_UNIX_IXGRP | RTFS_UNIX_IRGRP | RTFS_UNIX_IXOTH | RTFS_UNIX_IROTH);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("vbsvcAutomounterMountIt: Failed to create mount path '%s' for share '%s': %Rrc\n",
+ pEntry->pszActualMountPoint, pEntry->pszName, rc);
+ return rc;
+ }
+
+ gid_t gidMount;
+ struct group *grp_vboxsf = getgrnam("vboxsf");
+ if (grp_vboxsf)
+ gidMount = grp_vboxsf->gr_gid;
+ else
+ {
+ VGSvcError("vbsvcAutomounterMountIt: Group 'vboxsf' does not exist\n");
+ gidMount = 0;
+ }
+
+# if defined(RT_OS_LINUX)
+ /*
+ * Linux a bit more work...
+ */
+ struct vbsf_mount_info_new MntInfo;
+ RT_ZERO(MntInfo);
+ struct vbsf_mount_opts MntOpts;
+ RT_ZERO(MntOpts);
+ MntInfo.nullchar = '\0';
+ MntInfo.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
+ MntInfo.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
+ MntInfo.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
+ MntInfo.length = sizeof(MntInfo);
+ MntInfo.uid = MntOpts.uid = 0;
+ MntInfo.gid = MntOpts.gid = gidMount;
+ MntInfo.dmode = MntOpts.dmode = 0770;
+ MntInfo.fmode = MntOpts.fmode = 0770;
+ MntInfo.dmask = MntOpts.dmask = 0000;
+ MntInfo.fmask = MntOpts.fmask = 0000;
+ memcpy(MntInfo.tag, g_szTag, sizeof(g_szTag)); AssertCompile(sizeof(MntInfo.tag) >= sizeof(g_szTag));
+ rc = RTStrCopy(MntInfo.name, sizeof(MntInfo.name), pEntry->pszName);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("vbsvcAutomounterMountIt: Share name '%s' is too long for the MntInfo.name field!\n", pEntry->pszName);
+ return rc;
+ }
+
+ errno = 0;
+ unsigned long fFlags = MS_NODEV;
+ rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, "vboxsf", fFlags, &MntInfo);
+ if (rc == 0)
+ {
+ VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
+ pEntry->pszName, pEntry->pszActualMountPoint);
+
+ errno = 0;
+ rc = vbsfmount_complete(pEntry->pszName, pEntry->pszActualMountPoint, fFlags, &MntOpts);
+ if (rc != 0) /* Ignorable. /etc/mtab is probably a link to /proc/mounts. */
+ VGSvcVerbose(1, "vbsvcAutomounterMountIt: vbsfmount_complete failed: %s (%d/%d)\n",
+ rc == 1 ? "open_memstream" : rc == 2 ? "setmntent" : rc == 3 ? "addmntent" : "unknown", rc, errno);
+ return VINF_SUCCESS;
+ }
+ else if (errno == EINVAL)
+ VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s' because it is probably mounted elsewhere arleady! (%d,%d)\n",
+ pEntry->pszName, pEntry->pszActualMountPoint, rc, errno);
+ else
+ VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s': %s (%d,%d)\n",
+ pEntry->pszName, pEntry->pszActualMountPoint, strerror(errno), rc, errno);
+ return VERR_WRITE_ERROR;
+
+# elif defined(RT_OS_SOLARIS)
+ /*
+ * Solaris is rather simple compared to linux.
+ *
+ * The ',VBoxService=auto' option (g_szTag) is ignored by the kernel but helps
+ * us identify our own mounts on restart. See vbsvcAutomounterPopulateTable().
+ *
+ * Note! Must pass MAX_MNTOPT_STR rather than cchOpts to mount, as it may fail
+ * with EOVERFLOW in vfs_buildoptionstr() during domount() otherwise.
+ */
+ char szOpts[MAX_MNTOPT_STR] = { '\0', };
+ ssize_t cchOpts = RTStrPrintf2(szOpts, sizeof(szOpts),
+ "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000,tag=%s", gidMount, g_szTag);
+ if (cchOpts <= 0)
+ {
+ VGSvcError("vbsvcAutomounterMountIt: szOpts overflow! %zd\n", cchOpts);
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, MS_OPTIONSTR, "vboxfs",
+ NULL /*dataptr*/, 0 /* datalen */, szOpts, MAX_MNTOPT_STR);
+ if (rc == 0)
+ {
+ VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
+ pEntry->pszName, pEntry->pszActualMountPoint);
+ return VINF_SUCCESS;
+ }
+
+ rc = errno;
+ VGSvcError("vbsvcAutomounterMountIt: mount failed for '%s' on '%s' (szOpts=%s): %s (%d)\n",
+ pEntry->pszName, pEntry->pszActualMountPoint, szOpts, strerror(rc), rc);
+ return VERR_OPEN_FAILED;
+
+# else
+# error "PORTME!"
+# endif
+#endif
+}
+
+
+/**
+ * Attempts to mount the given shared folder, adding it to the mount table on
+ * success.
+ *
+ * @returns iTable + 1 on success, iTable on failure.
+ * @param pTable The mount table.
+ * @param iTable The mount table index at which to add the mount.
+ * @param pszName The name of the shared folder mapping.
+ * @param pszMntPt The mount point (hint) specified by the host.
+ * @param fFlags The shared folder flags, SHFL_MIF_XXX.
+ * @param idRoot The root ID.
+ * @param uRootIdVersion The root ID version.
+ * @param fAutoMntPt Whether to try automatically assign a mount point if
+ * pszMntPt doesn't work out. This is set in pass \#3.
+ */
+static uint32_t vbsvcAutomounterMountNewEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable,
+ const char *pszName, const char *pszMntPt, uint64_t fFlags,
+ uint32_t idRoot, uint32_t uRootIdVersion, bool fAutoMntPt)
+{
+ VGSvcVerbose(3, "vbsvcAutomounterMountNewEntry: #%u: '%s' at '%s'%s\n",
+ iTable, pszName, pszMntPt, fAutoMntPt ? " auto-assign" : "");
+
+ /*
+ * First we need to figure out the actual mount point.
+ */
+ char szActualMountPoint[RTPATH_MAX];
+
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ /*
+ * Drive letter based. We only care about the first two characters
+ * and ignore the rest (see further down).
+ */
+ char chNextLetter = 'Z';
+ if (RT_C_IS_ALPHA(pszMntPt[0]) && pszMntPt[1] == ':')
+ szActualMountPoint[0] = RT_C_TO_UPPER(pszMntPt[0]);
+ else if (!fAutoMntPt)
+ return iTable;
+ else
+ szActualMountPoint[0] = chNextLetter--;
+ szActualMountPoint[1] = ':';
+ szActualMountPoint[2] = '\0';
+
+ int rc;
+ for (;;)
+ {
+ rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
+ if (rc == VWRN_NOT_FOUND)
+ break;
+
+ /* next */
+ if (chNextLetter == 'A' || !fAutoMntPt)
+ return iTable;
+ szActualMountPoint[0] = chNextLetter--;
+ }
+
+#else
+ /*
+ * Path based #1: Host specified mount point.
+ */
+
+ /* Skip DOS drive letter if there is a UNIX mount point path following it: */
+ if ( pszMntPt[0] != '/'
+ && pszMntPt[0] != '\0'
+ && pszMntPt[1] == ':'
+ && pszMntPt[2] == '/')
+ pszMntPt += 2;
+
+ /* Try specified mount point if it starts with a UNIX slash: */
+ int rc = VERR_ACCESS_DENIED;
+ if (*pszMntPt == '/')
+ {
+ rc = RTPathAbs(pszMntPt, szActualMountPoint, sizeof(szActualMountPoint));
+ if (RT_SUCCESS(rc))
+ {
+ static const char * const s_apszBlacklist[] =
+ { "/", "/dev", "/bin", "/sbin", "/lib", "/etc", "/var", "/tmp", "/usr", "/usr/bin", "/usr/sbin", "/usr/lib" };
+ for (size_t i = 0; i < RT_ELEMENTS(s_apszBlacklist); i++)
+ if (strcmp(szActualMountPoint, s_apszBlacklist[i]) == 0)
+ {
+ rc = VERR_ACCESS_DENIED;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
+ }
+ }
+ if (rc != VWRN_NOT_FOUND)
+ {
+ if (!fAutoMntPt)
+ return iTable;
+
+ /*
+ * Path based #2: Mount dir + prefix + share.
+ */
+ rc = vbsvcAutomounterQueryMountDirAndPrefix(szActualMountPoint, sizeof(szActualMountPoint));
+ if (RT_SUCCESS(rc))
+ {
+ /* Append a sanitized share name: */
+ size_t const offShare = strlen(szActualMountPoint);
+ size_t offDst = offShare;
+ size_t offSrc = 0;
+ for (;;)
+ {
+ char ch = pszName[offSrc++];
+ if (ch == ' ' || ch == '/' || ch == '\\' || ch == ':' || ch == '$')
+ ch = '_';
+ else if (!ch)
+ break;
+ else if (ch < 0x20 || ch == 0x7f)
+ continue;
+ if (offDst < sizeof(szActualMountPoint) - 1)
+ szActualMountPoint[offDst++] = ch;
+ }
+ szActualMountPoint[offDst] = '\0';
+ if (offDst > offShare)
+ {
+ rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
+ if (rc != VWRN_NOT_FOUND)
+ {
+ /*
+ * Path based #3: Mount dir + prefix + share + _ + number.
+ */
+ if (offDst + 2 >= sizeof(szActualMountPoint))
+ return iTable;
+
+ szActualMountPoint[offDst++] = '_';
+ for (uint32_t iTry = 1; iTry < 10 && rc != VWRN_NOT_FOUND; iTry++)
+ {
+ szActualMountPoint[offDst] = '0' + iTry;
+ szActualMountPoint[offDst + 1] = '\0';
+ rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
+ }
+ if (rc != VWRN_NOT_FOUND)
+ return iTable;
+ }
+ }
+ else
+ VGSvcError("vbsvcAutomounterMountNewEntry: Bad share name: %.*Rhxs", strlen(pszName), pszName);
+ }
+ else
+ VGSvcError("vbsvcAutomounterMountNewEntry: Failed to construct basic auto mount point for '%s'", pszName);
+ }
+#endif
+
+ /*
+ * Prepare a table entry and ensure space in the table..
+ */
+ if (pTable->cEntries + 1 > pTable->cAllocated)
+ {
+ void *pvEntries = RTMemRealloc(pTable->papEntries, sizeof(pTable->papEntries[0]) * (pTable->cAllocated + 8));
+ if (!pvEntries)
+ {
+ VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for growing table (size %u)\n", pTable->cAllocated);
+ return iTable;
+ }
+ pTable->cAllocated += 8;
+ pTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvEntries;
+ }
+
+ PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
+ if (pEntry)
+ {
+ pEntry->idRoot = idRoot;
+ pEntry->uRootIdVersion = uRootIdVersion;
+ pEntry->fFlags = fFlags;
+ pEntry->pszName = RTStrDup(pszName);
+ pEntry->pszMountPoint = RTStrDup(pszMntPt);
+ pEntry->pszActualMountPoint = RTStrDup(szActualMountPoint);
+ if (pEntry->pszName && pEntry->pszMountPoint && pEntry->pszActualMountPoint)
+ {
+ /*
+ * Now try mount it.
+ */
+ rc = vbsvcAutomounterMountIt(pEntry);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cToMove = pTable->cEntries - iTable;
+ if (cToMove > 0)
+ memmove(&pTable->papEntries[iTable + 1], &pTable->papEntries[iTable], cToMove * sizeof(pTable->papEntries[0]));
+ pTable->papEntries[iTable] = pEntry;
+ pTable->cEntries++;
+ return iTable + 1;
+ }
+ }
+ else
+ VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
+ RTMemFree(pEntry->pszActualMountPoint);
+ RTMemFree(pEntry->pszMountPoint);
+ RTMemFree(pEntry->pszName);
+ RTMemFree(pEntry);
+ }
+ else
+ VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
+ return iTable;
+}
+
+
+
+/**
+ * Does the actual unmounting.
+ *
+ * @returns Exactly one of the following IPRT status codes;
+ * @retval VINF_SUCCESS if successfully umounted or nothing was mounted there.
+ * @retval VERR_TRY_AGAIN if the shared folder is busy.
+ * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
+ * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
+ * there.
+ *
+ * @param pszMountPoint The mount point.
+ * @param pszName The shared folder (mapping) name.
+ */
+static int vbsvcAutomounterUnmount(const char *pszMountPoint, const char *pszName)
+{
+ /*
+ * Retry for 5 seconds in a hope that busy mounts will quiet down.
+ */
+ for (unsigned iTry = 0; ; iTry++)
+ {
+ /*
+ * Check what's mounted there before we start umounting stuff.
+ */
+ int rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
+ if (rc == VINF_SUCCESS)
+ { /* pszName is mounted there */ }
+ else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
+ return VINF_SUCCESS;
+ else
+ {
+ Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
+ return VERR_RESOURCE_BUSY;
+ }
+
+ /*
+ * Do host specific unmounting.
+ */
+#ifdef RT_OS_WINDOWS
+ Assert(RT_C_IS_UPPER(pszMountPoint[0]) && pszMountPoint[1] == ':' && pszMountPoint[2] == '\0');
+ RTUTF16 const wszDrive[4] = { pszMountPoint[0], ':', '\0', '\0' };
+ DWORD dwErr = WNetCancelConnection2W(wszDrive, 0 /*dwFlags*/, FALSE /*fForce*/);
+ if (dwErr == NO_ERROR)
+ return VINF_SUCCESS;
+ VGSvcVerbose(2, "vbsvcAutomounterUnmount: WNetCancelConnection2W returns %u for '%s' ('%s')\n", dwErr, pszMountPoint, pszName);
+ if (dwErr == ERROR_NOT_CONNECTED)
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_OS2)
+ APIRET rcOs2 = DosFSAttach(pszMountPoint, "VBOXSF", NULL, 0, FS_DETACH);
+ if (rcOs2 == NO_ERROR)
+ return VINF_SUCCESS;
+ VGSvcVerbose(2, "vbsvcAutomounterUnmount: DosFSAttach failed on '%s' ('%s'): %u\n", pszMountPoint, pszName, rcOs2);
+ if (rcOs2 == ERROR_INVALID_FSD_NAME)
+ return VERR_ACCESS_DENIED;
+ if ( rcOs2 == ERROR_INVALID_DRIVE
+ || rcOs2 == ERROR_INVALID_PATH)
+ return VERR_TRY_AGAIN;
+
+#else
+ int rc2 = umount(pszMountPoint);
+ if (rc2 == 0)
+ {
+ /* Remove the mount directory if not directly under the root dir. */
+ RTPATHPARSED Parsed = { 0 };
+ RTPathParse(pszMountPoint, &Parsed, sizeof(Parsed), RTPATH_STR_F_STYLE_HOST);
+ if (Parsed.cComps >= 3)
+ RTDirRemove(pszMountPoint);
+
+ return VINF_SUCCESS;
+ }
+ rc2 = errno;
+ VGSvcVerbose(2, "vbsvcAutomounterUnmount: umount failed on '%s' ('%s'): %d\n", pszMountPoint, pszName, rc2);
+ if (rc2 != EBUSY && rc2 != EAGAIN)
+ return VERR_ACCESS_DENIED;
+#endif
+
+ /*
+ * Check what's mounted there before we start delaying.
+ */
+ RTThreadSleep(8); /* fudge */
+ rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
+ if (rc == VINF_SUCCESS)
+ { /* pszName is mounted there */ }
+ else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
+ return VINF_SUCCESS;
+ else
+ {
+ Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
+ return VERR_RESOURCE_BUSY;
+ }
+
+ if (iTry >= 5)
+ return VERR_TRY_AGAIN;
+ RTThreadSleep(1000);
+ }
+}
+
+
+/**
+ * Unmounts a mount table entry and evicts it from the table if successful.
+ *
+ * @returns The next iTable (same value on success, +1 on failure).
+ * @param pTable The mount table.
+ * @param iTable The table entry.
+ * @param pszReason Why we're here.
+ */
+static uint32_t vbsvcAutomounterUnmountEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable, const char *pszReason)
+{
+ Assert(iTable < pTable->cEntries);
+ PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
+ VGSvcVerbose(2, "vbsvcAutomounterUnmountEntry: #%u: '%s' at '%s' (reason: %s)\n",
+ iTable, pEntry->pszName, pEntry->pszActualMountPoint, pszReason);
+
+ /*
+ * Do we need to umount the entry? Return if unmount fails and we .
+ */
+ if (pEntry->pszActualMountPoint)
+ {
+ int rc = vbsvcAutomounterUnmount(pEntry->pszActualMountPoint, pEntry->pszName);
+ if (rc == VERR_TRY_AGAIN)
+ {
+ VGSvcVerbose(1, "vbsvcAutomounterUnmountEntry: Keeping '%s' -> '%s' (VERR_TRY_AGAIN)\n",
+ pEntry->pszActualMountPoint, pEntry->pszName);
+ return iTable + 1;
+ }
+ }
+
+ /*
+ * Remove the entry by shifting up the ones after it.
+ */
+ pTable->cEntries -= 1;
+ uint32_t cAfter = pTable->cEntries - iTable;
+ if (cAfter)
+ memmove(&pTable->papEntries[iTable], &pTable->papEntries[iTable + 1], cAfter * sizeof(pTable->papEntries[0]));
+ pTable->papEntries[pTable->cEntries] = NULL;
+
+ RTStrFree(pEntry->pszActualMountPoint);
+ pEntry->pszActualMountPoint = NULL;
+ RTStrFree(pEntry->pszMountPoint);
+ pEntry->pszMountPoint = NULL;
+ RTStrFree(pEntry->pszName);
+ pEntry->pszName = NULL;
+ RTMemFree(pEntry);
+
+ return iTable;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSORTCMP, For sorting the mappings by ID. }
+ */
+static DECLCALLBACK(int) vbsvcSharedFolderMappingCompare(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ RT_NOREF_PV(pvUser);
+ PVBGLR3SHAREDFOLDERMAPPING pMapping1 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement1;
+ PVBGLR3SHAREDFOLDERMAPPING pMapping2 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement2;
+ return pMapping1->u32Root < pMapping2->u32Root ? -1 : pMapping1->u32Root != pMapping2->u32Root ? 1 : 0;
+}
+
+
+/**
+ * Refreshes the mount table.
+ *
+ * @returns true if we've processed the current config, false if we failed to
+ * query the mappings.
+ * @param pTable The mount table to refresh.
+ */
+static bool vbsvcAutomounterRefreshTable(PVBSVCAUTOMOUNTERTABLE pTable)
+{
+ /*
+ * Query the root IDs of all auto-mountable shared folder mappings.
+ */
+ uint32_t cMappings = 0;
+ PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
+ int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("vbsvcAutomounterRefreshTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
+ return false;
+ }
+
+ /*
+ * Walk the table and the mappings in parallel, so we have to make sure
+ * they are both sorted by root ID.
+ */
+ if (cMappings > 1)
+ RTSortShell(paMappings, cMappings, sizeof(paMappings[0]), vbsvcSharedFolderMappingCompare, NULL);
+
+ /*
+ * Pass #1: Do all the umounting.
+ *
+ * By doing the umount pass separately from the mount pass, we can
+ * better handle changing involving the same mount points (switching
+ * mount points between two shares, new share on same mount point but
+ * with lower root ID, ++).
+ */
+ uint32_t iTable = 0;
+ for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
+ {
+ /*
+ * Unmount table entries up to idRootSrc.
+ */
+ uint32_t const idRootSrc = paMappings[iSrc].u32Root;
+ while ( iTable < pTable->cEntries
+ && pTable->papEntries[iTable]->idRoot < idRootSrc)
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped");
+
+ /*
+ * If the paMappings entry and the mount table entry has the same
+ * root ID, umount if anything has changed or if we cannot query
+ * the mapping data.
+ */
+ if (iTable < pTable->cEntries)
+ {
+ PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
+ if (pEntry->idRoot == idRootSrc)
+ {
+ uint32_t uRootIdVer = UINT32_MAX;
+ uint64_t fFlags = 0;
+ char *pszName = NULL;
+ char *pszMntPt = NULL;
+ rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
+ &pszName, &pszMntPt, &fFlags, &uRootIdVer);
+ if (RT_FAILURE(rc))
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "VbglR3SharedFolderQueryFolderInfo failed");
+ else if (pEntry->uRootIdVersion != uRootIdVer)
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "root ID version changed");
+ else if (RTPathCompare(pEntry->pszMountPoint, pszMntPt) != 0)
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "mount point changed");
+ else if (RTStrICmp(pEntry->pszName, pszName) != 0)
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "name changed");
+ else
+ {
+ VGSvcVerbose(3, "vbsvcAutomounterRefreshTable: Unchanged: %s -> %s\n", pEntry->pszMountPoint, pEntry->pszName);
+ iTable++;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszName);
+ RTStrFree(pszMntPt);
+ }
+ }
+ }
+ }
+
+ while (iTable < pTable->cEntries)
+ iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped (tail)");
+
+ VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u entries in mount table after pass #1.\n", pTable->cEntries);
+
+ /*
+ * Pass #2: Try mount new folders that has mount points assigned.
+ * Pass #3: Try mount new folders not mounted in pass #2.
+ */
+ for (uint32_t iPass = 2; iPass <= 3; iPass++)
+ {
+ iTable = 0;
+ for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
+ {
+ uint32_t const idRootSrc = paMappings[iSrc].u32Root;
+
+ /*
+ * Skip tabel entries we couldn't umount in pass #1.
+ */
+ while ( iTable < pTable->cEntries
+ && pTable->papEntries[iTable]->idRoot < idRootSrc)
+ {
+ VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Skipping idRoot=%u %s\n",
+ iPass, iSrc, iTable, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
+ iTable++;
+ }
+
+ /*
+ * New share?
+ */
+ if ( iTable >= pTable->cEntries
+ || pTable->papEntries[iTable]->idRoot != idRootSrc)
+ {
+ uint32_t uRootIdVer = UINT32_MAX;
+ uint64_t fFlags = 0;
+ char *pszName = NULL;
+ char *pszMntPt = NULL;
+ rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
+ &pszName, &pszMntPt, &fFlags, &uRootIdVer);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Mounting idRoot=%u/%u %s\n", iPass, iSrc, iTable,
+ idRootSrc, iTable >= pTable->cEntries ? UINT32_MAX : pTable->papEntries[iTable]->idRoot, pszName);
+ iTable = vbsvcAutomounterMountNewEntry(pTable, iTable, pszName, pszMntPt, fFlags,
+ idRootSrc, uRootIdVer, iPass == 3);
+
+ RTStrFree(pszName);
+ RTStrFree(pszMntPt);
+ }
+ else
+ VGSvcVerbose(1, "vbsvcAutomounterRefreshTable: VbglR3SharedFolderQueryFolderInfo failed: %Rrc\n", rc);
+ }
+ else
+ VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: idRootSrc=%u vs idRoot=%u %s\n", iPass, iSrc,
+ iTable, idRootSrc, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
+ }
+ }
+
+ VbglR3SharedFolderFreeMappings(paMappings);
+ return true;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vbsvcAutomounterWorker(bool volatile *pfShutdown)
+{
+ /*
+ * Tell the control thread that it can continue spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /* Divert old hosts to original auto-mount code. */
+ if (!g_fHostSupportsWaitAndInfoQuery)
+ return vbsvcAutoMountWorkerOld(pfShutdown);
+
+ /*
+ * Initialize the state in case we're restarted...
+ */
+ VBSVCAUTOMOUNTERTABLE MountTable = { 0, 0, NULL };
+ int rc = vbsvcAutomounterPopulateTable(&MountTable);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("vbsvcAutomounterWorker: vbsvcAutomounterPopulateTable failed (%Rrc), quitting!\n", rc);
+ return rc;
+ }
+
+ /*
+ * Work loop.
+ */
+ uint32_t uConfigVer = UINT32_MAX;
+ uint32_t uNewVersion = 0;
+ bool fForceRefresh = true;
+ while (!*pfShutdown)
+ {
+ /*
+ * Update the mounts.
+ */
+ if ( uConfigVer != uNewVersion
+ || fForceRefresh)
+ {
+ fForceRefresh = !vbsvcAutomounterRefreshTable(&MountTable);
+ uConfigVer = uNewVersion;
+ }
+
+ /*
+ * Wait for more to do.
+ */
+ if (!*pfShutdown)
+ {
+ uNewVersion = uConfigVer - 1;
+ VGSvcVerbose(2, "vbsvcAutomounterWorker: Waiting with uConfigVer=%u\n", uConfigVer);
+ rc = VbglR3SharedFolderWaitForMappingsChanges(g_idClientSharedFolders, uConfigVer, &uNewVersion);
+ VGSvcVerbose(2, "vbsvcAutomounterWorker: Woke up with uNewVersion=%u and rc=%Rrc\n", uNewVersion, rc);
+
+ /* Delay a little before doing a table refresh so the GUI can finish
+ all its updates. Delay a little longer on non-shutdown failure to
+ avoid eating too many CPU cycles if something goes wrong here... */
+ if (!*pfShutdown)
+ RTSemEventMultiWait(g_hAutoMountEvent, RT_SUCCESS(rc) ? 256 : 1000);
+ }
+ }
+
+ /*
+ * Destroy the mount table.
+ */
+ while (MountTable.cEntries-- > 0)
+ RTMemFree(MountTable.papEntries[MountTable.cEntries]);
+ MountTable.papEntries = NULL;
+
+ VGSvcVerbose(3, "vbsvcAutomounterWorker: Finished\n");
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vbsvcAutomounterStop(void)
+{
+ RTSemEventMultiSignal(g_hAutoMountEvent);
+ if (g_fHostSupportsWaitAndInfoQuery)
+ VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vbsvcAutomounterTerm(void)
+{
+ VGSvcVerbose(3, "vbsvcAutoMountTerm\n");
+
+ if (g_fHostSupportsWaitAndInfoQuery)
+ VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
+
+ VbglR3SharedFolderDisconnect(g_idClientSharedFolders);
+ g_idClientSharedFolders = 0;
+
+ if (g_hAutoMountEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_hAutoMountEvent);
+ g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'automount' service description.
+ */
+VBOXSERVICE g_AutoMount =
+{
+ /* pszName. */
+ "automount",
+ /* pszDescription. */
+ "Automounter for Shared Folders",
+ /* pszUsage. */
+ NULL,
+ /* pszOptions. */
+ NULL,
+ /* methods */
+ VGSvcDefaultPreInit,
+ VGSvcDefaultOption,
+ vbsvcAutomounterInit,
+ vbsvcAutomounterWorker,
+ vbsvcAutomounterStop,
+ vbsvcAutomounterTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp
new file mode 100644
index 00000000..b4522fbd
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp
@@ -0,0 +1,447 @@
+/* $Id: VBoxServiceBalloon.cpp $ */
+/** @file
+ * VBoxService - Memory Ballooning.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc_memballoon VBoxService - Memory Ballooning
+ *
+ * The Memory Ballooning subservice works with VBoxGuest, PGM and GMM to
+ * dynamically reallocate memory between VMs.
+ *
+ * Memory ballooning is typically used to deal with overcomitting memory on the
+ * host. It allowes you to borrow memory from one or more VMs and make it
+ * available to others. In theory it could also be used to make memory
+ * available to the host system, however memory fragmentation typically makes
+ * that difficult.
+ *
+ * The memory ballooning subservices talks to PGM, GMM and Main via the VMMDev.
+ * It polls for change requests at an interval and executes them when they
+ * arrive. There are two ways we implement the actual ballooning, either
+ * VBoxGuest allocates kernel memory and donates it to the host, or this service
+ * allocates process memory which VBoxGuest then locks down and donates to the
+ * host. While we prefer the former method it is not practicable on all OS and
+ * we have to use the latter.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <VBox/err.h>
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+
+#ifdef RT_OS_LINUX
+# include <iprt/param.h>
+# include <sys/mman.h>
+# ifndef MADV_DONTFORK
+# define MADV_DONTFORK 10
+# endif
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The balloon size. */
+static uint32_t g_cMemBalloonChunks = 0;
+
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
+
+/** The array holding the R3 pointers of the balloon. */
+static void **g_pavBalloon = NULL;
+
+#ifdef RT_OS_LINUX
+/** True = madvise(MADV_DONTFORK) works, false otherwise. */
+static bool g_fSysMadviseWorks;
+#endif
+
+
+/**
+ * Check whether madvise() works.
+ */
+static void vgsvcBalloonInitMadvise(void)
+{
+#ifdef RT_OS_LINUX
+ void *pv = (void*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pv != MAP_FAILED)
+ {
+ g_fSysMadviseWorks = madvise(pv, PAGE_SIZE, MADV_DONTFORK) == 0;
+ munmap(pv, PAGE_SIZE);
+ }
+#endif
+}
+
+
+/**
+ * Allocate a chunk of the balloon. Fulfil the prerequisite that we can lock this memory
+ * and protect it against fork() in R0. See also suplibOsPageAlloc().
+ */
+static void *VGSvcBalloonAllocChunk(void)
+{
+ size_t cb = VMMDEV_MEMORY_BALLOON_CHUNK_SIZE;
+ char *pu8;
+
+#ifdef RT_OS_LINUX
+ if (!g_fSysMadviseWorks)
+ cb += 2 * PAGE_SIZE;
+
+ pu8 = (char*)mmap(NULL, cb, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pu8 == MAP_FAILED)
+ return NULL;
+
+ if (g_fSysMadviseWorks)
+ {
+ /*
+ * It is not fatal if we fail here but a forked child (e.g. the ALSA sound server)
+ * could crash. Linux < 2.6.16 does not implement madvise(MADV_DONTFORK) but the
+ * kernel seems to split bigger VMAs and that is all that we want -- later we set the
+ * VM_DONTCOPY attribute in supdrvOSLockMemOne().
+ */
+ madvise(pu8, cb, MADV_DONTFORK);
+ }
+ else
+ {
+ /*
+ * madvise(MADV_DONTFORK) is not available (most probably Linux 2.4). Enclose any
+ * mmapped region by two unmapped pages to guarantee that there is exactly one VM
+ * area struct of the very same size as the mmap area.
+ */
+ RTMemProtect(pu8, PAGE_SIZE, RTMEM_PROT_NONE);
+ RTMemProtect(pu8 + cb - PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_NONE);
+ pu8 += PAGE_SIZE;
+ }
+
+#else
+
+ pu8 = (char*)RTMemPageAlloc(cb);
+ if (!pu8)
+ return pu8;
+
+#endif
+
+ memset(pu8, 0, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
+ return pu8;
+}
+
+
+/**
+ * Free an allocated chunk undoing VGSvcBalloonAllocChunk().
+ */
+static void vgsvcBalloonFreeChunk(void *pv)
+{
+ char *pu8 = (char*)pv;
+ size_t cb = VMMDEV_MEMORY_BALLOON_CHUNK_SIZE;
+
+#ifdef RT_OS_LINUX
+
+ if (!g_fSysMadviseWorks)
+ {
+ cb += 2 * PAGE_SIZE;
+ pu8 -= PAGE_SIZE;
+ /* This is not really necessary */
+ RTMemProtect(pu8, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemProtect(pu8 + cb - PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ }
+ munmap(pu8, cb);
+
+#else
+
+ RTMemPageFree(pu8, cb);
+
+#endif
+}
+
+
+/**
+ * Adapt the R0 memory balloon by granting/reclaiming 1MB chunks to/from R0.
+ *
+ * returns IPRT status code.
+ * @param cNewChunks The new number of 1MB chunks in the balloon.
+ */
+static int vgsvcBalloonSetUser(uint32_t cNewChunks)
+{
+ if (cNewChunks == g_cMemBalloonChunks)
+ return VINF_SUCCESS;
+
+ VGSvcVerbose(3, "vgsvcBalloonSetUser: cNewChunks=%u g_cMemBalloonChunks=%u\n", cNewChunks, g_cMemBalloonChunks);
+ int rc = VINF_SUCCESS;
+ if (cNewChunks > g_cMemBalloonChunks)
+ {
+ /* inflate */
+ g_pavBalloon = (void**)RTMemRealloc(g_pavBalloon, cNewChunks * sizeof(void*));
+ uint32_t i;
+ for (i = g_cMemBalloonChunks; i < cNewChunks; i++)
+ {
+ void *pv = VGSvcBalloonAllocChunk();
+ if (!pv)
+ break;
+ rc = VbglR3MemBalloonChange(pv, /* inflate=*/ true);
+ if (RT_SUCCESS(rc))
+ {
+ g_pavBalloon[i] = pv;
+#ifndef RT_OS_SOLARIS
+ /*
+ * Protect against access by dangling pointers (ignore errors as it may fail).
+ * On Solaris it corrupts the address space leaving the process unkillable. This
+ * could perhaps be related to what the underlying segment driver does; currently
+ * just disable it.
+ */
+ RTMemProtect(pv, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, RTMEM_PROT_NONE);
+#endif
+ g_cMemBalloonChunks++;
+ }
+ else
+ {
+ vgsvcBalloonFreeChunk(pv);
+ break;
+ }
+ }
+ VGSvcVerbose(3, "vgsvcBalloonSetUser: inflation complete. chunks=%u rc=%d\n", i, rc);
+ }
+ else
+ {
+ /* deflate */
+ uint32_t i;
+ for (i = g_cMemBalloonChunks; i-- > cNewChunks;)
+ {
+ void *pv = g_pavBalloon[i];
+ rc = VbglR3MemBalloonChange(pv, /* inflate=*/ false);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_SOLARIS
+ /* unprotect */
+ RTMemProtect(pv, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+#endif
+ vgsvcBalloonFreeChunk(pv);
+ g_pavBalloon[i] = NULL;
+ g_cMemBalloonChunks--;
+ }
+ else
+ break;
+ VGSvcVerbose(3, "vgsvcBalloonSetUser: deflation complete. chunks=%u rc=%d\n", i, rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vgsvcBalloonInit(void)
+{
+ VGSvcVerbose(3, "vgsvcBalloonInit\n");
+
+ int rc = RTSemEventMultiCreate(&g_MemBalloonEvent);
+ AssertRCReturn(rc, rc);
+
+ vgsvcBalloonInitMadvise();
+
+ g_cMemBalloonChunks = 0;
+ uint32_t cNewChunks = 0;
+ bool fHandleInR3;
+
+ /* Check balloon size */
+ rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "MemBalloon: New balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
+ if (fHandleInR3)
+ rc = vgsvcBalloonSetUser(cNewChunks);
+ else
+ g_cMemBalloonChunks = cNewChunks;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* If the service was not found, we disable this service without
+ causing VBoxService to fail. */
+ if ( rc == VERR_NOT_IMPLEMENTED
+#ifdef RT_OS_WINDOWS /** @todo r=bird: Windows kernel driver should return VERR_NOT_IMPLEMENTED,
+ * VERR_INVALID_PARAMETER has too many other uses. */
+ || rc == VERR_INVALID_PARAMETER
+#endif
+ )
+ {
+ VGSvcVerbose(0, "MemBalloon: Memory ballooning support is not available\n");
+ rc = VERR_SERVICE_DISABLED;
+ }
+ else
+ {
+ VGSvcVerbose(3, "MemBalloon: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
+ rc = VERR_SERVICE_DISABLED; /** @todo Playing safe for now, figure out the exact status codes here. */
+ }
+ RTSemEventMultiDestroy(g_MemBalloonEvent);
+ g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Query the size of the memory balloon, given as a page count.
+ *
+ * @returns Number of pages.
+ * @param cbPage The page size.
+ */
+uint32_t VGSvcBalloonQueryPages(uint32_t cbPage)
+{
+ Assert(cbPage > 0);
+ return g_cMemBalloonChunks * (VMMDEV_MEMORY_BALLOON_CHUNK_SIZE / cbPage);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vgsvcBalloonWorker(bool volatile *pfShutdown)
+{
+ /* Start monitoring of the stat event change event. */
+ int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
+ return rc;
+ }
+
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Now enter the loop retrieving runtime data continuously.
+ */
+ for (;;)
+ {
+ uint32_t fEvents = 0;
+
+ /* Check if an update interval change is pending. */
+ rc = VbglR3WaitEvent(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
+ if ( RT_SUCCESS(rc)
+ && (fEvents & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST))
+ {
+ uint32_t cNewChunks;
+ bool fHandleInR3;
+ rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "vgsvcBalloonInit: new balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
+ if (fHandleInR3)
+ {
+ rc = vgsvcBalloonSetUser(cNewChunks);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(3, "vgsvcBalloonInit: failed to set balloon size %d MB (%s memory)\n",
+ cNewChunks, fHandleInR3 ? "R3" : "R0");
+ }
+ else
+ VGSvcVerbose(3, "vgsvcBalloonInit: successfully set requested balloon size %d.\n", cNewChunks);
+ }
+ else
+ g_cMemBalloonChunks = cNewChunks;
+ }
+ else
+ VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
+ }
+
+ /*
+ * Block for a while.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ int rc2 = RTSemEventMultiWait(g_MemBalloonEvent, 5000);
+ if (*pfShutdown)
+ break;
+ if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
+ {
+ VGSvcError("vgsvcBalloonInit: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
+ rc = rc2;
+ break;
+ }
+ }
+
+ /* Cancel monitoring of the memory balloon change event. */
+ rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_BALLOON_CHANGE_REQUEST);
+ if (RT_FAILURE(rc))
+ VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
+
+ VGSvcVerbose(3, "vgsvcBalloonInit: finished mem balloon change request thread\n");
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcBalloonStop(void)
+{
+ RTSemEventMultiSignal(g_MemBalloonEvent);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcBalloonTerm(void)
+{
+ if (g_MemBalloonEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_MemBalloonEvent);
+ g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'memballoon' service description.
+ */
+VBOXSERVICE g_MemBalloon =
+{
+ /* pszName. */
+ "memballoon",
+ /* pszDescription. */
+ "Memory Ballooning",
+ /* pszUsage. */
+ NULL,
+ /* pszOptions. */
+ NULL,
+ /* methods */
+ VGSvcDefaultPreInit,
+ VGSvcDefaultOption,
+ vgsvcBalloonInit,
+ vgsvcBalloonWorker,
+ vgsvcBalloonStop,
+ vgsvcBalloonTerm
+};
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp
new file mode 100644
index 00000000..cd18432f
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp
@@ -0,0 +1,1129 @@
+/** $Id: VBoxServiceClipboard-os2.cpp $ */
+/** @file
+ * VBoxService - Guest Additions Clipboard Service, OS/2.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc_clipboard VBoxService - Clipboard (OS/2)
+ *
+ * The Clipboard subservice provides clipboard sharing for OS/2 guests only.
+ *
+ * This was the second subservice that was added to VBoxService. OS/2 is a
+ * single user system and we don't provide any VBoxTray or VBoxClient like
+ * processes. Because it's kind of simple system, it became natural to put the
+ * clipboard sharing here in VBoxService for OS/2.
+ *
+ * In addition to integrating with the native OS/2 PM clipboard formats, we also
+ * try provide the Odin32, a windows API layer for OS/2 (developed by Sander van
+ * Leeuwen and friends, later mainly InnoTek), with additional formats.
+ *
+ * Bitmaps are currently not supported, but that can easily be added should the
+ * need ever arrise.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_PM
+#define INCL_ERRORS
+#include <os2.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/utf16.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include "VBoxServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Header for Odin32 specific clipboard entries.
+ * (Used to get the correct size of the data.)
+ */
+typedef struct _Odin32ClipboardHeader
+{
+ /** magic number */
+ char achMagic[8];
+ /** Size of the following data.
+ * (The interpretation depends on the type.) */
+ unsigned cbData;
+ /** Odin32 format number. */
+ unsigned uFormat;
+} CLIPHEADER, *PCLIPHEADER;
+
+#define CLIPHEADER_MAGIC "Odin\1\0\1"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** The control thread (main) handle.
+ * Only used to avoid some queue creation trouble. */
+static RTTHREAD g_ThreadCtrl = NIL_RTTHREAD;
+/** The HAB of the control thread (main). */
+static HAB g_habCtrl = NULLHANDLE;
+/** The HMQ of the control thread (main). */
+static HMQ g_hmqCtrl = NULLHANDLE;
+
+/** The Listener thread handle. */
+static RTTHREAD g_ThreadListener = NIL_RTTHREAD;
+/** The HAB of the listener thread. */
+static HAB g_habListener = NULLHANDLE;
+/** The HMQ of the listener thread. */
+static HMQ g_hmqListener = NULLHANDLE;
+/** Indicator that gets set if the listener thread is successfully initialized. */
+static bool volatile g_fListenerOkay = false;
+
+/** The HAB of the worker thread. */
+static HAB g_habWorker = NULLHANDLE;
+/** The HMQ of the worker thread. */
+static HMQ g_hmqWorker = NULLHANDLE;
+/** The object window handle. */
+static HWND g_hwndWorker = NULLHANDLE;
+/** The timer id returned by WinStartTimer. */
+static ULONG g_idWorkerTimer = ~0UL;
+/** The state of the clipboard.
+ * @remark I'm trying out the 'k' prefix from the mac here, bear with me. */
+static enum
+{
+ /** The clipboard hasn't been initialized yet. */
+ kClipboardState_Uninitialized = 0,
+ /** WinSetClipbrdViewer call in progress, ignore WM_DRAWCLIPBOARD. */
+ kClipboardState_SettingViewer,
+ /** We're monitoring the clipboard as a viewer. */
+ kClipboardState_Viewer,
+ /** We're monitoring the clipboard using polling.
+ * This usually means something is wrong... */
+ kClipboardState_Polling,
+ /** We're destroying the clipboard content, ignore WM_DESTROYCLIPBOARD. */
+ kClipboardState_Destroying,
+ /** We're owning the clipboard (i.e. we have data on it). */
+ kClipboardState_Owner
+} g_enmState = kClipboardState_Uninitialized;
+/** Set if the clipboard was empty the last time we polled it. */
+static bool g_fEmptyClipboard = false;
+
+/** A clipboard format atom for the dummy clipboard data we insert
+ * watching for clipboard changes. If this format is found on the
+ * clipboard, the empty clipboard function has not been called
+ * since we last polled it. */
+static ATOM g_atomNothingChanged = 0;
+
+/** The clipboard connection client ID. */
+static uint32_t g_u32ClientId;
+/** Odin32 CF_UNICODETEXT. See user32.cpp. */
+static ATOM g_atomOdin32UnicodeText = 0;
+/** Odin32 CF_UNICODETEXT. See user32.cpp. */
+#define SZFMT_ODIN32_UNICODETEXT (PCSZ)"Odin32 UnicodeText"
+
+
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnPreInit}
+ */
+static DECLCALLBACK(int) vgsvcClipboardOs2PreInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnOption}
+ */
+static DECLCALLBACK(int) vgsvcClipboardOs2Option(const char **ppszShort, int argc, char **argv, int *pi)
+{
+ NOREF(ppszShort);
+ NOREF(argc);
+ NOREF(argv);
+ NOREF(pi);
+
+ return -1;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vgsvcClipboardOs2Init(void)
+{
+ int rc = VERR_GENERAL_FAILURE;
+ g_ThreadCtrl = RTThreadSelf();
+
+ /*
+ * Make PM happy.
+ */
+ PPIB pPib;
+ PTIB pTib;
+ DosGetInfoBlocks(&pTib, &pPib);
+ pPib->pib_ultype = 3; /* PM session type */
+
+ /*
+ * Since we have to send shutdown messages and such from the
+ * service controller (main) thread, create a HAB and HMQ for it.
+ */
+ g_habCtrl = WinInitialize(0);
+ if (g_habCtrl == NULLHANDLE)
+ {
+ VGSvcError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
+ return VERR_GENERAL_FAILURE;
+ }
+ g_hmqCtrl = WinCreateMsgQueue(g_habCtrl, 0);
+ if (g_hmqCtrl != NULLHANDLE)
+ {
+ WinCancelShutdown(g_hmqCtrl, TRUE); /* We don't care about shutdown */
+
+ /*
+ * Create the 'nothing-changed' format.
+ */
+ g_atomNothingChanged = WinAddAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox Clipboard Service");
+ LONG lLastError = WinGetLastError(g_habCtrl);
+ if (g_atomNothingChanged == 0)
+ g_atomNothingChanged = WinFindAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox Clipboard Service");
+ if (g_atomNothingChanged)
+ {
+ /*
+ * Connect to the clipboard service.
+ */
+ VGSvcVerbose(4, "clipboard: connecting\n");
+ rc = VbglR3ClipboardConnect(&g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create any extra clipboard type atoms, like the odin unicode text.
+ */
+ g_atomOdin32UnicodeText = WinAddAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
+ lLastError = WinGetLastError(g_habCtrl);
+ if (g_atomOdin32UnicodeText == 0)
+ g_atomOdin32UnicodeText = WinFindAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
+ if (g_atomOdin32UnicodeText == 0)
+ VGSvcError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n",
+ lLastError, WinGetLastError(g_habCtrl));
+
+ VGSvcVerbose(2, "g_u32ClientId=%RX32 g_atomNothingChanged=%#x g_atomOdin32UnicodeText=%#x\n",
+ g_u32ClientId, g_atomNothingChanged, g_atomOdin32UnicodeText);
+ return VINF_SUCCESS;
+ }
+
+ VGSvcError("Failed to connect to the clipboard service, rc=%Rrc!\n", rc);
+ }
+ else
+ VGSvcError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n",
+ lLastError, WinGetLastError(g_habCtrl));
+ }
+ else
+ VGSvcError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
+ WinTerminate(g_habCtrl);
+ return rc;
+}
+
+
+/**
+ * Check that we're still the view / try make us the viewer.
+ */
+static void vgsvcClipboardOs2PollViewer(void)
+{
+ const int iOrgState = g_enmState;
+
+ HWND hwndClipboardViewer = WinQueryClipbrdViewer(g_habWorker);
+ if (hwndClipboardViewer == g_hwndWorker)
+ return;
+
+ if (hwndClipboardViewer == NULLHANDLE)
+ {
+ /* The API will send a WM_DRAWCLIPBOARD message before returning. */
+ g_enmState = kClipboardState_SettingViewer;
+ if (WinSetClipbrdViewer(g_habWorker, g_hwndWorker))
+ g_enmState = kClipboardState_Viewer;
+ else
+ g_enmState = kClipboardState_Polling;
+ }
+ else
+ g_enmState = kClipboardState_Polling;
+ if ((int)g_enmState != iOrgState)
+ {
+ if (g_enmState == kClipboardState_Viewer)
+ VGSvcVerbose(3, "clipboard: viewer\n");
+ else
+ VGSvcVerbose(3, "clipboard: poller\n");
+ }
+}
+
+
+/**
+ * Advertise the formats available from the host.
+ *
+ * @param fFormats The formats available on the host.
+ */
+static void vgsvcClipboardOs2AdvertiseHostFormats(uint32_t fFormats)
+{
+ /*
+ * Open the clipboard and switch to 'destruction' mode.
+ * Make sure we stop being viewer. Temporarily also make sure we're
+ * not the owner so that PM won't send us any WM_DESTROYCLIPBOARD message.
+ */
+ if (WinOpenClipbrd(g_habWorker))
+ {
+ if (g_enmState == kClipboardState_Viewer)
+ WinSetClipbrdViewer(g_habWorker, NULLHANDLE);
+ if (g_enmState == kClipboardState_Owner)
+ WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+
+ g_enmState = kClipboardState_Destroying;
+ if (WinEmptyClipbrd(g_habWorker))
+ {
+ /*
+ * Take clipboard ownership.
+ */
+ if (WinSetClipbrdOwner(g_habWorker, g_hwndWorker))
+ {
+ g_enmState = kClipboardState_Owner;
+
+ /*
+ * Do the format advertising.
+ */
+ if (fFormats & (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT/* | VBOX_SHARED_CLIPBOARD_FMT_HTML ?? */))
+ {
+ if (!WinSetClipbrdData(g_habWorker, 0, CF_TEXT, CFI_POINTER))
+ VGSvcError("WinSetClipbrdData(,,CF_TEXT,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ if ( g_atomOdin32UnicodeText
+ && !WinSetClipbrdData(g_habWorker, 0, g_atomOdin32UnicodeText, CFI_POINTER))
+ VGSvcError("WinSetClipbrdData(,,g_atomOdin32UnicodeText,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ }
+ if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ {
+ /** @todo bitmaps */
+ }
+ }
+ else
+ {
+ VGSvcError("WinSetClipbrdOwner failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ g_enmState = kClipboardState_Polling;
+ }
+ }
+ else
+ {
+ VGSvcError("WinEmptyClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ g_enmState = kClipboardState_Polling;
+ }
+
+ if (g_enmState == kClipboardState_Polling)
+ {
+ g_fEmptyClipboard = true;
+ vgsvcClipboardOs2PollViewer();
+ }
+
+ WinCloseClipbrd(g_habWorker);
+ }
+ else
+ VGSvcError("vgsvcClipboardOs2AdvertiseHostFormats: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+}
+
+
+/**
+ * Converts (render) to an Odin32 clipboard format.
+ *
+ * We ASSUME we get windows data from the host and all we've got to do here is
+ * slapping an Odin32 header on it.
+ *
+ * @returns Pointer to the data (DosFreeMem).
+ * @param fFormat The host format.
+ * @param usFmt The PM/Odin32 format.
+ * @param pv The data in host formatting.
+ * @param cb The size of the data.
+ */
+static void *vgsvcClipboardOs2ConvertToOdin32(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
+{
+ PVOID pvPM = NULL;
+ APIRET rc = DosAllocSharedMem(&pvPM, NULL, cb + sizeof(CLIPHEADER), OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+ if (rc)
+ {
+ PCLIPHEADER pHdr = (PCLIPHEADER)pvPM;
+ memcpy(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic));
+ pHdr->cbData = cb;
+ if (usFmt == g_atomOdin32UnicodeText)
+ pHdr->uFormat = usFmt;
+ else
+ AssertFailed();
+ memcpy(pHdr + 1, pv, cb);
+ }
+ else
+ {
+ VGSvcError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), rc);
+ pvPM = NULL;
+ }
+ return pvPM;
+}
+
+
+/**
+ * Converts (render) to a PM clipboard format.
+ *
+ * @returns Pointer to the data (DosFreeMem).
+ * @param fFormat The host format.
+ * @param usFmt The PM/Odin32 format.
+ * @param pv The data in host formatting.
+ * @param cb The size of the data.
+ */
+static void *vgsvcClipboardOs2ConvertToPM(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
+{
+ void *pvPM = NULL;
+
+ /*
+ * The Odin32 stuff is simple, we just assume windows data from the host
+ * and all we need to do is add the header.
+ */
+ if ( usFmt
+ && ( usFmt == g_atomOdin32UnicodeText
+ /* || usFmt == ...*/
+ )
+ )
+ pvPM = vgsvcClipboardOs2ConvertToOdin32(fFormat, usFmt, pv, cb);
+ else if (usFmt == CF_TEXT)
+ {
+ /*
+ * Convert the unicode text to the current ctype locale.
+ *
+ * Note that we probably should be using the current PM or DOS codepage
+ * here instead of the LC_CTYPE one which iconv uses by default.
+ * -lazybird
+ */
+ Assert(fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ char *pszUtf8;
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pv, &pszUtf8);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszLocale;
+ rc = RTStrUtf8ToCurrentCP(&pszLocale, pszUtf8);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbPM = strlen(pszLocale) + 1;
+ APIRET orc = DosAllocSharedMem(&pvPM, NULL, cbPM, OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+ if (orc == NO_ERROR)
+ memcpy(pvPM, pszLocale, cbPM);
+ else
+ {
+ VGSvcError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), orc);
+ pvPM = NULL;
+ }
+ RTStrFree(pszLocale);
+ }
+ else
+ VGSvcError("RTStrUtf8ToCurrentCP() -> %Rrc\n", rc);
+ RTStrFree(pszUtf8);
+ }
+ else
+ VGSvcError("RTUtf16ToUtf8() -> %Rrc\n", rc);
+ }
+
+ return pvPM;
+}
+
+
+/**
+ * Tries to deliver an advertised host format.
+ *
+ * @param usFmt The PM format name.
+ *
+ * @remark We must not try open the clipboard here because WM_RENDERFMT is a
+ * request send synchronously by someone who has already opened the
+ * clipboard. We would enter a deadlock trying to open it here.
+ */
+static void vgsvcClipboardOs2RenderFormat(USHORT usFmt)
+{
+ bool fSucceeded = false;
+
+ /*
+ * Determine which format.
+ */
+ uint32_t fFormat;
+ if ( usFmt == CF_TEXT
+ || usFmt == g_atomOdin32UnicodeText)
+ fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ else /** @todo bitmaps */
+ fFormat = 0;
+ if (fFormat)
+ {
+ /*
+ * Query the data from the host.
+ * This might require two iterations because of buffer guessing.
+ */
+ uint32_t cb = _4K;
+ uint32_t cbAllocated = cb;
+ int rc = VERR_NO_MEMORY;
+ void *pv = RTMemPageAllocZ(cbAllocated);
+ if (pv)
+ {
+ VGSvcVerbose(4, "clipboard: reading host data (%#x)\n", fFormat);
+ rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
+ if (rc == VINF_BUFFER_OVERFLOW)
+ {
+ RTMemPageFree(pv, cbAllocated);
+ cbAllocated = cb = RT_ALIGN_32(cb, PAGE_SIZE);
+ pv = RTMemPageAllocZ(cbAllocated);
+ rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
+ }
+ if (RT_FAILURE(rc))
+ RTMemPageFree(pv, cbAllocated);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "clipboard: read %u bytes\n", cb);
+
+ /*
+ * Convert the host clipboard data to PM clipboard data and set it.
+ */
+ PVOID pvPM = vgsvcClipboardOs2ConvertToPM(fFormat, usFmt, pv, cb);
+ if (pvPM)
+ {
+ if (WinSetClipbrdData(g_habWorker, (ULONG)pvPM, usFmt, CFI_POINTER))
+ fSucceeded = true;
+ else
+ {
+ VGSvcError("vgsvcClipboardOs2RenderFormat: WinSetClipbrdData(,%p,%#x, CF_POINTER) failed, lasterror=%lx\n",
+ pvPM, usFmt, WinGetLastError(g_habWorker));
+ DosFreeMem(pvPM);
+ }
+ }
+ RTMemPageFree(pv, cbAllocated);
+ }
+ else
+ VGSvcError("vgsvcClipboardOs2RenderFormat: Failed to query / allocate data. rc=%Rrc cb=%#RX32\n", rc, cb);
+ }
+
+ /*
+ * Empty the clipboard on failure so we don't end up in any loops.
+ */
+ if (!fSucceeded)
+ {
+ WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+ g_enmState = kClipboardState_Destroying;
+ WinEmptyClipbrd(g_habWorker);
+ g_enmState = kClipboardState_Polling;
+ g_fEmptyClipboard = true;
+ vgsvcClipboardOs2PollViewer();
+ }
+}
+
+
+/**
+ * Sends data to the host.
+ *
+ * @param fFormat The data format the host is requesting.
+ */
+static void vgsvcClipboardOs2SendDataToHost(uint32_t fFormat)
+{
+ if (WinOpenClipbrd(g_habWorker))
+ {
+ PRTUTF16 pwszFree = NULL;
+ void *pv = NULL;
+ uint32_t cb = 0;
+
+ if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ /* Got any odin32 unicode text? */
+ PVOID pvPM;
+ PCLIPHEADER pHdr = (PCLIPHEADER)WinQueryClipbrdData(g_habWorker, g_atomOdin32UnicodeText);
+ if ( pHdr
+ && !memcmp(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic)))
+ {
+ pv = pHdr + 1;
+ cb = pHdr->cbData;
+ }
+
+ /* Got any CF_TEXT? */
+ if ( !pv
+ && (pvPM = (PVOID)WinQueryClipbrdData(g_habWorker, CF_TEXT)) != NULL)
+ {
+ char *pszUtf8;
+ int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)pvPM);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwsz;
+ rc = RTStrToUtf16(pszUtf8, &pwsz);
+ if (RT_SUCCESS(rc))
+ {
+ pv = pwszFree = pwsz;
+ cb = (RTUtf16Len(pwsz) + 1) * sizeof(RTUTF16);
+ }
+ RTStrFree(pszUtf8);
+ }
+ }
+ }
+ if (!pv)
+ VGSvcError("vgsvcClipboardOs2SendDataToHost: couldn't find data for %#x\n", fFormat);
+
+ /*
+ * Now, sent whatever we've got to the host (it's waiting).
+ */
+ VGSvcVerbose(4, "clipboard: writing %pv/%#d (fFormat=%#x)\n", pv, cb, fFormat);
+ VbglR3ClipboardWriteData(g_u32ClientId, fFormat, pv, cb);
+ RTUtf16Free(pwszFree);
+
+ WinCloseClipbrd(g_habWorker);
+ }
+ else
+ {
+ VGSvcError("vgsvcClipboardOs2SendDataToHost: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ VGSvcVerbose(4, "clipboard: writing NULL/0 (fFormat=%x)\n", fFormat);
+ VbglR3ClipboardWriteData(g_u32ClientId, fFormat, NULL, 0);
+ }
+}
+
+
+/**
+ * Figure out what's on the clipboard and report it to the host.
+ */
+static void vgsvcClipboardOs2ReportFormats(void)
+{
+ uint32_t fFormats = 0;
+ ULONG ulFormat = 0;
+ while ((ulFormat = WinEnumClipbrdFmts(g_habWorker, ulFormat)) != 0)
+ {
+ if ( ulFormat == CF_TEXT
+ || ulFormat == g_atomOdin32UnicodeText)
+ fFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ /** @todo else bitmaps and stuff. */
+ }
+ VGSvcVerbose(4, "clipboard: reporting fFormats=%#x\n", fFormats);
+ VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
+}
+
+
+/**
+ * Poll the clipboard for changes.
+ *
+ * This is called both when we're the viewer and when we're
+ * falling back to polling. If something has changed it will
+ * notify the host.
+ */
+static void vgsvcClipboardOs2Poll(void)
+{
+ if (WinOpenClipbrd(g_habWorker))
+ {
+ /*
+ * If our dummy is no longer there, something has actually changed,
+ * unless the clipboard is really empty.
+ */
+ ULONG fFmtInfo;
+ if (!WinQueryClipbrdFmtInfo(g_habWorker, g_atomNothingChanged, &fFmtInfo))
+ {
+ if (WinEnumClipbrdFmts(g_habWorker, 0) != 0)
+ {
+ g_fEmptyClipboard = false;
+ vgsvcClipboardOs2ReportFormats();
+
+ /* inject the dummy */
+ PVOID pv;
+ APIRET rc = DosAllocSharedMem(&pv, NULL, 1, OBJ_GIVEABLE | OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+ if (rc == NO_ERROR)
+ {
+ if (WinSetClipbrdData(g_habWorker, (ULONG)pv, g_atomNothingChanged, CFI_POINTER))
+ VGSvcVerbose(4, "clipboard: Added dummy item.\n");
+ else
+ {
+ VGSvcError("vgsvcClipboardOs2Poll: WinSetClipbrdData failed, lasterr=%#lx\n", WinGetLastError(g_habWorker));
+ DosFreeMem(pv);
+ }
+ }
+ else
+ VGSvcError("vgsvcClipboardOs2Poll: DosAllocSharedMem(,,1,) -> %ld\n", rc);
+ }
+ else if (!g_fEmptyClipboard)
+ {
+ g_fEmptyClipboard = true;
+ VGSvcVerbose(3, "Reporting empty clipboard\n");
+ VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+ }
+ }
+ WinCloseClipbrd(g_habWorker);
+ }
+ else
+ VGSvcError("vgsvcClipboardOs2Poll: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+}
+
+
+/**
+ * The clipboard we owned was destroyed by someone else.
+ */
+static void vgsvcClipboardOs2Destroyed(void)
+{
+ /* make sure we're no longer the owner. */
+ if (WinQueryClipbrdOwner(g_habWorker) == g_hwndWorker)
+ WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+
+ /* switch to polling state and notify the host. */
+ g_enmState = kClipboardState_Polling;
+ g_fEmptyClipboard = true;
+ VGSvcVerbose(3, "Reporting empty clipboard\n");
+ VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+
+ vgsvcClipboardOs2PollViewer();
+}
+
+
+/**
+ * The window procedure for the object window.
+ *
+ * @returns Message result.
+ *
+ * @param hwnd The window handle.
+ * @param msg The message.
+ * @param mp1 Message parameter 1.
+ * @param mp2 Message parameter 2.
+ */
+static MRESULT EXPENTRY vgsvcClipboardOs2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+ if (msg != WM_TIMER)
+ VGSvcVerbose(6, "vgsvcClipboardOs2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
+
+ switch (msg)
+ {
+ /*
+ * Handle the two system defined messages for object windows.
+ *
+ * We'll just use the CREATE/DESTROY message to create that timer we're
+ * using for the viewer checks and polling fallback.
+ */
+ case WM_CREATE:
+ g_idWorkerTimer = WinStartTimer(g_habWorker, hwnd, 1 /* id */, 1000 /* 1 second */);
+ g_fEmptyClipboard = true;
+ g_enmState = kClipboardState_Polling;
+ return NULL; /* FALSE(/NULL) == Continue*/
+
+ case WM_DESTROY:
+ WinStopTimer(g_habWorker, hwnd, g_idWorkerTimer);
+ g_idWorkerTimer = ~0UL;
+ g_hwndWorker = NULLHANDLE;
+ break;
+
+ /*
+ * Clipboard viewer message - the content has been changed.
+ * This is sent *after* releasing the clipboard sem
+ * and during the WinSetClipbrdViewer call.
+ */
+ case WM_DRAWCLIPBOARD:
+ if (g_enmState == kClipboardState_SettingViewer)
+ break;
+ AssertMsgBreak(g_enmState == kClipboardState_Viewer, ("g_enmState=%d\n", g_enmState));
+ vgsvcClipboardOs2Poll();
+ break;
+
+ /*
+ * Clipboard owner message - the content was replaced.
+ * This is sent by someone with an open clipboard, so don't try open it now.
+ */
+ case WM_DESTROYCLIPBOARD:
+ if (g_enmState == kClipboardState_Destroying)
+ break; /* it's us doing the replacing, ignore. */
+ AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState));
+ vgsvcClipboardOs2Destroyed();
+ break;
+
+ /*
+ * Clipboard owner message - somebody is requesting us to render a format.
+ * This is called by someone which owns the clipboard, but that's fine.
+ */
+ case WM_RENDERFMT:
+ AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState));
+ vgsvcClipboardOs2RenderFormat(SHORT1FROMMP(mp1));
+ break;
+
+ /*
+ * Clipboard owner message - we're about to quit and should render all formats.
+ *
+ * However, because we're lazy, we'll just ASSUME that since we're quitting
+ * we're probably about to shutdown or something and there is no point in
+ * doing anything here except for emptying the clipboard and removing
+ * ourselves as owner. Any failures at this point are silently ignored.
+ */
+ case WM_RENDERALLFMTS:
+ WinOpenClipbrd(g_habWorker);
+ WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+ g_enmState = kClipboardState_Destroying;
+ WinEmptyClipbrd(g_habWorker);
+ g_enmState = kClipboardState_Polling;
+ g_fEmptyClipboard = true;
+ WinCloseClipbrd(g_habWorker);
+ break;
+
+ /*
+ * Listener message - the host has new formats to offer.
+ */
+ case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
+ vgsvcClipboardOs2AdvertiseHostFormats(LONGFROMMP(mp1));
+ break;
+
+ /*
+ * Listener message - the host wish to read our clipboard data.
+ */
+ case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
+ vgsvcClipboardOs2SendDataToHost(LONGFROMMP(mp1));
+ break;
+
+ /*
+ * This is just a fallback polling strategy in case some other
+ * app is trying to view the clipboard too. We also use this
+ * to try recover from errors.
+ *
+ * Because the way the clipboard service works, we have to monitor
+ * it all the time and cannot get away with simpler solutions like
+ * synergy is employing (basically checking upon entering and leaving
+ * a desktop).
+ */
+ case WM_TIMER:
+ if ( g_enmState != kClipboardState_Viewer
+ && g_enmState != kClipboardState_Polling)
+ break;
+
+ /* Lost the position as clipboard viewer?*/
+ if (g_enmState == kClipboardState_Viewer)
+ {
+ if (WinQueryClipbrdViewer(g_habWorker) == hwnd)
+ break;
+ g_enmState = kClipboardState_Polling;
+ }
+
+ /* poll for changes */
+ vgsvcClipboardOs2Poll();
+ vgsvcClipboardOs2PollViewer();
+ break;
+
+
+ /*
+ * Clipboard owner messages dealing with owner drawn content.
+ * We shouldn't be seeing any of these.
+ */
+ case WM_PAINTCLIPBOARD:
+ case WM_SIZECLIPBOARD:
+ case WM_HSCROLLCLIPBOARD:
+ case WM_VSCROLLCLIPBOARD:
+ AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+ break;
+
+ /*
+ * We shouldn't be seeing any other messages according to the docs.
+ * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message
+ * during WinCreateWindow. So, ignore that and assert on anything else.
+ */
+ default:
+ AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+ case WM_ADJUSTWINDOWPOS:
+ break;
+ }
+ return NULL;
+}
+
+
+/**
+ * The listener thread.
+ *
+ * This thread is dedicated to listening for host messages and forwarding
+ * these to the worker thread (using PM).
+ *
+ * The thread will set g_fListenerOkay and signal its user event when it has
+ * completed initialization. In the case of init failure g_fListenerOkay will
+ * not be set.
+ *
+ * @returns Init error code or VINF_SUCCESS.
+ * @param ThreadSelf Our thread handle.
+ * @param pvUser Pointer to the clipboard service shutdown indicator.
+ */
+static DECLCALLBACK(int) vgsvcClipboardOs2Listener(RTTHREAD ThreadSelf, void *pvUser)
+{
+ bool volatile *pfShutdown = (bool volatile *)pvUser;
+ int rc = VERR_GENERAL_FAILURE;
+ VGSvcVerbose(3, "vgsvcClipboardOs2Listener: ThreadSelf=%RTthrd\n", ThreadSelf);
+
+ g_habListener = WinInitialize(0);
+ if (g_habListener != NULLHANDLE)
+ {
+ g_hmqListener = WinCreateMsgQueue(g_habListener, 0);
+ if (g_hmqListener != NULLHANDLE)
+ {
+ WinCancelShutdown(g_hmqListener, TRUE); /* We don't care about shutdown */
+
+ /*
+ * Tell the worker thread that we're good.
+ */
+ rc = VINF_SUCCESS;
+ ASMAtomicXchgBool(&g_fListenerOkay, true);
+ RTThreadUserSignal(ThreadSelf);
+ VGSvcVerbose(3, "vgsvcClipboardOs2Listener: Started successfully\n");
+
+ /*
+ * Loop until termination is requested.
+ */
+ bool fQuit = false;
+ while (!*pfShutdown && !fQuit)
+ {
+ uint32_t Msg;
+ uint32_t fFormats;
+ rc = VbglR3ClipboardGetHostMsg(g_u32ClientId, &Msg, &fFormats);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "vgsvcClipboardOs2Listener: Msg=%#x fFormats=%#x\n", Msg, fFormats);
+ switch (Msg)
+ {
+ /*
+ * The host has announced available clipboard formats.
+ * Forward the information to the window, so it can later
+ * respond do WM_RENDERFORMAT message.
+ */
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
+ if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
+ MPFROMLONG(fFormats), 0))
+ VGSvcError("WinPostMsg(%lx, FORMATS,,) failed, lasterr=%#lx\n",
+ g_hwndWorker, WinGetLastError(g_habListener));
+ break;
+
+ /*
+ * The host needs data in the specified format.
+ */
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
+ if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ MPFROMLONG(fFormats), 0))
+ VGSvcError("WinPostMsg(%lx, READ_DATA,,) failed, lasterr=%#lx\n",
+ g_hwndWorker, WinGetLastError(g_habListener));
+ break;
+
+ /*
+ * The host is terminating.
+ */
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
+ fQuit = true;
+ break;
+
+ default:
+ VGSvcVerbose(1, "vgsvcClipboardOs2Listener: Unknown message %RU32\n", Msg);
+ break;
+ }
+ }
+ else
+ {
+ if (*pfShutdown)
+ break;
+ VGSvcError("VbglR3ClipboardGetHostMsg failed, rc=%Rrc\n", rc);
+ RTThreadSleep(1000);
+ }
+ } /* the loop */
+
+ WinDestroyMsgQueue(g_hmqListener);
+ }
+ WinTerminate(g_habListener);
+ g_habListener = NULLHANDLE;
+ }
+
+ /* Signal our semaphore to make the worker catch on. */
+ RTThreadUserSignal(ThreadSelf);
+ VGSvcVerbose(3, "vgsvcClipboardOs2Listener: terminating, rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vgsvcClipboardOs2Worker(bool volatile *pfShutdown)
+{
+ int rc = VERR_GENERAL_FAILURE;
+
+ /*
+ * Standard PM init.
+ */
+ g_habWorker = RTThreadSelf() != g_ThreadCtrl ? WinInitialize(0) : g_habCtrl;
+ if (g_habWorker != NULLHANDLE)
+ {
+ g_hmqWorker = RTThreadSelf() != g_ThreadCtrl ? WinCreateMsgQueue(g_habWorker, 0) : g_hmqCtrl;
+ if (g_hmqWorker != NULLHANDLE)
+ {
+ if (g_hmqWorker != g_hmqCtrl)
+ WinCancelShutdown(g_hmqWorker, TRUE); /* We don't care about shutdown */
+
+ /*
+ * Create the object window.
+ */
+ if (WinRegisterClass(g_habWorker, (PCSZ)"VBoxServiceClipboardClass", vgsvcClipboardOs2WinProc, 0, 0))
+ {
+ g_hwndWorker = WinCreateWindow(HWND_OBJECT, /* hwndParent */
+ (PCSZ)"VBoxServiceClipboardClass", /* pszClass */
+ (PCSZ)"VirtualBox Clipboard Service", /* pszName */
+ 0, /* flStyle */
+ 0, 0, 0, 0, /* x, y, cx, cy */
+ NULLHANDLE, /* hwndOwner */
+ HWND_BOTTOM, /* hwndInsertBehind */
+ 42, /* id */
+ NULL, /* pCtlData */
+ NULL); /* pPresParams */
+ if (g_hwndWorker != NULLHANDLE)
+ {
+ VGSvcVerbose(3, "g_hwndWorker=%#lx g_habWorker=%#lx g_hmqWorker=%#lx\n", g_hwndWorker, g_habWorker, g_hmqWorker);
+
+ /*
+ * Create the listener thread.
+ */
+ g_fListenerOkay = false;
+ rc = RTThreadCreate(&g_ThreadListener, vgsvcClipboardOs2Listener, (void *)pfShutdown, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CLIPLISTEN");
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadUserWait(g_ThreadListener, 30*1000);
+ RTThreadUserReset(g_ThreadListener);
+ if (!g_fListenerOkay)
+ RTThreadWait(g_ThreadListener, 60*1000, NULL);
+ if (g_fListenerOkay)
+ {
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * The PM event pump.
+ */
+ VGSvcVerbose(2, "clipboard: Entering PM message loop.\n");
+ rc = VINF_SUCCESS;
+ QMSG qmsg;
+ while (WinGetMsg(g_habWorker, &qmsg, NULLHANDLE, NULLHANDLE, 0))
+ {
+ if (qmsg.msg != WM_TIMER)
+ VGSvcVerbose(6, "WinGetMsg -> hwnd=%p msg=%#x mp1=%p mp2=%p time=%#x ptl=%d,%d rsrv=%#x\n",
+ qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2, qmsg.time, qmsg.ptl.x, qmsg.ptl.y, qmsg.reserved);
+ WinDispatchMsg(g_habWorker, &qmsg);
+ }
+ VGSvcVerbose(2, "clipboard: Exited PM message loop. *pfShutdown=%RTbool\n", *pfShutdown);
+
+ RTThreadWait(g_ThreadListener, 60*1000, NULL);
+ }
+ g_ThreadListener = NIL_RTTHREAD;
+ }
+
+ /*
+ * Got a WM_QUIT, clean up.
+ */
+ if (g_hwndWorker != NULLHANDLE)
+ {
+ WinDestroyWindow(g_hwndWorker);
+ g_hwndWorker = NULLHANDLE;
+ }
+ }
+ else
+ VGSvcError("WinCreateWindow() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+ /* no class deregistration in PM. */
+ }
+ else
+ VGSvcError("WinRegisterClass() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+
+ if (g_hmqCtrl != g_hmqWorker)
+ WinDestroyMsgQueue(g_hmqWorker);
+ g_hmqWorker = NULLHANDLE;
+ }
+ else
+ VGSvcError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+
+ if (g_habCtrl != g_habWorker)
+ WinTerminate(g_habWorker);
+ g_habWorker = NULLHANDLE;
+ }
+ else
+ VGSvcError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcClipboardOs2Stop(void)
+{
+ if ( g_hmqWorker != NULLHANDLE
+ && !WinPostQueueMsg(g_hmqWorker, WM_QUIT, NULL, NULL))
+ VGSvcError("WinPostQueueMsg(g_hmqWorker, WM_QUIT, 0,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
+
+ /* Must disconnect the clipboard here otherwise the listner won't quit and
+ the service shutdown will not stop. */
+ if (g_u32ClientId != 0)
+ {
+ if (g_hmqWorker != NULLHANDLE)
+ RTThreadSleep(32); /* fudge */
+
+ VGSvcVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
+ int rc = VbglR3ClipboardDisconnect(g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ g_u32ClientId = 0;
+ else
+ VGSvcError("clipboard: VbglR3ClipboardDisconnect(%#x) -> %Rrc\n", g_u32ClientId, rc);
+ }
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcClipboardOs2Term(void)
+{
+ if (g_u32ClientId != 0)
+ {
+ VGSvcVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
+ int rc = VbglR3ClipboardDisconnect(g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ g_u32ClientId = 0;
+ else
+ VGSvcError("clipboard: VbglR3ClipboardDisconnect(%#x) -> %Rrc\n", g_u32ClientId, rc);
+ }
+ WinDestroyMsgQueue(g_hmqCtrl);
+ g_hmqCtrl = NULLHANDLE;
+ WinTerminate(g_habCtrl);
+ g_habCtrl = NULLHANDLE;
+}
+
+
+/**
+ * The OS/2 'clipboard' service description.
+ */
+VBOXSERVICE g_Clipboard =
+{
+ /* pszName. */
+ "clipboard",
+ /* pszDescription. */
+ "Shared Clipboard",
+ /* pszUsage. */
+ ""
+ ,
+ /* pszOptions. */
+ ""
+ ,
+ /* methods */
+ vgsvcClipboardOs2PreInit,
+ vgsvcClipboardOs2Option,
+ vgsvcClipboardOs2Init,
+ vgsvcClipboardOs2Worker,
+ vgsvcClipboardOs2Stop,
+ vgsvcClipboardOs2Term
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp
new file mode 100644
index 00000000..dd2b23b8
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp
@@ -0,0 +1,573 @@
+/* $Id: VBoxServiceControl.cpp $ */
+/** @file
+ * VBoxServiceControl - Host-driven Guest Control.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vgsvc_gstctrl VBoxService - Guest Control
+ *
+ * The Guest Control subservice helps implementing the IGuest APIs.
+ *
+ * The communication between this service (and its children) and IGuest goes
+ * over the HGCM GuestControl service.
+ *
+ * The IGuest APIs provides means to manipulate (control) files, directories,
+ * symbolic links and processes within the guest. Most of these means requires
+ * credentials of a guest OS user to operate, though some restricted ones
+ * operates directly as the VBoxService user (root / system service account).
+ *
+ * The current design is that a subprocess is spawned for handling operations as
+ * a given user. This process is represented as IGuestSession in the API. The
+ * subprocess will be spawned as the given use, giving up the privileges the
+ * parent subservice had.
+ *
+ * It will try handle as many of the operations directly from within the
+ * subprocess, but for more complicated things (or things that haven't yet been
+ * converted), it will spawn a helper process that does the actual work.
+ *
+ * These helpers are the typically modeled on similar unix core utilities, like
+ * mkdir, rm, rmdir, cat and so on. The helper tools can also be launched
+ * directly from VBoxManage by the user by prepending the 'vbox_' prefix to the
+ * unix command.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <VBox/err.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/HostServices/GuestControlSvc.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceControl.h"
+#include "VBoxServiceUtils.h"
+
+using namespace guestControl;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The control interval (milliseconds). */
+static uint32_t g_msControlInterval = 0;
+/** The semaphore we're blocking our main control thread on. */
+static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
+/** The VM session ID. Changes whenever the VM is restored or reset. */
+static uint64_t g_idControlSession;
+/** The guest control service client ID. */
+uint32_t g_idControlSvcClient = 0;
+#if 0 /** @todo process limit */
+/** How many started guest processes are kept into memory for supplying
+ * information to the host. Default is 256 processes. If 0 is specified,
+ * the maximum number of processes is unlimited. */
+static uint32_t g_uControlProcsMaxKept = 256;
+#endif
+/** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD).
+ * A guest session thread represents a forked guest session process
+ * of VBoxService. */
+RTLISTANCHOR g_lstControlSessionThreads;
+/** The local session object used for handling all session-related stuff.
+ * When using the legacy guest control protocol (< 2), this session runs
+ * under behalf of the VBoxService main process. On newer protocol versions
+ * each session is a forked version of VBoxService using the appropriate
+ * user credentials for opening a guest session. These forked sessions then
+ * are kept in VBOXSERVICECTRLSESSIONTHREAD structures. */
+VBOXSERVICECTRLSESSION g_Session;
+/** Copy of VbglR3GuestCtrlSupportsOptimizations().*/
+bool g_fControlSupportsOptimizations = true;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
+static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
+static void vgsvcGstCtrlShutdown(void);
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnPreInit}
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlPreInit(void)
+{
+ int rc;
+#ifdef VBOX_WITH_GUEST_PROPS
+ /*
+ * Read the service options from the VM's guest properties.
+ * Note that these options can be overridden by the command line options later.
+ */
+ uint32_t uGuestPropSvcClientID;
+ rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
+ {
+ VGSvcVerbose(0, "Guest property service is not available, skipping\n");
+ rc = VINF_SUCCESS;
+ }
+ else
+ VGSvcError("Failed to connect to the guest property service, rc=%Rrc\n", rc);
+ }
+ else
+ VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
+
+ if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
+ rc = VINF_SUCCESS;
+#else
+ /* Nothing to do here yet. */
+ rc = VINF_SUCCESS;
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Init session object. */
+ rc = VGSvcGstCtrlSessionInit(&g_Session, 0 /* Flags */);
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnOption}
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlOption(const char **ppszShort, int argc, char **argv, int *pi)
+{
+ int rc = -1;
+ if (ppszShort)
+ /* no short options */;
+ else if (!strcmp(argv[*pi], "--control-interval"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi,
+ &g_msControlInterval, 1, UINT32_MAX - 1);
+#ifdef DEBUG
+ else if (!strcmp(argv[*pi], "--control-dump-stdout"))
+ {
+ g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
+ rc = 0; /* Flag this command as parsed. */
+ }
+ else if (!strcmp(argv[*pi], "--control-dump-stderr"))
+ {
+ g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
+ rc = 0; /* Flag this command as parsed. */
+ }
+#endif
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlInit(void)
+{
+ /*
+ * If not specified, find the right interval default.
+ * Then create the event sem to block on.
+ */
+ if (!g_msControlInterval)
+ g_msControlInterval = 1000;
+
+ int rc = RTSemEventMultiCreate(&g_hControlEvent);
+ AssertRCReturn(rc, rc);
+
+ VbglR3GetSessionId(&g_idControlSession); /* The status code is ignored as this information is not available with VBox < 3.2.10. */
+
+ RTListInit(&g_lstControlSessionThreads);
+
+ /*
+ * Try connect to the host service and tell it we want to be master (if supported).
+ */
+ rc = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
+ if (RT_SUCCESS(rc))
+ {
+ g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
+ if (g_fControlSupportsOptimizations)
+ rc = VbglR3GuestCtrlMakeMeMaster(g_idControlSvcClient);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "Guest control service client ID=%RU32%s\n",
+ g_idControlSvcClient, g_fControlSupportsOptimizations ? " w/ optimizations" : "");
+ return VINF_SUCCESS;
+ }
+ VGSvcError("Failed to become guest control master: %Rrc\n", rc);
+ VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
+ }
+ else
+ {
+ /* If the service was not found, we disable this service without
+ causing VBoxService to fail. */
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
+ {
+ VGSvcVerbose(0, "Guest control service is not available\n");
+ rc = VERR_SERVICE_DISABLED;
+ }
+ else
+ VGSvcError("Failed to connect to the guest control service! Error: %Rrc\n", rc);
+ }
+ RTSemEventMultiDestroy(g_hControlEvent);
+ g_hControlEvent = NIL_RTSEMEVENTMULTI;
+ g_idControlSvcClient = 0;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlWorker(bool volatile *pfShutdown)
+{
+ /*
+ * Tell the control thread that it can continue spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+ Assert(g_idControlSvcClient > 0);
+
+ /* Allocate a scratch buffer for messages which also send
+ * payload data with them. */
+ uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */
+ AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), VERR_INVALID_PARAMETER);
+ uint8_t *pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf);
+ AssertReturn(pvScratchBuf, VERR_NO_MEMORY);
+
+ int rc = VINF_SUCCESS; /* (shut up compiler warnings) */
+ int cRetrievalFailed = 0; /* Number of failed message retrievals in a row. */
+ while (!*pfShutdown)
+ {
+ VGSvcVerbose(3, "GstCtrl: Waiting for host msg ...\n");
+ VBGLR3GUESTCTRLCMDCTX ctxHost = { g_idControlSvcClient, 0 /*idContext*/, 2 /*uProtocol*/, 0 /*cParms*/ };
+ uint32_t idMsg = 0;
+ rc = VbglR3GuestCtrlMsgPeekWait(g_idControlSvcClient, &idMsg, &ctxHost.uNumParms, &g_idControlSession);
+ if (RT_SUCCESS(rc))
+ {
+ cRetrievalFailed = 0; /* Reset failed retrieval count. */
+ VGSvcVerbose(4, "idMsg=%RU32 (%s) (%RU32 parms) retrieved\n",
+ idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms);
+
+ /*
+ * Handle the host message.
+ */
+ switch (idMsg)
+ {
+ case HOST_MSG_CANCEL_PENDING_WAITS:
+ VGSvcVerbose(1, "We were asked to quit ...\n");
+ break;
+
+ case HOST_MSG_SESSION_CREATE:
+ rc = vgsvcGstCtrlHandleSessionOpen(&ctxHost);
+ break;
+
+ /* This message is also sent to the child session process (by the host). */
+ case HOST_MSG_SESSION_CLOSE:
+ rc = vgsvcGstCtrlHandleSessionClose(&ctxHost);
+ break;
+
+ default:
+ if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
+ {
+ rc = VbglR3GuestCtrlMsgSkip(g_idControlSvcClient, VERR_NOT_SUPPORTED, idMsg);
+ VGSvcVerbose(1, "Skipped unexpected message idMsg=%RU32 (%s), cParms=%RU32 (rc=%Rrc)\n",
+ idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms, rc);
+ }
+ else
+ {
+ rc = VbglR3GuestCtrlMsgSkipOld(g_idControlSvcClient);
+ VGSvcVerbose(3, "Skipped idMsg=%RU32, cParms=%RU32, rc=%Rrc\n", idMsg, ctxHost.uNumParms, rc);
+ }
+ break;
+ }
+
+ /* Do we need to shutdown? */
+ if (idMsg == HOST_MSG_CANCEL_PENDING_WAITS)
+ break;
+
+ /* Let's sleep for a bit and let others run ... */
+ RTThreadYield();
+ }
+ /*
+ * Handle restore notification from host. All the context IDs (sessions,
+ * files, proceses, etc) are invalidated by a VM restore and must be closed.
+ */
+ else if (rc == VERR_VM_RESTORED)
+ {
+ VGSvcVerbose(1, "The VM session ID changed (i.e. restored).\n");
+ int rc2 = VGSvcGstCtrlSessionClose(&g_Session);
+ AssertRC(rc2);
+ }
+ else
+ {
+ /* Note: VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
+ /** @todo r=bird: Above comment makes no sense. How can you get a timeout in a blocking HGCM call? */
+ VGSvcError("GstCtrl: Getting host message failed with %Rrc\n", rc);
+
+ /* Check for VM session change. */
+ /** @todo We don't need to check the host here. */
+ uint64_t idNewSession = g_idControlSession;
+ int rc2 = VbglR3GetSessionId(&idNewSession);
+ if ( RT_SUCCESS(rc2)
+ && (idNewSession != g_idControlSession))
+ {
+ VGSvcVerbose(1, "GstCtrl: The VM session ID changed\n");
+ g_idControlSession = idNewSession;
+
+ /* Close all opened guest sessions -- all context IDs, sessions etc.
+ * are now invalid. */
+ rc2 = VGSvcGstCtrlSessionClose(&g_Session);
+ AssertRC(rc2);
+
+ /* Do a reconnect. */
+ VGSvcVerbose(1, "Reconnecting to HGCM service ...\n");
+ rc2 = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
+ if (RT_SUCCESS(rc2))
+ {
+ VGSvcVerbose(3, "Guest control service client ID=%RU32\n", g_idControlSvcClient);
+ cRetrievalFailed = 0;
+ continue; /* Skip waiting. */
+ }
+ VGSvcError("Unable to re-connect to HGCM service, rc=%Rrc, bailing out\n", rc);
+ break;
+ }
+
+ if (rc == VERR_INTERRUPTED)
+ RTThreadYield(); /* To be on the safe side... */
+ else if (++cRetrievalFailed <= 16) /** @todo Make this configurable? */
+ RTThreadSleep(1000); /* Wait a bit before retrying. */
+ else
+ {
+ VGSvcError("Too many failed attempts in a row to get next message, bailing out\n");
+ break;
+ }
+ }
+ }
+
+ VGSvcVerbose(0, "Guest control service stopped\n");
+
+ /* Delete scratch buffer. */
+ if (pvScratchBuf)
+ RTMemFree(pvScratchBuf);
+
+ VGSvcVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the message parameters.
+ */
+ VBOXSERVICECTRLSESSIONSTARTUPINFO ssInfo = { 0 };
+ int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx,
+ &ssInfo.uProtocol,
+ ssInfo.szUser, sizeof(ssInfo.szUser),
+ ssInfo.szPassword, sizeof(ssInfo.szPassword),
+ ssInfo.szDomain, sizeof(ssInfo.szDomain),
+ &ssInfo.fFlags, &ssInfo.uSessionID);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Flat out refuse to work with protocol v1 hosts.
+ */
+ if (ssInfo.uProtocol == 2)
+ {
+ pHostCtx->uProtocol = ssInfo.uProtocol;
+ VGSvcVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n", pHostCtx->uClientID, pHostCtx->uProtocol);
+
+/** @todo Someone explain why this code isn't in this file too? v1 support? */
+ rc = VGSvcGstCtrlSessionThreadCreate(&g_lstControlSessionThreads, &ssInfo, NULL /* ppSessionThread */);
+ /* Report failures to the host (successes are taken care of by the session thread). */
+ }
+ else
+ {
+ VGSvcError("The host wants to use protocol v%u, we only support v2!\n", ssInfo.uProtocol);
+ rc = VERR_VERSION_MISMATCH;
+ }
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx, GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Reporting session error status on open failed with rc=%Rrc\n", rc2);
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for opening guest session: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ VGSvcVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ uint32_t idSession;
+ uint32_t fFlags;
+ int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &fFlags, &idSession);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_NOT_FOUND;
+
+ PVBOXSERVICECTRLSESSIONTHREAD pThread;
+ RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node)
+ {
+ if (pThread->StartupInfo.uSessionID == idSession)
+ {
+ rc = VGSvcGstCtrlSessionThreadDestroy(pThread, fFlags);
+ break;
+ }
+ }
+
+#if 0 /** @todo A bit of a mess here as this message goes to both to this process (master) and the session process. */
+ if (RT_FAILURE(rc))
+ {
+ /* Report back on failure. On success this will be done
+ * by the forked session thread. */
+ int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
+ GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Reporting session error status on close failed with rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+#endif
+ VGSvcVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n", idSession, rc);
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for closing guest session: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcGstCtrlStop(void)
+{
+ VGSvcVerbose(3, "Stopping ...\n");
+
+ /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
+ * annoying call since doesn't support timeouts in the posix world. */
+ if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
+ RTSemEventMultiSignal(g_hControlEvent);
+
+ /*
+ * Ask the host service to cancel all pending requests for the main
+ * control thread so that we can shutdown properly here.
+ */
+ if (g_idControlSvcClient)
+ {
+ VGSvcVerbose(3, "Cancelling pending waits (client ID=%u) ...\n",
+ g_idControlSvcClient);
+
+ int rc = VbglR3GuestCtrlCancelPendingWaits(g_idControlSvcClient);
+ if (RT_FAILURE(rc))
+ VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);
+ }
+}
+
+
+/**
+ * Destroys all guest process threads which are still active.
+ */
+static void vgsvcGstCtrlShutdown(void)
+{
+ VGSvcVerbose(2, "Shutting down ...\n");
+
+ int rc2 = VGSvcGstCtrlSessionThreadDestroyAll(&g_lstControlSessionThreads, 0 /* Flags */);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Closing session threads failed with rc=%Rrc\n", rc2);
+
+ rc2 = VGSvcGstCtrlSessionClose(&g_Session);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Closing session failed with rc=%Rrc\n", rc2);
+
+ VGSvcVerbose(2, "Shutting down complete\n");
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcGstCtrlTerm(void)
+{
+ VGSvcVerbose(3, "Terminating ...\n");
+
+ vgsvcGstCtrlShutdown();
+
+ VGSvcVerbose(3, "Disconnecting client ID=%u ...\n", g_idControlSvcClient);
+ VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
+ g_idControlSvcClient = 0;
+
+ if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_hControlEvent);
+ g_hControlEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'vminfo' service description.
+ */
+VBOXSERVICE g_Control =
+{
+ /* pszName. */
+ "control",
+ /* pszDescription. */
+ "Host-driven Guest Control",
+ /* pszUsage. */
+#ifdef DEBUG
+ " [--control-dump-stderr] [--control-dump-stdout]\n"
+#endif
+ " [--control-interval <ms>]"
+ ,
+ /* pszOptions. */
+#ifdef DEBUG
+ " --control-dump-stderr Dumps all guest proccesses stderr data to the\n"
+ " temporary directory.\n"
+ " --control-dump-stdout Dumps all guest proccesses stdout data to the\n"
+ " temporary directory.\n"
+#endif
+ " --control-interval Specifies the interval at which to check for\n"
+ " new control messages. The default is 1000 ms.\n"
+ ,
+ /* methods */
+ vgsvcGstCtrlPreInit,
+ vgsvcGstCtrlOption,
+ vgsvcGstCtrlInit,
+ vgsvcGstCtrlWorker,
+ vgsvcGstCtrlStop,
+ vgsvcGstCtrlTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h
new file mode 100644
index 00000000..e02d0871
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h
@@ -0,0 +1,334 @@
+/* $Id: VBoxServiceControl.h $ */
+/** @file
+ * VBoxServiceControl.h - Internal guest control definitions.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceControl_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceControl_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/critsect.h>
+#include <iprt/list.h>
+#include <iprt/req.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/GuestControl.h>
+#include <VBox/HostServices/GuestControlSvc.h>
+
+
+/**
+ * Pipe IDs for handling the guest process poll set.
+ */
+typedef enum VBOXSERVICECTRLPIPEID
+{
+ VBOXSERVICECTRLPIPEID_UNKNOWN = 0,
+ VBOXSERVICECTRLPIPEID_STDIN = 10,
+ VBOXSERVICECTRLPIPEID_STDIN_WRITABLE = 11,
+ /** Pipe for reading from guest process' stdout. */
+ VBOXSERVICECTRLPIPEID_STDOUT = 40,
+ /** Pipe for reading from guest process' stderr. */
+ VBOXSERVICECTRLPIPEID_STDERR = 50,
+ /** Notification pipe for waking up the guest process
+ * control thread. */
+ VBOXSERVICECTRLPIPEID_IPC_NOTIFY = 100
+} VBOXSERVICECTRLPIPEID;
+
+/**
+ * Structure for one (opened) guest file.
+ */
+typedef struct VBOXSERVICECTRLFILE
+{
+ /** Pointer to list archor of following
+ * list node.
+ * @todo Would be nice to have a RTListGetAnchor(). */
+ PRTLISTANCHOR pAnchor;
+ /** Node to global guest control file list. */
+ /** @todo Use a map later? */
+ RTLISTNODE Node;
+ /** The file name. */
+ char szName[RTPATH_MAX];
+ /** The file handle on the guest. */
+ RTFILE hFile;
+ /** File handle to identify this file. */
+ uint32_t uHandle;
+ /** Context ID. */
+ uint32_t uContextID;
+} VBOXSERVICECTRLFILE;
+/** Pointer to thread data. */
+typedef VBOXSERVICECTRLFILE *PVBOXSERVICECTRLFILE;
+
+typedef struct VBOXSERVICECTRLSESSIONSTARTUPINFO
+{
+ /** The session's protocol version to use. */
+ uint32_t uProtocol;
+ /** The session's ID. */
+ uint32_t uSessionID;
+ /** User name (account) to start the guest session under. */
+ char szUser[GUESTPROCESS_MAX_USER_LEN];
+ /** Password of specified user name (account). */
+ char szPassword[GUESTPROCESS_MAX_PASSWORD_LEN];
+ /** Domain of the user account. */
+ char szDomain[GUESTPROCESS_MAX_DOMAIN_LEN];
+ /** Session creation flags.
+ * @sa VBOXSERVICECTRLSESSIONSTARTUPFLAG_* flags. */
+ uint32_t fFlags;
+} VBOXSERVICECTRLSESSIONSTARTUPINFO;
+/** Pointer to thread data. */
+typedef VBOXSERVICECTRLSESSIONSTARTUPINFO *PVBOXSERVICECTRLSESSIONSTARTUPINFO;
+
+/**
+ * Structure for a guest session thread to
+ * observe/control the forked session instance from
+ * the VBoxService main executable.
+ */
+typedef struct VBOXSERVICECTRLSESSIONTHREAD
+{
+ /** Node to global guest control session list. */
+ /** @todo Use a map later? */
+ RTLISTNODE Node;
+ /** The sessions's startup info. */
+ VBOXSERVICECTRLSESSIONSTARTUPINFO StartupInfo;
+ /** Critical section for thread-safe use. */
+ RTCRITSECT CritSect;
+ /** The worker thread. */
+ RTTHREAD Thread;
+ /** Process handle for forked child. */
+ RTPROCESS hProcess;
+ /** Shutdown indicator; will be set when the thread
+ * needs (or is asked) to shutdown. */
+ bool volatile fShutdown;
+ /** Indicator set by the service thread exiting. */
+ bool volatile fStopped;
+ /** Whether the thread was started or not. */
+ bool fStarted;
+#if 0 /* Pipe IPC not used yet. */
+ /** Pollset containing all the pipes. */
+ RTPOLLSET hPollSet;
+ RTPIPE hStdInW;
+ RTPIPE hStdOutR;
+ RTPIPE hStdErrR;
+ struct StdPipe
+ {
+ RTHANDLE hChild;
+ PRTHANDLE phChild;
+ } StdIn,
+ StdOut,
+ StdErr;
+ /** The notification pipe associated with this guest session.
+ * This is NIL_RTPIPE for output pipes. */
+ RTPIPE hNotificationPipeW;
+ /** The other end of hNotificationPipeW. */
+ RTPIPE hNotificationPipeR;
+#endif
+ /** Pipe for handing the secret key to the session process. */
+ RTPIPE hKeyPipe;
+ /** Secret key. */
+ uint8_t abKey[_4K];
+} VBOXSERVICECTRLSESSIONTHREAD;
+/** Pointer to thread data. */
+typedef VBOXSERVICECTRLSESSIONTHREAD *PVBOXSERVICECTRLSESSIONTHREAD;
+
+/** Flag indicating that this session has been spawned from
+ * the main executable. */
+#define VBOXSERVICECTRLSESSION_FLAG_SPAWN RT_BIT(0)
+/** Flag indicating that this session is anonymous, that is,
+ * it will run start guest processes with the same credentials
+ * as the main executable. */
+#define VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS RT_BIT(1)
+/** Flag indicating that started guest processes will dump their
+ * stdout output to a separate file on disk. For debugging. */
+#define VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT RT_BIT(2)
+/** Flag indicating that started guest processes will dump their
+ * stderr output to a separate file on disk. For debugging. */
+#define VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR RT_BIT(3)
+
+/**
+ * Structure for maintaining a guest session. This also
+ * contains all started threads (e.g. for guest processes).
+ *
+ * This structure can act in two different ways:
+ * - For legacy guest control handling (protocol version < 2)
+ * this acts as a per-guest process structure containing all
+ * the information needed to get a guest process up and running.
+ * - For newer guest control protocols (>= 2) this structure is
+ * part of the forked session child, maintaining all guest
+ * control objects under it.
+ */
+typedef struct VBOXSERVICECTRLSESSION
+{
+ /* The session's startup information. */
+ VBOXSERVICECTRLSESSIONSTARTUPINFO
+ StartupInfo;
+ /** List of active guest process threads
+ * (VBOXSERVICECTRLPROCESS). */
+ RTLISTANCHOR lstProcesses;
+ /** List of guest control files (VBOXSERVICECTRLFILE). */
+ RTLISTANCHOR lstFiles;
+ /** The session's critical section. */
+ RTCRITSECT CritSect;
+ /** Internal session flags, not related
+ * to StartupInfo stuff.
+ * @sa VBOXSERVICECTRLSESSION_FLAG_* flags. */
+ uint32_t fFlags;
+ /** How many processes do we allow keeping around at a time? */
+ uint32_t uProcsMaxKept;
+} VBOXSERVICECTRLSESSION;
+/** Pointer to guest session. */
+typedef VBOXSERVICECTRLSESSION *PVBOXSERVICECTRLSESSION;
+
+/**
+ * Structure holding information for starting a guest
+ * process.
+ */
+typedef struct VBOXSERVICECTRLPROCSTARTUPINFO
+{
+ /** Full qualified path of process to start (without arguments). */
+ char szCmd[GUESTPROCESS_MAX_CMD_LEN];
+ /** Process execution flags. @sa */
+ uint32_t uFlags;
+ /** Command line arguments. */
+ char szArgs[GUESTPROCESS_MAX_ARGS_LEN];
+ /** Number of arguments specified in pszArgs. */
+ uint32_t uNumArgs;
+ /** String of environment variables ("FOO=BAR") to pass to the process
+ * to start. */
+ char szEnv[GUESTPROCESS_MAX_ENV_LEN];
+ /** Size (in bytes) of environment variables block. */
+ uint32_t cbEnv;
+ /** Number of environment variables specified in pszEnv. */
+ uint32_t uNumEnvVars;
+ /** User name (account) to start the process under. */
+ char szUser[GUESTPROCESS_MAX_USER_LEN];
+ /** Password of specified user name (account). */
+ char szPassword[GUESTPROCESS_MAX_PASSWORD_LEN];
+ /** Domain to be used for authenticating the specified user name (account). */
+ char szDomain[GUESTPROCESS_MAX_DOMAIN_LEN];
+ /** Time limit (in ms) of the process' life time. */
+ uint32_t uTimeLimitMS;
+ /** Process priority. */
+ uint32_t uPriority;
+ /** Process affinity. At the moment we support
+ * up to 4 * 64 = 256 CPUs. */
+ uint64_t uAffinity[4];
+ /** Number of used process affinity blocks. */
+ uint32_t uNumAffinity;
+} VBOXSERVICECTRLPROCSTARTUPINFO;
+/** Pointer to a guest process block. */
+typedef VBOXSERVICECTRLPROCSTARTUPINFO *PVBOXSERVICECTRLPROCSTARTUPINFO;
+
+/**
+ * Structure for holding data for one (started) guest process.
+ */
+typedef struct VBOXSERVICECTRLPROCESS
+{
+ /** Node. */
+ RTLISTNODE Node;
+ /** Process handle. */
+ RTPROCESS hProcess;
+ /** Number of references using this struct. */
+ uint32_t cRefs;
+ /** The worker thread. */
+ RTTHREAD Thread;
+ /** The session this guest process
+ * is bound to. */
+ PVBOXSERVICECTRLSESSION pSession;
+ /** Shutdown indicator; will be set when the thread
+ * needs (or is asked) to shutdown. */
+ bool volatile fShutdown;
+ /** Whether the guest process thread was stopped or not. */
+ bool volatile fStopped;
+ /** Whether the guest process thread was started or not. */
+ bool fStarted;
+ /** Context ID. */
+ uint32_t uContextID;
+ /** Critical section for thread-safe use. */
+ RTCRITSECT CritSect;
+ /** Process startup information. */
+ VBOXSERVICECTRLPROCSTARTUPINFO
+ StartupInfo;
+ /** The process' PID assigned by the guest OS. */
+ uint32_t uPID;
+ /** The process' request queue to handle requests
+ * from the outside, e.g. the session. */
+ RTREQQUEUE hReqQueue;
+ /** Our pollset, used for accessing the process'
+ * std* pipes + the notification pipe. */
+ RTPOLLSET hPollSet;
+ /** StdIn pipe for addressing writes to the
+ * guest process' stdin.*/
+ RTPIPE hPipeStdInW;
+ /** StdOut pipe for addressing reads from
+ * guest process' stdout.*/
+ RTPIPE hPipeStdOutR;
+ /** StdOut pipe for addressing reads from
+ * guest process' stdout.*/
+ RTPIPE hPipeStdErrR;
+
+ /** The write end of the notification pipe that is used to poke the thread
+ * monitoring the process.
+ * This is NIL_RTPIPE for output pipes. */
+ RTPIPE hNotificationPipeW;
+ /** The other end of hNotificationPipeW, read by vgsvcGstCtrlProcessProcLoop(). */
+ RTPIPE hNotificationPipeR;
+} VBOXSERVICECTRLPROCESS;
+/** Pointer to thread data. */
+typedef VBOXSERVICECTRLPROCESS *PVBOXSERVICECTRLPROCESS;
+
+RT_C_DECLS_BEGIN
+
+extern RTLISTANCHOR g_lstControlSessionThreads;
+extern VBOXSERVICECTRLSESSION g_Session;
+extern uint32_t g_idControlSvcClient;
+extern bool g_fControlSupportsOptimizations;
+
+
+/** @name Guest session thread handling.
+ * @{ */
+extern int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo, PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread);
+extern int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pSession, uint32_t uFlags);
+extern int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags);
+extern int VGSvcGstCtrlSessionThreadTerminate(PVBOXSERVICECTRLSESSIONTHREAD pSession);
+extern RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv);
+/** @} */
+/** @name Per-session functions.
+ * @{ */
+extern PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID);
+extern int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession);
+extern int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession);
+extern int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t uFlags);
+extern int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx, void *pvScratchBuf, size_t cbScratchBuf, volatile bool *pfShutdown);
+extern int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess);
+extern int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess);
+extern int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pbAllowed);
+extern int VGSvcGstCtrlSessionReapProcesses(PVBOXSERVICECTRLSESSION pSession);
+/** @} */
+/** @name Per-guest process functions.
+ * @{ */
+extern int VGSvcGstCtrlProcessFree(PVBOXSERVICECTRLPROCESS pProcess);
+extern int VGSvcGstCtrlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, bool fPendingClose, void *pvBuf, uint32_t cbBuf);
+extern int VGSvcGstCtrlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags);
+extern int VGSvcGstCtrlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess);
+extern void VGSvcGstCtrlProcessRelease(PVBOXSERVICECTRLPROCESS pProcess);
+extern int VGSvcGstCtrlProcessStart(const PVBOXSERVICECTRLSESSION pSession, const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, uint32_t uContext);
+extern int VGSvcGstCtrlProcessStop(PVBOXSERVICECTRLPROCESS pProcess);
+extern int VGSvcGstCtrlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, RTMSINTERVAL msTimeout, int *pRc);
+/** @} */
+
+RT_C_DECLS_END
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceControl_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp
new file mode 100644
index 00000000..1705a174
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp
@@ -0,0 +1,2129 @@
+/* $Id: VBoxServiceControlProcess.cpp $ */
+/** @file
+ * VBoxServiceControlThread - Guest process handling.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/handle.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/HostServices/GuestControlSvc.h>
+
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceControl.h"
+#include "VBoxServiceToolBox.h"
+
+using namespace guestControl;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vgsvcGstCtrlProcessAssignPID(PVBOXSERVICECTRLPROCESS pThread, uint32_t uPID);
+static int vgsvcGstCtrlProcessLock(PVBOXSERVICECTRLPROCESS pProcess);
+static int vgsvcGstCtrlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph,
+ PRTPIPE phPipe);
+static int vgsvcGstCtrlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess);
+/* Request handlers. */
+static DECLCALLBACK(int) vgsvcGstCtrlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ bool fPendingClose, void *pvBuf, uint32_t cbBuf);
+static DECLCALLBACK(int) vgsvcGstCtrlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags);
+
+
+/**
+ * Initialies the passed in thread data structure with the parameters given.
+ *
+ * @return IPRT status code.
+ * @param pProcess Process to initialize.
+ * @param pSession Guest session the process is bound to.
+ * @param pStartupInfo Startup information.
+ * @param u32ContextID The context ID bound to this request / command.
+ */
+static int vgsvcGstCtrlProcessInit(PVBOXSERVICECTRLPROCESS pProcess,
+ const PVBOXSERVICECTRLSESSION pSession,
+ const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo,
+ uint32_t u32ContextID)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
+
+ /* General stuff. */
+ pProcess->hProcess = NIL_RTPROCESS;
+ pProcess->pSession = pSession;
+ pProcess->Node.pPrev = NULL;
+ pProcess->Node.pNext = NULL;
+
+ pProcess->fShutdown = false;
+ pProcess->fStarted = false;
+ pProcess->fStopped = false;
+
+ pProcess->uPID = 0; /* Don't have a PID yet. */
+ pProcess->cRefs = 0;
+ /*
+ * Use the initial context ID we got for starting
+ * the process to report back its status with the
+ * same context ID.
+ */
+ pProcess->uContextID = u32ContextID;
+ /*
+ * Note: pProcess->ClientID will be assigned when thread is started;
+ * every guest process has its own client ID to detect crashes on
+ * a per-guest-process level.
+ */
+
+ int rc = RTCritSectInit(&pProcess->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ pProcess->hPollSet = NIL_RTPOLLSET;
+ pProcess->hPipeStdInW = NIL_RTPIPE;
+ pProcess->hPipeStdOutR = NIL_RTPIPE;
+ pProcess->hPipeStdErrR = NIL_RTPIPE;
+ pProcess->hNotificationPipeW = NIL_RTPIPE;
+ pProcess->hNotificationPipeR = NIL_RTPIPE;
+
+ rc = RTReqQueueCreate(&pProcess->hReqQueue);
+ AssertReleaseRC(rc);
+
+ /* Copy over startup info. */
+ memcpy(&pProcess->StartupInfo, pStartupInfo, sizeof(VBOXSERVICECTRLPROCSTARTUPINFO));
+
+ /* Adjust timeout value. */
+ if ( pProcess->StartupInfo.uTimeLimitMS == UINT32_MAX
+ || pProcess->StartupInfo.uTimeLimitMS == 0)
+ pProcess->StartupInfo.uTimeLimitMS = RT_INDEFINITE_WAIT;
+
+ if (RT_FAILURE(rc)) /* Clean up on failure. */
+ VGSvcGstCtrlProcessFree(pProcess);
+ return rc;
+}
+
+
+/**
+ * Frees a guest process. On success, pProcess will be
+ * free'd and thus won't be available anymore.
+ *
+ * @return IPRT status code.
+ * @param pProcess Guest process to free.
+ */
+int VGSvcGstCtrlProcessFree(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ VGSvcVerbose(3, "[PID %RU32]: Freeing (cRefs=%RU32)...\n", pProcess->uPID, pProcess->cRefs);
+ Assert(pProcess->cRefs == 0);
+
+ /*
+ * Destroy other thread data.
+ */
+ if (RTCritSectIsInitialized(&pProcess->CritSect))
+ RTCritSectDelete(&pProcess->CritSect);
+
+ int rc = RTReqQueueDestroy(pProcess->hReqQueue);
+ AssertRC(rc);
+
+ /*
+ * Remove from list.
+ */
+ AssertPtr(pProcess->pSession);
+ rc = VGSvcGstCtrlSessionProcessRemove(pProcess->pSession, pProcess);
+ AssertRC(rc);
+
+ /*
+ * Destroy thread structure as final step.
+ */
+ RTMemFree(pProcess);
+ pProcess = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Signals a guest process thread that we want it to shut down in
+ * a gentle way.
+ *
+ * @return IPRT status code.
+ * @param pProcess Process to stop.
+ */
+int VGSvcGstCtrlProcessStop(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ VGSvcVerbose(3, "[PID %RU32]: Stopping ...\n", pProcess->uPID);
+
+ /* Do *not* set pThread->fShutdown or other stuff here!
+ * The guest thread loop will clean up itself. */
+
+ return VGSvcGstCtrlProcessHandleTerm(pProcess);
+}
+
+
+/**
+ * Releases a previously acquired guest process (decreases the refcount).
+ *
+ * @param pProcess Process to unlock.
+ */
+void VGSvcGstCtrlProcessRelease(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturnVoid(pProcess);
+
+ bool fShutdown = false;
+
+ int rc = RTCritSectEnter(&pProcess->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pProcess->cRefs);
+ pProcess->cRefs--;
+ fShutdown = pProcess->fStopped; /* Has the process' thread been stopped? */
+
+ rc = RTCritSectLeave(&pProcess->CritSect);
+ AssertRC(rc);
+ }
+
+ if (fShutdown)
+ VGSvcGstCtrlProcessFree(pProcess);
+}
+
+
+/**
+ * Wait for a guest process thread to shut down.
+ *
+ * @return IPRT status code.
+ * @param pProcess Process to wait shutting down for.
+ * @param msTimeout Timeout in ms to wait for shutdown.
+ * @param prc Where to store the thread's return code.
+ * Optional.
+ */
+int VGSvcGstCtrlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, RTMSINTERVAL msTimeout, int *prc)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(prc, VERR_INVALID_POINTER);
+
+ int rc = vgsvcGstCtrlProcessLock(pProcess);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(2, "[PID %RU32]: Waiting for shutdown (%RU32ms) ...\n", pProcess->uPID, msTimeout);
+
+ AssertMsgReturn(pProcess->fStarted,
+ ("Tried to wait on guest process=%p (PID %RU32) which has not been started yet\n",
+ pProcess, pProcess->uPID), VERR_INVALID_PARAMETER);
+
+ /* Guest process already has been stopped, no need to wait. */
+ if (!pProcess->fStopped)
+ {
+ /* Unlock process before waiting. */
+ rc = vgsvcGstCtrlProcessUnlock(pProcess);
+ AssertRC(rc);
+
+ /* Do the actual waiting. */
+ int rcThread;
+ Assert(pProcess->Thread != NIL_RTTHREAD);
+ rc = RTThreadWait(pProcess->Thread, msTimeout, &rcThread);
+ if (RT_SUCCESS(rc))
+ {
+ pProcess->Thread = NIL_RTTHREAD;
+ VGSvcVerbose(3, "[PID %RU32]: Thread shutdown complete, thread rc=%Rrc\n", pProcess->uPID, rcThread);
+ if (prc)
+ *prc = rcThread;
+ }
+ else
+ VGSvcError("[PID %RU32]: Waiting for shutting down thread returned error rc=%Rrc\n", pProcess->uPID, rc);
+ }
+ else
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Thread already shut down, no waiting needed\n", pProcess->uPID);
+
+ int rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
+ AssertRC(rc2);
+ }
+ }
+
+ VGSvcVerbose(3, "[PID %RU32]: Waiting resulted in rc=%Rrc\n", pProcess->uPID, rc);
+ return rc;
+}
+
+
+/**
+ * Closes the stdin pipe of a guest process.
+ *
+ * @return IPRT status code.
+ * @param pProcess The process which input pipe we close.
+ * @param phStdInW The standard input pipe handle.
+ */
+static int vgsvcGstCtrlProcessPollsetCloseInput(PVBOXSERVICECTRLPROCESS pProcess, PRTPIPE phStdInW)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
+
+ int rc = RTPollSetRemove(pProcess->hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
+ if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
+ AssertRC(rc);
+
+ if (*phStdInW != NIL_RTPIPE)
+ {
+ rc = RTPipeClose(*phStdInW);
+ AssertRC(rc);
+ *phStdInW = NIL_RTPIPE;
+ }
+
+ return rc;
+}
+
+
+#ifdef DEBUG
+/**
+ * Names a poll handle ID.
+ *
+ * @returns Pointer to read-only string.
+ * @param idPollHnd What to name.
+ */
+static const char *vgsvcGstCtrlProcessPollHandleToString(uint32_t idPollHnd)
+{
+ switch (idPollHnd)
+ {
+ case VBOXSERVICECTRLPIPEID_UNKNOWN:
+ return "unknown";
+ case VBOXSERVICECTRLPIPEID_STDIN:
+ return "stdin";
+ case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
+ return "stdin_writable";
+ case VBOXSERVICECTRLPIPEID_STDOUT:
+ return "stdout";
+ case VBOXSERVICECTRLPIPEID_STDERR:
+ return "stderr";
+ case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
+ return "ipc_notify";
+ default:
+ return "unknown";
+ }
+}
+#endif /* DEBUG */
+
+
+/**
+ * Handle an error event on standard input.
+ *
+ * @return IPRT status code.
+ * @param pProcess Process to handle pollset for.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phStdInW The standard input pipe handle.
+ */
+static int vgsvcGstCtrlProcessPollsetOnInput(PVBOXSERVICECTRLPROCESS pProcess, uint32_t fPollEvt, PRTPIPE phStdInW)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ NOREF(fPollEvt);
+
+ return vgsvcGstCtrlProcessPollsetCloseInput(pProcess, phStdInW);
+}
+
+
+/**
+ * Handle pending output data or error on standard out or standard error.
+ *
+ * @returns IPRT status code from client send.
+ * @param pProcess Process to handle pollset for.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phPipeR The pipe handle.
+ * @param idPollHnd The pipe ID to handle.
+ */
+static int vgsvcGstCtrlProcessHandleOutputError(PVBOXSERVICECTRLPROCESS pProcess,
+ uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd)
+{
+ RT_NOREF1(fPollEvt);
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ if (!phPipeR)
+ return VINF_SUCCESS;
+
+#ifdef DEBUG
+ VGSvcVerbose(4, "[PID %RU32]: Output error: idPollHnd=%s, fPollEvt=0x%x\n",
+ pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), fPollEvt);
+#endif
+
+ /* Remove pipe from poll set. */
+ int rc2 = RTPollSetRemove(pProcess->hPollSet, idPollHnd);
+ AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2));
+
+ bool fClosePipe = true; /* By default close the pipe. */
+
+ /* Check if there's remaining data to read from the pipe. */
+ if (*phPipeR != NIL_RTPIPE)
+ {
+ size_t cbReadable;
+ rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable);
+ if ( RT_SUCCESS(rc2)
+ && cbReadable)
+ {
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: idPollHnd=%s has %zu bytes left, vetoing close\n",
+ pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), cbReadable);
+#endif
+ /* Veto closing the pipe yet because there's still stuff to read
+ * from the pipe. This can happen on UNIX-y systems where on
+ * error/hangup there still can be data to be read out. */
+ fClosePipe = false;
+ }
+ }
+#ifdef DEBUG
+ else
+ VGSvcVerbose(3, "[PID %RU32]: idPollHnd=%s will be closed\n",
+ pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd));
+#endif
+
+ if ( *phPipeR != NIL_RTPIPE
+ && fClosePipe)
+ {
+ rc2 = RTPipeClose(*phPipeR);
+ AssertRC(rc2);
+ *phPipeR = NIL_RTPIPE;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Handle pending output data or error on standard out or standard error.
+ *
+ * @returns IPRT status code from client send.
+ * @param pProcess Process to handle pollset for.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phPipeR The pipe handle.
+ * @param idPollHnd The pipe ID to handle.
+ *
+ */
+static int vgsvcGstCtrlProcessPollsetOnOutput(PVBOXSERVICECTRLPROCESS pProcess,
+ uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+#ifdef DEBUG
+ VGSvcVerbose(4, "[PID %RU32]: Output event phPipeR=%p, idPollHnd=%s, fPollEvt=0x%x\n",
+ pProcess->uPID, phPipeR, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), fPollEvt);
+#endif
+
+ if (!phPipeR)
+ return VINF_SUCCESS;
+
+ int rc = VINF_SUCCESS;
+
+#ifdef DEBUG
+ if (*phPipeR != NIL_RTPIPE)
+ {
+ size_t cbReadable;
+ rc = RTPipeQueryReadable(*phPipeR, &cbReadable);
+ if ( RT_SUCCESS(rc)
+ && cbReadable)
+ {
+ VGSvcVerbose(4, "[PID %RU32]: Output event cbReadable=%zu\n", pProcess->uPID, cbReadable);
+ }
+ }
+#endif
+
+#if 0
+ /* Push output to the host. */
+ if (fPollEvt & RTPOLL_EVT_READ)
+ {
+ size_t cbRead = 0;
+ uint8_t byData[_64K];
+ rc = RTPipeRead(*phPipeR, byData, sizeof(byData), &cbRead);
+ VGSvcVerbose(4, "VGSvcGstCtrlProcessHandleOutputEvent cbRead=%u, rc=%Rrc\n", cbRead, rc);
+
+ /* Make sure we go another poll round in case there was too much data
+ for the buffer to hold. */
+ fPollEvt &= RTPOLL_EVT_ERROR;
+ }
+#endif
+
+ if (fPollEvt & RTPOLL_EVT_ERROR)
+ rc = vgsvcGstCtrlProcessHandleOutputError(pProcess, fPollEvt, phPipeR, idPollHnd);
+ return rc;
+}
+
+
+/**
+ * Execution loop which runs in a dedicated per-started-process thread and
+ * handles all pipe input/output and signalling stuff.
+ *
+ * @return IPRT status code.
+ * @param pProcess The guest process to handle.
+ */
+static int vgsvcGstCtrlProcessProcLoop(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ int rc;
+ int rc2;
+ uint64_t const uMsStart = RTTimeMilliTS();
+ RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
+ bool fProcessAlive = true;
+ bool fProcessTimedOut = false;
+ uint64_t MsProcessKilled = UINT64_MAX;
+ RTMSINTERVAL const cMsPollBase = pProcess->hPipeStdInW != NIL_RTPIPE
+ ? 100 /* Need to poll for input. */
+ : 1000; /* Need only poll for process exit and aborts. */
+ RTMSINTERVAL cMsPollCur = 0;
+
+ /*
+ * Assign PID to thread data.
+ * Also check if there already was a thread with the same PID and shut it down -- otherwise
+ * the first (stale) entry will be found and we get really weird results!
+ */
+ rc = vgsvcGstCtrlProcessAssignPID(pProcess, pProcess->hProcess /* Opaque PID handle */);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("Unable to assign PID=%u, to new thread, rc=%Rrc\n", pProcess->hProcess, rc);
+ return rc;
+ }
+
+ /*
+ * Before entering the loop, tell the host that we've started the guest
+ * and that it's now OK to send input to the process.
+ */
+ VGSvcVerbose(2, "[PID %RU32]: Process '%s' started, CID=%u, User=%s, cMsTimeout=%RU32\n",
+ pProcess->uPID, pProcess->StartupInfo.szCmd, pProcess->uContextID,
+ pProcess->StartupInfo.szUser, pProcess->StartupInfo.uTimeLimitMS);
+ VBGLR3GUESTCTRLCMDCTX ctxStart = { g_idControlSvcClient, pProcess->uContextID };
+ rc = VbglR3GuestCtrlProcCbStatus(&ctxStart,
+ pProcess->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
+ NULL /* pvData */, 0 /* cbData */);
+ if (rc == VERR_INTERRUPTED)
+ rc = VINF_SUCCESS; /* SIGCHLD send by quick childs! */
+ if (RT_FAILURE(rc))
+ VGSvcError("[PID %RU32]: Error reporting starting status to host, rc=%Rrc\n", pProcess->uPID, rc);
+
+ /*
+ * Process input, output, the test pipe and client requests.
+ */
+ while ( RT_SUCCESS(rc)
+ && RT_UNLIKELY(!pProcess->fShutdown))
+ {
+ /*
+ * Wait/Process all pending events.
+ */
+ uint32_t idPollHnd;
+ uint32_t fPollEvt;
+ rc2 = RTPollNoResume(pProcess->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
+ if (pProcess->fShutdown)
+ continue;
+
+ cMsPollCur = 0; /* No rest until we've checked everything. */
+
+ if (RT_SUCCESS(rc2))
+ {
+ switch (idPollHnd)
+ {
+ case VBOXSERVICECTRLPIPEID_STDIN:
+ rc = vgsvcGstCtrlProcessPollsetOnInput(pProcess, fPollEvt, &pProcess->hPipeStdInW);
+ break;
+
+ case VBOXSERVICECTRLPIPEID_STDOUT:
+ rc = vgsvcGstCtrlProcessPollsetOnOutput(pProcess, fPollEvt, &pProcess->hPipeStdOutR, idPollHnd);
+ break;
+
+ case VBOXSERVICECTRLPIPEID_STDERR:
+ rc = vgsvcGstCtrlProcessPollsetOnOutput(pProcess, fPollEvt, &pProcess->hPipeStdOutR, idPollHnd);
+ break;
+
+ case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
+#ifdef DEBUG_andy
+ VGSvcVerbose(4, "[PID %RU32]: IPC notify\n", pProcess->uPID);
+#endif
+ rc2 = vgsvcGstCtrlProcessLock(pProcess);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Drain the notification pipe. */
+ uint8_t abBuf[8];
+ size_t cbIgnore;
+ rc2 = RTPipeRead(pProcess->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Draining IPC notification pipe failed with rc=%Rrc\n", rc2);
+
+ /* Process all pending requests. */
+ VGSvcVerbose(4, "[PID %RU32]: Processing pending requests ...\n", pProcess->uPID);
+ Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
+ rc2 = RTReqQueueProcess(pProcess->hReqQueue,
+ 0 /* Only process all pending requests, don't wait for new ones */);
+ if ( RT_FAILURE(rc2)
+ && rc2 != VERR_TIMEOUT)
+ VGSvcError("Processing requests failed with with rc=%Rrc\n", rc2);
+
+ int rc3 = vgsvcGstCtrlProcessUnlock(pProcess);
+ AssertRC(rc3);
+#ifdef DEBUG
+ VGSvcVerbose(4, "[PID %RU32]: Processing pending requests done, rc=%Rrc\n", pProcess->uPID, rc2);
+#endif
+ }
+
+ break;
+
+ default:
+ AssertMsgFailed(("Unknown idPollHnd=%RU32\n", idPollHnd));
+ break;
+ }
+
+ if (RT_FAILURE(rc) || rc == VINF_EOF)
+ break; /* Abort command, or client dead or something. */
+ }
+#if 0
+ VGSvcVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%RU32, idPollHnd=%s, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n",
+ pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), vgsvcGstCtrlProcessPollHandleToString(idPollHnd), rc, fProcessAlive, pProcess->fShutdown);
+ VGSvcVerbose(4, "[PID %RU32]: stdOut=%s, stdErrR=%s\n",
+ pProcess->uPID,
+ *phStdOutR == NIL_RTPIPE ? "closed" : "open",
+ *phStdErrR == NIL_RTPIPE ? "closed" : "open");
+#endif
+ if (RT_UNLIKELY(pProcess->fShutdown))
+ break; /* We were asked to shutdown. */
+
+ /*
+ * Check for process death.
+ */
+ if (fProcessAlive)
+ {
+ rc2 = RTProcWaitNoResume(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+ if (RT_SUCCESS_NP(rc2))
+ {
+ fProcessAlive = false;
+ /* Note: Don't bail out here yet. First check in the next block below
+ * if all needed pipe outputs have been consumed. */
+ }
+ else
+ {
+ if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
+ continue;
+ if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
+ {
+ fProcessAlive = false;
+ ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
+ ProcessStatus.iStatus = 255;
+ AssertFailed();
+ }
+ else
+ AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
+ }
+ }
+
+ /*
+ * If the process has terminated and all output has been consumed,
+ * we should be heading out.
+ */
+ if (!fProcessAlive)
+ {
+ if ( fProcessTimedOut
+ || ( pProcess->hPipeStdOutR == NIL_RTPIPE
+ && pProcess->hPipeStdErrR == NIL_RTPIPE)
+ )
+ {
+ VGSvcVerbose(3, "[PID %RU32]: RTProcWaitNoResume=%Rrc\n", pProcess->uPID, rc2);
+ break;
+ }
+ }
+
+ /*
+ * Check for timed out, killing the process.
+ */
+ uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
+ if ( pProcess->StartupInfo.uTimeLimitMS != RT_INDEFINITE_WAIT
+ && pProcess->StartupInfo.uTimeLimitMS != 0)
+ {
+ uint64_t u64Now = RTTimeMilliTS();
+ uint64_t cMsElapsed = u64Now - uMsStart;
+ if (cMsElapsed >= pProcess->StartupInfo.uTimeLimitMS)
+ {
+ fProcessTimedOut = true;
+ if ( MsProcessKilled == UINT64_MAX
+ || u64Now - MsProcessKilled > 1000)
+ {
+ if (u64Now - MsProcessKilled > 20*60*1000)
+ break; /* Give up after 20 mins. */
+
+ VGSvcVerbose(3, "[PID %RU32]: Timed out (%RU64ms elapsed > %RU32ms timeout), killing ...\n",
+ pProcess->uPID, cMsElapsed, pProcess->StartupInfo.uTimeLimitMS);
+
+ rc2 = RTProcTerminate(pProcess->hProcess);
+ VGSvcVerbose(3, "[PID %RU32]: Killing process resulted in rc=%Rrc\n",
+ pProcess->uPID, rc2);
+ MsProcessKilled = u64Now;
+ continue;
+ }
+ cMilliesLeft = 10000;
+ }
+ else
+ cMilliesLeft = pProcess->StartupInfo.uTimeLimitMS - (uint32_t)cMsElapsed;
+ }
+
+ /* Reset the polling interval since we've done all pending work. */
+ cMsPollCur = fProcessAlive
+ ? cMsPollBase
+ : RT_MS_1MIN;
+ if (cMilliesLeft < cMsPollCur)
+ cMsPollCur = cMilliesLeft;
+ }
+
+ VGSvcVerbose(3, "[PID %RU32]: Loop ended: rc=%Rrc, fShutdown=%RTbool, fProcessAlive=%RTbool, fProcessTimedOut=%RTbool, MsProcessKilled=%RU64 (%RX64)\n",
+ pProcess->uPID, rc, pProcess->fShutdown, fProcessAlive, fProcessTimedOut, MsProcessKilled, MsProcessKilled);
+ VGSvcVerbose(3, "[PID %RU32]: *phStdOutR=%s, *phStdErrR=%s\n",
+ pProcess->uPID,
+ pProcess->hPipeStdOutR == NIL_RTPIPE ? "closed" : "open",
+ pProcess->hPipeStdErrR == NIL_RTPIPE ? "closed" : "open");
+
+ /* Signal that this thread is in progress of shutting down. */
+ ASMAtomicWriteBool(&pProcess->fShutdown, true);
+
+ /*
+ * Try killing the process if it's still alive at this point.
+ */
+ if (fProcessAlive)
+ {
+ if (MsProcessKilled == UINT64_MAX)
+ {
+ VGSvcVerbose(2, "[PID %RU32]: Is still alive and not killed yet\n", pProcess->uPID);
+
+ MsProcessKilled = RTTimeMilliTS();
+ rc2 = RTProcTerminate(pProcess->hProcess);
+ if (rc2 == VERR_NOT_FOUND)
+ {
+ fProcessAlive = false;
+ }
+ else if (RT_FAILURE(rc2))
+ VGSvcError("[PID %RU32]: Killing process failed with rc=%Rrc\n", pProcess->uPID, rc2);
+ RTThreadSleep(500);
+ }
+
+ for (int i = 0; i < 10 && fProcessAlive; i++)
+ {
+ VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n", pProcess->uPID, i + 1);
+ rc2 = RTProcWait(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+ if (RT_SUCCESS(rc2))
+ {
+ VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n", pProcess->uPID, i + 1);
+ fProcessAlive = false;
+ break;
+ }
+ if (i >= 5)
+ {
+ VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n", pProcess->uPID, i + 1);
+ rc2 = RTProcTerminate(pProcess->hProcess);
+ if ( RT_FAILURE(rc)
+ && rc2 != VERR_NOT_FOUND)
+ VGSvcError("PID %RU32]: Killing process failed with rc=%Rrc\n",
+ pProcess->uPID, rc2);
+ }
+ RTThreadSleep(i >= 5 ? 2000 : 500);
+ }
+
+ if (fProcessAlive)
+ VGSvcError("[PID %RU32]: Could not be killed\n", pProcess->uPID);
+ }
+
+ /*
+ * Shutdown procedure:
+ * - Set the pProcess->fShutdown indicator to let others know we're
+ * not accepting any new requests anymore.
+ * - After setting the indicator, try to process all outstanding
+ * requests to make sure they're getting delivered.
+ *
+ * Note: After removing the process from the session's list it's not
+ * even possible for the session anymore to control what's
+ * happening to this thread, so be careful and don't mess it up.
+ */
+
+ rc2 = vgsvcGstCtrlProcessLock(pProcess);
+ if (RT_SUCCESS(rc2))
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Processing outstanding requests ...\n", pProcess->uPID);
+
+ /* Process all pending requests (but don't wait for new ones). */
+ Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
+ rc2 = RTReqQueueProcess(pProcess->hReqQueue, 0 /* No timeout */);
+ if ( RT_FAILURE(rc2)
+ && rc2 != VERR_TIMEOUT)
+ VGSvcError("[PID %RU32]: Processing outstanding requests failed with with rc=%Rrc\n", pProcess->uPID, rc2);
+
+ VGSvcVerbose(3, "[PID %RU32]: Processing outstanding requests done, rc=%Rrc\n", pProcess->uPID, rc2);
+
+ rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
+ AssertRC(rc2);
+ }
+
+ /*
+ * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
+ * clients exec packet now.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uStatus = PROC_STS_UNDEFINED;
+ uint32_t fFlags = 0;
+
+ if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Timed out and got killed\n", pProcess->uPID);
+ uStatus = PROC_STS_TOK;
+ }
+ else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n", pProcess->uPID);
+ uStatus = PROC_STS_TOA;
+ }
+ else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n", pProcess->uPID);
+ uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
+ fFlags = pProcess->StartupInfo.uFlags; /* Return handed-in execution flags back to the host. */
+ }
+ else if (fProcessAlive)
+ VGSvcError("[PID %RU32]: Is alive when it should not!\n", pProcess->uPID);
+ else if (MsProcessKilled != UINT64_MAX)
+ VGSvcError("[PID %RU32]: Has been killed when it should not!\n", pProcess->uPID);
+ else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %d)\n",
+ pProcess->uPID, ProcessStatus.iStatus);
+ uStatus = PROC_STS_TEN;
+ fFlags = ProcessStatus.iStatus;
+ }
+ else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
+ {
+ VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
+ pProcess->uPID, ProcessStatus.iStatus);
+ uStatus = PROC_STS_TES;
+ fFlags = ProcessStatus.iStatus;
+ }
+ else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
+ {
+ /* ProcessStatus.iStatus will be undefined. */
+ VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n", pProcess->uPID);
+ uStatus = PROC_STS_TEA;
+ fFlags = ProcessStatus.iStatus;
+ }
+ else
+ VGSvcVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n", pProcess->uPID, ProcessStatus.enmReason);
+ VBGLR3GUESTCTRLCMDCTX ctxEnd = { g_idControlSvcClient, pProcess->uContextID };
+ VGSvcVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
+ pProcess->uPID, ctxEnd.uClientID, pProcess->uContextID, uStatus, fFlags);
+
+ rc2 = VbglR3GuestCtrlProcCbStatus(&ctxEnd, pProcess->uPID, uStatus, fFlags, NULL /* pvData */, 0 /* cbData */);
+ if ( RT_FAILURE(rc2)
+ && rc2 == VERR_NOT_FOUND)
+ VGSvcError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n", pProcess->uPID, rc2);
+ }
+
+ VGSvcVerbose(3, "[PID %RU32]: Process loop returned with rc=%Rrc\n", pProcess->uPID, rc);
+ return rc;
+}
+
+
+#if 0 /* unused */
+/**
+ * Initializes a pipe's handle and pipe object.
+ *
+ * @return IPRT status code.
+ * @param ph The pipe's handle to initialize.
+ * @param phPipe The pipe's object to initialize.
+ */
+static int vgsvcGstCtrlProcessInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
+{
+ AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
+
+ ph->enmType = RTHANDLETYPE_PIPE;
+ ph->u.hPipe = NIL_RTPIPE;
+ *phPipe = NIL_RTPIPE;
+
+ return VINF_SUCCESS;
+}
+#endif
+
+
+/**
+ * Sets up the redirection / pipe / nothing for one of the standard handles.
+ *
+ * @returns IPRT status code. No client replies made.
+ * @param pszHowTo How to set up this standard handle.
+ * @param fd Which standard handle it is (0 == stdin, 1 ==
+ * stdout, 2 == stderr).
+ * @param ph The generic handle that @a pph may be set
+ * pointing to. Always set.
+ * @param pph Pointer to the RTProcCreateExec argument.
+ * Always set.
+ * @param phPipe Where to return the end of the pipe that we
+ * should service.
+ */
+static int vgsvcGstCtrlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
+{
+ AssertPtrReturn(ph, VERR_INVALID_POINTER);
+ AssertPtrReturn(pph, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+
+ int rc;
+
+ ph->enmType = RTHANDLETYPE_PIPE;
+ ph->u.hPipe = NIL_RTPIPE;
+ *pph = NULL;
+ *phPipe = NIL_RTPIPE;
+
+ if (!strcmp(pszHowTo, "|"))
+ {
+ /*
+ * Setup a pipe for forwarding to/from the client.
+ * The ph union struct will be filled with a pipe read/write handle
+ * to represent the "other" end to phPipe.
+ */
+ if (fd == 0) /* stdin? */
+ {
+ /* Connect a wrtie pipe specified by phPipe to stdin. */
+ rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
+ }
+ else /* stdout or stderr. */
+ {
+ /* Connect a read pipe specified by phPipe to stdout or stderr. */
+ rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
+ }
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+ ph->enmType = RTHANDLETYPE_PIPE;
+ *pph = ph;
+ }
+ else if (!strcmp(pszHowTo, "/dev/null"))
+ {
+ /*
+ * Redirect to/from /dev/null.
+ */
+ RTFILE hFile;
+ rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ ph->enmType = RTHANDLETYPE_FILE;
+ ph->u.hFile = hFile;
+ *pph = ph;
+ }
+ else /* Add other piping stuff here. */
+ rc = VINF_SUCCESS; /* Same as parent (us). */
+
+ return rc;
+}
+
+
+/**
+ * Expands a file name / path to its real content. This only works on Windows
+ * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
+ * with system / administrative rights).
+ *
+ * @return IPRT status code.
+ * @param pszPath Path to resolve.
+ * @param pszExpanded Pointer to string to store the resolved path in.
+ * @param cbExpanded Size (in bytes) of string to store the resolved path.
+ */
+static int vgsvcGstCtrlProcessMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
+{
+ int rc = VINF_SUCCESS;
+/** @todo r=bird: This feature shall be made optional, i.e. require a
+ * flag to be passed down. Further, it shall work on the environment
+ * block of the new process (i.e. include env changes passed down from
+ * the caller). I would also suggest using the unix variable expansion
+ * syntax, not the DOS one.
+ *
+ * Since this currently not available on non-windows guests, I suggest
+ * we disable it until such a time as it is implemented correctly. */
+#ifdef RT_OS_WINDOWS
+ if (!ExpandEnvironmentStrings(pszPath, pszExpanded, (DWORD)cbExpanded))
+ rc = RTErrConvertFromWin32(GetLastError());
+#else
+ /* No expansion for non-Windows yet. */
+ rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
+#endif
+#ifdef DEBUG
+ VGSvcVerbose(3, "vgsvcGstCtrlProcessMakeFullPath: %s -> %s\n", pszPath, pszExpanded);
+#endif
+ return rc;
+}
+
+
+/**
+ * Resolves the full path of a specified executable name. This function also
+ * resolves internal VBoxService tools to its appropriate executable path + name if
+ * VBOXSERVICE_NAME is specified as pszFileName.
+ *
+ * @return IPRT status code.
+ * @param pszFileName File name to resolve.
+ * @param pszResolved Pointer to a string where the resolved file name will be stored.
+ * @param cbResolved Size (in bytes) of resolved file name string.
+ */
+static int vgsvcGstCtrlProcessResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
+{
+ AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszResolved, VERR_INVALID_POINTER);
+ AssertReturn(cbResolved, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+
+ char szPathToResolve[RTPATH_MAX];
+ if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0))
+ || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
+ {
+ /* Resolve executable name of this process. */
+ if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve)))
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ {
+ /* Take the raw argument to resolve. */
+ rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vgsvcGstCtrlProcessMakeFullPath(szPathToResolve, pszResolved, cbResolved);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "Looked up executable: %s -> %s\n", pszFileName, pszResolved);
+ }
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Failed to lookup executable '%s' with rc=%Rrc\n", pszFileName, rc);
+ return rc;
+}
+
+
+/**
+ * Constructs the argv command line by resolving environment variables
+ * and relative paths.
+ *
+ * @return IPRT status code.
+ * @param pszArgv0 First argument (argv0), either original or modified version. Optional.
+ * @param papszArgs Original argv command line from the host, starting at argv[1].
+ * @param fFlags The process creation flags pass to us from the host.
+ * @param ppapszArgv Pointer to a pointer with the new argv command line.
+ * Needs to be freed with RTGetOptArgvFree.
+ */
+static int vgsvcGstCtrlProcessAllocateArgv(const char *pszArgv0, const char * const *papszArgs, uint32_t fFlags,
+ char ***ppapszArgv)
+{
+ AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER);
+
+ VGSvcVerbose(3, "VGSvcGstCtrlProcessPrepareArgv: pszArgv0=%p, papszArgs=%p, fFlags=%#x, ppapszArgv=%p\n",
+ pszArgv0, papszArgs, fFlags, ppapszArgv);
+
+ int rc = VINF_SUCCESS;
+ uint32_t cArgs;
+ for (cArgs = 0; papszArgs[cArgs]; cArgs++)
+ {
+ if (cArgs >= UINT32_MAX - 2)
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /* Allocate new argv vector (adding + 2 for argv0 + termination). */
+ size_t cbSize = (cArgs + 2) * sizeof(char *);
+ char **papszNewArgv = (char **)RTMemAlloc(cbSize);
+ if (!papszNewArgv)
+ return VERR_NO_MEMORY;
+
+#ifdef DEBUG
+ VGSvcVerbose(3, "VGSvcGstCtrlProcessAllocateArgv: cbSize=%RU32, cArgs=%RU32\n", cbSize, cArgs);
+#endif
+
+ /* HACK ALERT! Since we still don't allow the user to really specify the first
+ argument separately from the executable image, we have to fudge
+ a little in the unquoted argument case to deal with executables
+ containing spaces. */
+ /** @todo Fix the stupid host/guest protocol so the user can do this for us! */
+ if ( !(fFlags & EXECUTEPROCESSFLAG_UNQUOTED_ARGS)
+ || !strpbrk(pszArgv0, " \t\n\r")
+ || pszArgv0[0] == '"')
+ rc = RTStrDupEx(&papszNewArgv[0], pszArgv0);
+ else
+ {
+ size_t cchArgv0 = strlen(pszArgv0);
+ rc = RTStrAllocEx(&papszNewArgv[0], 1 + cchArgv0 + 1 + 1);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszDst = papszNewArgv[0];
+ *pszDst++ = '"';
+ memcpy(pszDst, pszArgv0, cchArgv0);
+ pszDst += cchArgv0;
+ *pszDst++ = '"';
+ *pszDst = '\0';
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ size_t i;
+ for (i = 0; i < cArgs; i++)
+ {
+ char *pszArg;
+#if 0 /* Arguments expansion -- untested. */
+ if (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS)
+ {
+/** @todo r=bird: If you want this, we need a generic implementation, preferably in RTEnv or somewhere like that. The marking
+ * up of the variables must be the same on all platforms. */
+ /* According to MSDN the limit on older Windows version is 32K, whereas
+ * Vista+ there are no limits anymore. We still stick to 4K. */
+ char szExpanded[_4K];
+# ifdef RT_OS_WINDOWS
+ if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded)))
+ rc = RTErrConvertFromWin32(GetLastError());
+# else
+ /* No expansion for non-Windows yet. */
+ rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded);
+# endif
+ if (RT_SUCCESS(rc))
+ rc = RTStrDupEx(&pszArg, szExpanded);
+ }
+ else
+#endif
+ rc = RTStrDupEx(&pszArg, papszArgs[i]);
+
+ if (RT_FAILURE(rc))
+ break;
+
+ papszNewArgv[i + 1] = pszArg;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Terminate array. */
+ papszNewArgv[cArgs + 1] = NULL;
+
+ *ppapszArgv = papszNewArgv;
+ return VINF_SUCCESS;
+ }
+
+ /* Failed, bail out. */
+ for (; i > 0; i--)
+ RTStrFree(papszNewArgv[i]);
+ }
+ RTMemFree(papszNewArgv);
+ return rc;
+}
+
+
+/**
+ * Assigns a valid PID to a guest control thread and also checks if there already was
+ * another (stale) guest process which was using that PID before and destroys it.
+ *
+ * @return IPRT status code.
+ * @param pProcess Process to assign PID to.
+ * @param uPID PID to assign to the specified guest control execution thread.
+ */
+static int vgsvcGstCtrlProcessAssignPID(PVBOXSERVICECTRLPROCESS pProcess, uint32_t uPID)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ AssertReturn(uPID, VERR_INVALID_PARAMETER);
+
+ AssertPtr(pProcess->pSession);
+ int rc = RTCritSectEnter(&pProcess->pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Search old threads using the desired PID and shut them down completely -- it's
+ * not used anymore. */
+ bool fTryAgain;
+ do
+ {
+ fTryAgain = false;
+ PVBOXSERVICECTRLPROCESS pProcessCur;
+ RTListForEach(&pProcess->pSession->lstProcesses, pProcessCur, VBOXSERVICECTRLPROCESS, Node)
+ {
+ if (pProcessCur->uPID == uPID)
+ {
+ Assert(pProcessCur != pProcess); /* can't happen */
+ uint32_t uTriedPID = uPID;
+ uPID += 391939;
+ VGSvcVerbose(2, "PID %RU32 was used before (process %p), trying again with %RU32 ...\n",
+ uTriedPID, pProcessCur, uPID);
+ fTryAgain = true;
+ break;
+ }
+ }
+ } while (fTryAgain);
+
+ /* Assign PID to current thread. */
+ pProcess->uPID = uPID;
+
+ rc = RTCritSectLeave(&pProcess->pSession->CritSect);
+ AssertRC(rc);
+ }
+
+ return rc;
+}
+
+
+static void vgsvcGstCtrlProcessFreeArgv(char **papszArgv)
+{
+ if (papszArgv)
+ {
+ size_t i = 0;
+ while (papszArgv[i])
+ RTStrFree(papszArgv[i++]);
+ RTMemFree(papszArgv);
+ }
+}
+
+
+/**
+ * Helper function to create/start a process on the guest.
+ *
+ * @return IPRT status code.
+ * @param pszExec Full qualified path of process to start (without arguments).
+ * @param papszArgs Pointer to array of command line arguments.
+ * @param hEnv Handle to environment block to use.
+ * @param fFlags Process execution flags.
+ * @param phStdIn Handle for the process' stdin pipe.
+ * @param phStdOut Handle for the process' stdout pipe.
+ * @param phStdErr Handle for the process' stderr pipe.
+ * @param pszAsUser User name (account) to start the process under.
+ * @param pszPassword Password of the specified user.
+ * @param pszDomain Domain to use for authentication.
+ * @param phProcess Pointer which will receive the process handle after
+ * successful process start.
+ */
+static int vgsvcGstCtrlProcessCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
+ PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr,
+ const char *pszAsUser, const char *pszPassword, const char *pszDomain,
+ PRTPROCESS phProcess)
+{
+#ifndef RT_OS_WINDOWS
+ RT_NOREF1(pszDomain);
+#endif
+ AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
+ /* phStdIn is optional. */
+ /* phStdOut is optional. */
+ /* phStdErr is optional. */
+ /* pszPassword is optional. */
+ /* pszDomain is optional. */
+ AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ char szExecExp[RTPATH_MAX];
+
+#ifdef DEBUG
+ /* Never log this in release mode! */
+ VGSvcVerbose(4, "pszUser=%s, pszPassword=%s, pszDomain=%s\n", pszAsUser, pszPassword, pszDomain);
+#endif
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * If sysprep should be executed do this in the context of VBoxService, which
+ * (usually, if started by SCM) has administrator rights. Because of that a UI
+ * won't be shown (doesn't have a desktop).
+ */
+ if (!RTStrICmp(pszExec, "sysprep"))
+ {
+ /* Use a predefined sysprep path as default. */
+ char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
+ /** @todo Check digital signature of file above before executing it? */
+
+ /*
+ * On Windows Vista (and up) sysprep is located in "system32\\Sysprep\\sysprep.exe",
+ * so detect the OS and use a different path.
+ */
+ OSVERSIONINFOEX OSInfoEx;
+ RT_ZERO(OSInfoEx);
+ OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ BOOL fRet = GetVersionEx((LPOSVERSIONINFO) &OSInfoEx);
+ if ( fRet
+ && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
+ {
+ rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
+#ifndef RT_ARCH_AMD64
+ /* Don't execute 64-bit sysprep from a 32-bit service host! */
+ char szSysWow64[RTPATH_MAX];
+ if (RTStrPrintf(szSysWow64, sizeof(szSysWow64), "%s", szSysprepCmd))
+ {
+ rc = RTPathAppend(szSysWow64, sizeof(szSysWow64), "SysWow64");
+ AssertRC(rc);
+ }
+ if ( RT_SUCCESS(rc)
+ && RTPathExists(szSysWow64))
+ VGSvcVerbose(0, "Warning: This service is 32-bit; could not execute sysprep on 64-bit OS!\n");
+#endif
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\Sysprep\\sysprep.exe");
+ if (RT_SUCCESS(rc))
+ RTPathChangeToDosSlashes(szSysprepCmd, false /* No forcing necessary */);
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Failed to detect sysrep location, rc=%Rrc\n", rc);
+ }
+ else if (!fRet)
+ VGSvcError("Failed to retrieve OS information, last error=%ld\n", GetLastError());
+
+ VGSvcVerbose(3, "Sysprep executable is: %s\n", szSysprepCmd);
+
+ if (RT_SUCCESS(rc))
+ {
+ char **papszArgsExp;
+ rc = vgsvcGstCtrlProcessAllocateArgv(szSysprepCmd /* argv0 */, papszArgs, fFlags, &papszArgsExp);
+ if (RT_SUCCESS(rc))
+ {
+ /* As we don't specify credentials for the sysprep process, it will
+ * run under behalf of the account VBoxService was started under, most
+ * likely local system. */
+ rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
+ phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
+ NULL /* pszPassword */, phProcess);
+ vgsvcGstCtrlProcessFreeArgv(papszArgsExp);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ VGSvcVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc);
+
+ return rc;
+ }
+#endif /* RT_OS_WINDOWS */
+
+#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
+ if (RTStrStr(pszExec, "vbox_") == pszExec)
+ {
+ /* We want to use the internal toolbox (all internal
+ * tools are starting with "vbox_" (e.g. "vbox_cat"). */
+ rc = vgsvcGstCtrlProcessResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
+ }
+ else
+ {
+#endif
+ /*
+ * Do the environment variables expansion on executable and arguments.
+ */
+ rc = vgsvcGstCtrlProcessResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
+#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
+ }
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ char **papszArgsExp;
+ /** @todo r-bird: pszExec != argv[0]! When are you going to get that?!? How many
+ * times does this need to be pointed out? HOST/GUEST INTERFACE IS MISDESIGNED! */
+ rc = vgsvcGstCtrlProcessAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */,
+ papszArgs /* Append the rest of the argument vector (if any). */,
+ fFlags, &papszArgsExp);
+ if (RT_FAILURE(rc))
+ {
+ /* Don't print any arguments -- may contain passwords or other sensible data! */
+ VGSvcError("Could not prepare arguments, rc=%Rrc\n", rc);
+ }
+ else
+ {
+ uint32_t uProcFlags = 0;
+ if (fFlags)
+ {
+ if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
+ uProcFlags |= RTPROC_FLAGS_HIDDEN;
+ if (fFlags & EXECUTEPROCESSFLAG_PROFILE)
+ uProcFlags |= RTPROC_FLAGS_PROFILE;
+ if (fFlags & EXECUTEPROCESSFLAG_UNQUOTED_ARGS)
+ uProcFlags |= RTPROC_FLAGS_UNQUOTED_ARGS;
+ }
+
+ /* If no user name specified run with current credentials (e.g.
+ * full service/system rights). This is prohibited via official Main API!
+ *
+ * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
+ * code (at least on Windows) for running processes as different users
+ * started from our system service. */
+ if (pszAsUser && *pszAsUser)
+ uProcFlags |= RTPROC_FLAGS_SERVICE;
+#ifdef DEBUG
+ VGSvcVerbose(3, "Command: %s\n", szExecExp);
+ for (size_t i = 0; papszArgsExp[i]; i++)
+ VGSvcVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]);
+#endif
+ VGSvcVerbose(3, "Starting process '%s' ...\n", szExecExp);
+
+ const char *pszUser = pszAsUser;
+#ifdef RT_OS_WINDOWS
+ /* If a domain name is given, construct an UPN (User Principle Name) with
+ * the domain name built-in, e.g. "joedoe@example.com". */
+ char *pszUserUPN = NULL;
+ if ( pszDomain
+ && strlen(pszDomain))
+ {
+ int cbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s", pszAsUser, pszDomain);
+ if (cbUserUPN > 0)
+ {
+ pszUser = pszUserUPN;
+ VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
+ }
+ }
+#endif
+
+ /* Do normal execution. */
+ rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
+ phStdIn, phStdOut, phStdErr,
+ pszUser,
+ pszPassword && *pszPassword ? pszPassword : NULL,
+ phProcess);
+#ifdef RT_OS_WINDOWS
+ if (pszUserUPN)
+ RTStrFree(pszUserUPN);
+#endif
+ VGSvcVerbose(3, "Starting process '%s' returned rc=%Rrc\n", szExecExp, rc);
+
+ vgsvcGstCtrlProcessFreeArgv(papszArgsExp);
+ }
+ }
+ return rc;
+}
+
+
+#ifdef DEBUG
+static int vgsvcGstCtrlProcessDumpToFile(const char *pszFileName, void *pvBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+
+ if (!cbBuf)
+ return VINF_SUCCESS;
+
+ char szFile[RTPATH_MAX];
+
+ int rc = RTPathTemp(szFile, sizeof(szFile));
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szFile, sizeof(szFile), pszFileName);
+
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "Dumping %ld bytes to '%s'\n", cbBuf, szFile);
+
+ RTFILE fh;
+ rc = RTFileOpen(&fh, szFile, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */);
+ RTFileClose(fh);
+ }
+ }
+
+ return rc;
+}
+#endif /* DEBUG */
+
+
+/**
+ * The actual worker routine (loop) for a started guest process.
+ *
+ * @return IPRT status code.
+ * @param pProcess The process we're servicing and monitoring.
+ */
+static int vgsvcGstCtrlProcessProcessWorker(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ VGSvcVerbose(3, "Thread of process pThread=0x%p = '%s' started\n", pProcess, pProcess->StartupInfo.szCmd);
+
+ VGSvcVerbose(3, "Guest process '%s', flags=0x%x\n", pProcess->StartupInfo.szCmd, pProcess->StartupInfo.uFlags);
+
+ int rc = VGSvcGstCtrlSessionProcessAdd(pProcess->pSession, pProcess);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("Errorwhile adding guest process '%s' (%p) to session process list, rc=%Rrc\n",
+ pProcess->StartupInfo.szCmd, pProcess, rc);
+ RTThreadUserSignal(RTThreadSelf());
+ return rc;
+ }
+
+ bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
+
+ /*
+ * Prepare argument list.
+ */
+ char **papszArgs;
+ int cArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
+ rc = RTGetOptArgvFromString(&papszArgs, &cArgs,
+ pProcess->StartupInfo.uNumArgs > 0 ? pProcess->StartupInfo.szArgs : "",
+ RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
+ /* Did we get the same result? */
+ Assert((int)pProcess->StartupInfo.uNumArgs == cArgs + 1 /* Take argv[0] into account */);
+
+ /*
+ * Prepare environment variables list.
+ */
+/** @todo r=bird: you don't need to prepare this, do you? Why don't you replace
+ * the brilliant RTStrAPrintf call with RTEnvPutEx and drop the papszEnv related code? */
+ char **papszEnv = NULL;
+ uint32_t uNumEnvVars = 0; /* Initialize in case of failing ... */
+ if (RT_SUCCESS(rc))
+ {
+ /* Prepare environment list. */
+ if (pProcess->StartupInfo.uNumEnvVars)
+ {
+ papszEnv = (char **)RTMemAlloc(pProcess->StartupInfo.uNumEnvVars * sizeof(char*));
+ AssertPtr(papszEnv);
+ uNumEnvVars = pProcess->StartupInfo.uNumEnvVars;
+
+ const char *pszCur = pProcess->StartupInfo.szEnv;
+ uint32_t i = 0;
+ uint32_t cbLen = 0;
+ while (cbLen < pProcess->StartupInfo.cbEnv)
+ {
+ /* sanity check */
+ if (i >= pProcess->StartupInfo.uNumEnvVars)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ int cbStr = RTStrAPrintf(&papszEnv[i++], "%s", pszCur);
+ if (cbStr < 0)
+ {
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+ pszCur += cbStr + 1; /* Skip terminating '\0' */
+ cbLen += cbStr + 1; /* Skip terminating '\0' */
+ }
+ Assert(i == pProcess->StartupInfo.uNumEnvVars);
+ }
+ }
+
+ /*
+ * Create the environment.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ RTENV hEnv;
+ rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ size_t i;
+ for (i = 0; i < uNumEnvVars && papszEnv; i++)
+ {
+ rc = RTEnvPutEx(hEnv, papszEnv[i]);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Setup the redirection of the standard stuff.
+ */
+ /** @todo consider supporting: gcc stuff.c >file 2>&1. */
+ RTHANDLE hStdIn;
+ PRTHANDLE phStdIn;
+ rc = vgsvcGstCtrlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
+ &hStdIn, &phStdIn, &pProcess->hPipeStdInW);
+ if (RT_SUCCESS(rc))
+ {
+ RTHANDLE hStdOut;
+ PRTHANDLE phStdOut;
+ rc = vgsvcGstCtrlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
+ ? "|" : "/dev/null",
+ 1 /*STDOUT_FILENO*/,
+ &hStdOut, &phStdOut, &pProcess->hPipeStdOutR);
+ if (RT_SUCCESS(rc))
+ {
+ RTHANDLE hStdErr;
+ PRTHANDLE phStdErr;
+ rc = vgsvcGstCtrlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
+ ? "|" : "/dev/null",
+ 2 /*STDERR_FILENO*/,
+ &hStdErr, &phStdErr, &pProcess->hPipeStdErrR);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a poll set for the pipes and let the
+ * transport layer add stuff to it as well.
+ */
+ rc = RTPollSetCreate(&pProcess->hPollSet);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uFlags = RTPOLL_EVT_ERROR;
+#if 0
+ /* Add reading event to pollset to get some more information. */
+ uFlags |= RTPOLL_EVT_READ;
+#endif
+ /* Stdin. */
+ if (RT_SUCCESS(rc))
+ rc = RTPollSetAddPipe(pProcess->hPollSet,
+ pProcess->hPipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
+ /* Stdout. */
+ if (RT_SUCCESS(rc))
+ rc = RTPollSetAddPipe(pProcess->hPollSet,
+ pProcess->hPipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
+ /* Stderr. */
+ if (RT_SUCCESS(rc))
+ rc = RTPollSetAddPipe(pProcess->hPollSet,
+ pProcess->hPipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
+ /* IPC notification pipe. */
+ if (RT_SUCCESS(rc))
+ rc = RTPipeCreate(&pProcess->hNotificationPipeR, &pProcess->hNotificationPipeW, 0 /* Flags */);
+ if (RT_SUCCESS(rc))
+ rc = RTPollSetAddPipe(pProcess->hPollSet,
+ pProcess->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
+ if (RT_SUCCESS(rc))
+ {
+ AssertPtr(pProcess->pSession);
+ bool fNeedsImpersonation = !(pProcess->pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_SPAWN);
+
+ rc = vgsvcGstCtrlProcessCreateProcess(pProcess->StartupInfo.szCmd, papszArgs, hEnv,
+ pProcess->StartupInfo.uFlags,
+ phStdIn, phStdOut, phStdErr,
+ fNeedsImpersonation ? pProcess->StartupInfo.szUser : NULL,
+ fNeedsImpersonation ? pProcess->StartupInfo.szPassword : NULL,
+ fNeedsImpersonation ? pProcess->StartupInfo.szDomain : NULL,
+ &pProcess->hProcess);
+ if (RT_FAILURE(rc))
+ VGSvcError("Error starting process, rc=%Rrc\n", rc);
+ /*
+ * Tell the session thread that it can continue
+ * spawning guest processes. This needs to be done after the new
+ * process has been started because otherwise signal handling
+ * on (Open) Solaris does not work correctly (see @bugref{5068}).
+ */
+ int rc2 = RTThreadUserSignal(RTThreadSelf());
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ fSignalled = true;
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close the child ends of any pipes and redirected files.
+ */
+ rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
+ phStdIn = NULL;
+ rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
+ phStdOut = NULL;
+ rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
+ phStdErr = NULL;
+
+ /* Enter the process main loop. */
+ rc = vgsvcGstCtrlProcessProcLoop(pProcess);
+
+ /*
+ * The handles that are no longer in the set have
+ * been closed by the above call in order to prevent
+ * the guest from getting stuck accessing them.
+ * So, NIL the handles to avoid closing them again.
+ */
+ /** @todo r=bird: Can't see how hNotificationPipeR could be closed here! Found (and fixed)
+ * confused comments documenting hNotificationPipeW, probably related. */
+ if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
+ VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
+ {
+ pProcess->hNotificationPipeR = NIL_RTPIPE;
+ pProcess->hNotificationPipeW = NIL_RTPIPE;
+ }
+ if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
+ VBOXSERVICECTRLPIPEID_STDERR, NULL)))
+ pProcess->hPipeStdErrR = NIL_RTPIPE;
+ if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
+ VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
+ pProcess->hPipeStdOutR = NIL_RTPIPE;
+ if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
+ VBOXSERVICECTRLPIPEID_STDIN, NULL)))
+ pProcess->hPipeStdInW = NIL_RTPIPE;
+ }
+ }
+ RTPollSetDestroy(pProcess->hPollSet);
+
+ RTPipeClose(pProcess->hNotificationPipeR);
+ pProcess->hNotificationPipeR = NIL_RTPIPE;
+ RTPipeClose(pProcess->hNotificationPipeW);
+ pProcess->hNotificationPipeW = NIL_RTPIPE;
+ }
+ RTPipeClose(pProcess->hPipeStdErrR);
+ pProcess->hPipeStdErrR = NIL_RTPIPE;
+ RTHandleClose(phStdErr);
+ if (phStdErr)
+ RTHandleClose(phStdErr);
+ }
+ RTPipeClose(pProcess->hPipeStdOutR);
+ pProcess->hPipeStdOutR = NIL_RTPIPE;
+ RTHandleClose(&hStdOut);
+ if (phStdOut)
+ RTHandleClose(phStdOut);
+ }
+ RTPipeClose(pProcess->hPipeStdInW);
+ pProcess->hPipeStdInW = NIL_RTPIPE;
+ RTHandleClose(phStdIn);
+ }
+ }
+ RTEnvDestroy(hEnv);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ VBGLR3GUESTCTRLCMDCTX ctx = { g_idControlSvcClient, pProcess->uContextID };
+ int rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
+ pProcess->uPID, PROC_STS_ERROR, rc,
+ NULL /* pvData */, 0 /* cbData */);
+ if ( RT_FAILURE(rc2)
+ && rc2 != VERR_NOT_FOUND)
+ VGSvcError("[PID %RU32]: Could not report process failure error; rc=%Rrc (process error %Rrc)\n",
+ pProcess->uPID, rc2, rc);
+ }
+
+ /* Free argument + environment variable lists. */
+ if (uNumEnvVars)
+ {
+ for (uint32_t i = 0; i < uNumEnvVars; i++)
+ RTStrFree(papszEnv[i]);
+ RTMemFree(papszEnv);
+ }
+ if (cArgs)
+ RTGetOptArgvFree(papszArgs);
+
+ /*
+ * If something went wrong signal the user event so that others don't wait
+ * forever on this thread.
+ */
+ if (RT_FAILURE(rc) && !fSignalled)
+ RTThreadUserSignal(RTThreadSelf());
+
+ VGSvcVerbose(3, "[PID %RU32]: Thread of process '%s' ended with rc=%Rrc\n",
+ pProcess->uPID, pProcess->StartupInfo.szCmd, rc);
+
+ /* Finally, update stopped status. */
+ ASMAtomicWriteBool(&pProcess->fStopped, true);
+ ASMAtomicWriteBool(&pProcess->fShutdown, true);
+
+ return rc;
+}
+
+
+static int vgsvcGstCtrlProcessLock(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ int rc = RTCritSectEnter(&pProcess->CritSect);
+ AssertRC(rc);
+ return rc;
+}
+
+
+/**
+ * Thread main routine for a started process.
+ *
+ * @return IPRT status code.
+ * @param hThreadSelf The thread handle.
+ * @param pvUser Pointer to a VBOXSERVICECTRLPROCESS structure.
+ *
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlProcessThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF1(hThreadSelf);
+ PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)pvUser;
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ return vgsvcGstCtrlProcessProcessWorker(pProcess);
+}
+
+
+static int vgsvcGstCtrlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ int rc = RTCritSectLeave(&pProcess->CritSect);
+ AssertRC(rc);
+ return rc;
+}
+
+
+/**
+ * Executes (starts) a process on the guest. This causes a new thread to be created
+ * so that this function will not block the overall program execution.
+ *
+ * @return IPRT status code.
+ * @param pSession Guest session.
+ * @param pStartupInfo Startup info.
+ * @param uContextID Context ID to associate the process to start with.
+ */
+int VGSvcGstCtrlProcessStart(const PVBOXSERVICECTRLSESSION pSession,
+ const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, uint32_t uContextID)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
+
+ /*
+ * Allocate new thread data and assign it to our thread list.
+ */
+ PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)RTMemAlloc(sizeof(VBOXSERVICECTRLPROCESS));
+ if (!pProcess)
+ return VERR_NO_MEMORY;
+
+ int rc = vgsvcGstCtrlProcessInit(pProcess, pSession, pStartupInfo, uContextID);
+ if (RT_SUCCESS(rc))
+ {
+ static uint32_t s_uCtrlExecThread = 0;
+ if (s_uCtrlExecThread++ == UINT32_MAX) /** @todo r=bird: ????????????? */
+ s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */
+ rc = RTThreadCreateF(&pProcess->Thread, vgsvcGstCtrlProcessThread,
+ pProcess /*pvUser*/, 0 /*cbStack*/,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("Creating thread for guest process '%s' failed: rc=%Rrc, pProcess=%p\n",
+ pStartupInfo->szCmd, rc, pProcess);
+
+ VGSvcGstCtrlProcessFree(pProcess);
+ }
+ else
+ {
+ VGSvcVerbose(4, "Waiting for thread to initialize ...\n");
+
+ /* Wait for the thread to initialize. */
+ rc = RTThreadUserWait(pProcess->Thread, 60 * 1000 /* 60 seconds max. */);
+ AssertRC(rc);
+ if ( ASMAtomicReadBool(&pProcess->fShutdown)
+ || ASMAtomicReadBool(&pProcess->fStopped)
+ || RT_FAILURE(rc))
+ {
+ VGSvcError("Thread for process '%s' failed to start, rc=%Rrc\n", pStartupInfo->szCmd, rc);
+ int rc2 = RTThreadWait(pProcess->Thread, RT_MS_1SEC * 30, NULL);
+ if (RT_SUCCESS(rc2))
+ pProcess->Thread = NIL_RTTHREAD;
+ VGSvcGstCtrlProcessFree(pProcess);
+ }
+ else
+ {
+ ASMAtomicXchgBool(&pProcess->fStarted, true);
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) vgsvcGstCtrlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis,
+ const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ bool fPendingClose, void *pvBuf, uint32_t cbBuf)
+{
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ int rc;
+
+ size_t cbWritten = 0;
+ if (pvBuf && cbBuf)
+ {
+ if (pThis->hPipeStdInW != NIL_RTPIPE)
+ rc = RTPipeWrite(pThis->hPipeStdInW, pvBuf, cbBuf, &cbWritten);
+ else
+ rc = VINF_EOF;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ /*
+ * If this is the last write + we have really have written all data
+ * we need to close the stdin pipe on our end and remove it from
+ * the poll set.
+ */
+ if ( fPendingClose
+ && cbBuf == cbWritten)
+ {
+ int rc2 = vgsvcGstCtrlProcessPollsetCloseInput(pThis, &pThis->hPipeStdInW);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status to send back to the host. */
+ uint32_t fFlags = 0; /* No flags at the moment. */
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "[PID %RU32]: Written %RU32 bytes input, CID=%RU32, fPendingClose=%RTbool\n",
+ pThis->uPID, cbWritten, pHostCtx->uContextID, fPendingClose);
+ uStatus = INPUT_STS_WRITTEN;
+ }
+ else
+ {
+ if (rc == VERR_BAD_PIPE)
+ uStatus = INPUT_STS_TERMINATED;
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ uStatus = INPUT_STS_OVERFLOW;
+ /* else undefined */
+ }
+
+ /*
+ * If there was an error and we did not set the host status
+ * yet, then do it now.
+ */
+ if ( RT_FAILURE(rc)
+ && uStatus == INPUT_STS_UNDEFINED)
+ {
+ uStatus = INPUT_STS_ERROR;
+ fFlags = rc; /* funny thing to call a "flag"... */
+ }
+ Assert(uStatus > INPUT_STS_UNDEFINED);
+
+ int rc2 = VbglR3GuestCtrlProcCbStatusInput(pHostCtx, pThis->uPID, uStatus, fFlags, (uint32_t)cbWritten);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessOnInput returned with rc=%Rrc\n", pThis->uPID, rc);
+#endif
+ return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
+}
+
+
+static DECLCALLBACK(int) vgsvcGstCtrlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis,
+ const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ uint32_t uHandle, uint32_t cbToRead, uint32_t fFlags)
+{
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ const PVBOXSERVICECTRLSESSION pSession = pThis->pSession;
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
+ int rc;
+
+ uint32_t cbBuf = cbToRead;
+ uint8_t *pvBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (pvBuf)
+ {
+ PRTPIPE phPipe = uHandle == OUTPUT_HANDLE_ID_STDOUT
+ ? &pThis->hPipeStdOutR
+ : &pThis->hPipeStdErrR;
+ AssertPtr(phPipe);
+
+ size_t cbRead = 0;
+ if (*phPipe != NIL_RTPIPE)
+ {
+ rc = RTPipeRead(*phPipe, pvBuf, cbBuf, &cbRead);
+ if (RT_FAILURE(rc))
+ {
+ RTPollSetRemove(pThis->hPollSet, uHandle == OUTPUT_HANDLE_ID_STDERR
+ ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT);
+ RTPipeClose(*phPipe);
+ *phPipe = NIL_RTPIPE;
+ if (rc == VERR_BROKEN_PIPE)
+ rc = VINF_EOF;
+ }
+ }
+ else
+ rc = VINF_EOF;
+
+#ifdef DEBUG
+ if (RT_SUCCESS(rc))
+ {
+ if ( pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT
+ && ( uHandle == OUTPUT_HANDLE_ID_STDOUT
+ || uHandle == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED)
+ )
+ {
+ /** @todo r=bird: vgsvcGstCtrlProcessDumpToFile(void *pvBuf, size_t cbBuf, const char *pszFileNmFmt, ...) */
+ char szDumpFile[RTPATH_MAX];
+ if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdOut.txt",
+ pSession->StartupInfo.uSessionID, pThis->uPID)) rc = VERR_BUFFER_UNDERFLOW;
+ if (RT_SUCCESS(rc))
+ rc = vgsvcGstCtrlProcessDumpToFile(szDumpFile, pvBuf, cbRead);
+ AssertRC(rc);
+ }
+ else if ( pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR
+ && uHandle == OUTPUT_HANDLE_ID_STDERR)
+ {
+ char szDumpFile[RTPATH_MAX];
+ if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdErr.txt",
+ pSession->StartupInfo.uSessionID, pThis->uPID))
+ rc = VERR_BUFFER_UNDERFLOW;
+ if (RT_SUCCESS(rc))
+ rc = vgsvcGstCtrlProcessDumpToFile(szDumpFile, pvBuf, cbRead);
+ AssertRC(rc);
+ }
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: Read %RU32 bytes output: uHandle=%RU32, CID=%RU32, fFlags=%x\n",
+ pThis->uPID, cbRead, uHandle, pHostCtx->uContextID, fFlags);
+#endif
+ /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
+ * data which the host needs to work with -- so just pass through all data unfiltered! */
+
+ /* Note: Since the context ID is unique the request *has* to be completed here,
+ * regardless whether we got data or not! Otherwise the waiting events
+ * on the host never will get completed! */
+ Assert((uint32_t)cbRead == cbRead);
+ rc = VbglR3GuestCtrlProcCbOutput(pHostCtx, pThis->uPID, uHandle, fFlags, pvBuf, (uint32_t)cbRead);
+ if ( RT_FAILURE(rc)
+ && rc == VERR_NOT_FOUND) /* Not critical if guest PID is not found on the host (anymore). */
+ rc = VINF_SUCCESS;
+ }
+
+ RTMemFree(pvBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: Reading output returned with rc=%Rrc\n", pThis->uPID, rc);
+#endif
+ return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
+}
+
+
+static DECLCALLBACK(int) vgsvcGstCtrlProcessOnTerm(PVBOXSERVICECTRLPROCESS pThis)
+{
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ if (!ASMAtomicXchgBool(&pThis->fShutdown, true))
+ VGSvcVerbose(3, "[PID %RU32]: Setting shutdown flag ...\n", pThis->uPID);
+
+ return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
+}
+
+
+static int vgsvcGstCtrlProcessRequestExV(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, bool fAsync,
+ RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction, unsigned cArgs, va_list Args)
+{
+ RT_NOREF1(pHostCtx);
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ /* pHostCtx is optional. */
+ AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
+ if (!fAsync)
+ AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
+
+ int rc = vgsvcGstCtrlProcessLock(pProcess);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessRequestExV fAsync=%RTbool, uTimeoutMS=%RU32, cArgs=%u\n",
+ pProcess->uPID, fAsync, uTimeoutMS, cArgs);
+#endif
+ uint32_t fFlags = RTREQFLAGS_IPRT_STATUS;
+ if (fAsync)
+ {
+ Assert(uTimeoutMS == 0);
+ fFlags |= RTREQFLAGS_NO_WAIT;
+ }
+
+ rc = RTReqQueueCallV(pProcess->hReqQueue, &pReq, uTimeoutMS, fFlags, pfnFunction, cArgs, Args);
+ if (RT_SUCCESS(rc))
+ {
+ /* Wake up the process' notification pipe to get
+ * the request being processed. */
+ Assert(pProcess->hNotificationPipeW != NIL_RTPIPE || pProcess->fShutdown /* latter in case of race */);
+ size_t cbWritten = 0;
+ rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten);
+ if ( RT_SUCCESS(rc)
+ && cbWritten != 1)
+ {
+ VGSvcError("[PID %RU32]: Notification pipe got %zu bytes instead of 1\n",
+ pProcess->uPID, cbWritten);
+ }
+ else if (RT_UNLIKELY(RT_FAILURE(rc)))
+ VGSvcError("[PID %RU32]: Writing to notification pipe failed, rc=%Rrc\n",
+ pProcess->uPID, rc);
+ }
+ else
+ VGSvcError("[PID %RU32]: RTReqQueueCallV failed, rc=%Rrc\n",
+ pProcess->uPID, rc);
+
+ int rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+#ifdef DEBUG
+ VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessRequestExV returned rc=%Rrc\n", pProcess->uPID, rc);
+#endif
+ return rc;
+}
+
+
+static int vgsvcGstCtrlProcessRequestAsync(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ PFNRT pfnFunction, unsigned cArgs, ...)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ /* pHostCtx is optional. */
+ AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
+
+ va_list va;
+ va_start(va, cArgs);
+ int rc = vgsvcGstCtrlProcessRequestExV(pProcess, pHostCtx, true /* fAsync */, 0 /* uTimeoutMS */,
+ NULL /* pReq */, pfnFunction, cArgs, va);
+ va_end(va);
+
+ return rc;
+}
+
+
+#if 0 /* unused */
+static int vgsvcGstCtrlProcessRequestWait(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction, unsigned cArgs, ...)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ /* pHostCtx is optional. */
+ AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
+
+ va_list va;
+ va_start(va, cArgs);
+ int rc = vgsvcGstCtrlProcessRequestExV(pProcess, pHostCtx, false /* fAsync */, uTimeoutMS,
+ pReq, pfnFunction, cArgs, va);
+ va_end(va);
+
+ return rc;
+}
+#endif
+
+
+int VGSvcGstCtrlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ bool fPendingClose, void *pvBuf, uint32_t cbBuf)
+{
+ if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
+ return vgsvcGstCtrlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)vgsvcGstCtrlProcessOnInput,
+ 5 /* cArgs */, pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
+
+ return vgsvcGstCtrlProcessOnInput(pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
+}
+
+
+int VGSvcGstCtrlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ uint32_t uHandle, uint32_t cbToRead, uint32_t fFlags)
+{
+ if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
+ return vgsvcGstCtrlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)vgsvcGstCtrlProcessOnOutput,
+ 5 /* cArgs */, pProcess, pHostCtx, uHandle, cbToRead, fFlags);
+
+ return vgsvcGstCtrlProcessOnOutput(pProcess, pHostCtx, uHandle, cbToRead, fFlags);
+}
+
+
+int VGSvcGstCtrlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess)
+{
+ if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
+ return vgsvcGstCtrlProcessRequestAsync(pProcess, NULL /* pHostCtx */, (PFNRT)vgsvcGstCtrlProcessOnTerm,
+ 1 /* cArgs */, pProcess);
+
+ return vgsvcGstCtrlProcessOnTerm(pProcess);
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp
new file mode 100644
index 00000000..c30e0c8e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp
@@ -0,0 +1,2453 @@
+/* $Id: VBoxServiceControlSession.cpp $ */
+/** @file
+ * VBoxServiceControlSession - Guest session handling. Also handles the spawned session processes.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/handle.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/rand.h>
+
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+#include "VBoxServiceControl.h"
+
+using namespace guestControl;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Generic option indices for session spawn arguments. */
+enum
+{
+ VBOXSERVICESESSIONOPT_FIRST = 1000, /* For initialization. */
+ VBOXSERVICESESSIONOPT_DOMAIN,
+#ifdef DEBUG
+ VBOXSERVICESESSIONOPT_DUMP_STDOUT,
+ VBOXSERVICESESSIONOPT_DUMP_STDERR,
+#endif
+ VBOXSERVICESESSIONOPT_LOG_FILE,
+ VBOXSERVICESESSIONOPT_USERNAME,
+ VBOXSERVICESESSIONOPT_SESSION_ID,
+ VBOXSERVICESESSIONOPT_SESSION_PROTO,
+ VBOXSERVICESESSIONOPT_THREAD_ID
+};
+
+
+/**
+ * Helper that grows the scratch buffer.
+ * @returns Success indicator.
+ */
+static bool vgsvcGstCtrlSessionGrowScratchBuf(void **ppvScratchBuf, uint32_t *pcbScratchBuf, uint32_t cbMinBuf)
+{
+ uint32_t cbNew = *pcbScratchBuf * 2;
+ if ( cbNew <= VMMDEV_MAX_HGCM_DATA_SIZE
+ && cbMinBuf <= VMMDEV_MAX_HGCM_DATA_SIZE)
+ {
+ while (cbMinBuf > cbNew)
+ cbNew *= 2;
+ void *pvNew = RTMemRealloc(*ppvScratchBuf, cbNew);
+ if (pvNew)
+ {
+ *ppvScratchBuf = pvNew;
+ *pcbScratchBuf = cbNew;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+static int vgsvcGstCtrlSessionFileDestroy(PVBOXSERVICECTRLFILE pFile)
+{
+ AssertPtrReturn(pFile, VERR_INVALID_POINTER);
+
+ int rc = RTFileClose(pFile->hFile);
+ if (RT_SUCCESS(rc))
+ {
+ /* Remove file entry in any case. */
+ RTListNodeRemove(&pFile->Node);
+ /* Destroy this object. */
+ RTMemFree(pFile);
+ }
+
+ return rc;
+}
+
+
+/** @todo No locking done yet! */
+static PVBOXSERVICECTRLFILE vgsvcGstCtrlSessionFileGetLocked(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle)
+{
+ AssertPtrReturn(pSession, NULL);
+
+ /** @todo Use a map later! */
+ PVBOXSERVICECTRLFILE pFileCur;
+ RTListForEach(&pSession->lstFiles, pFileCur, VBOXSERVICECTRLFILE, Node)
+ {
+ if (pFileCur->uHandle == uHandle)
+ return pFileCur;
+ }
+
+ return NULL;
+}
+
+
+static int vgsvcGstCtrlSessionHandleDirRemove(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the message.
+ */
+ char szDir[RTPATH_MAX];
+ uint32_t fFlags; /* DIRREMOVE_FLAG_XXX */
+ int rc = VbglR3GuestCtrlDirGetRemove(pHostCtx, szDir, sizeof(szDir), &fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do some validating before executing the job.
+ */
+ if (!(fFlags & ~DIRREMOVE_FLAG_VALID_MASK))
+ {
+ if (fFlags & DIRREMOVE_FLAG_RECURSIVE)
+ {
+ uint32_t fFlagsRemRec = RTDIRRMREC_F_CONTENT_AND_DIR; /* Set default. */
+ if (fFlags & DIRREMOVE_FLAG_CONTENT_ONLY)
+ fFlagsRemRec |= RTDIRRMREC_F_CONTENT_ONLY;
+ rc = RTDirRemoveRecursive(szDir, fFlagsRemRec);
+ VGSvcVerbose(4, "[Dir %s]: rmdir /s (%#x) -> rc=%Rrc\n", szDir, fFlags, rc);
+ }
+ else
+ {
+ /* Only delete directory if not empty. */
+ rc = RTDirRemove(szDir);
+ VGSvcVerbose(4, "[Dir %s]: rmdir (%#x), rc=%Rrc\n", szDir, fFlags, rc);
+ }
+ }
+ else
+ {
+ VGSvcError("[Dir %s]: Unsupported flags: %#x (all %#x)\n", szDir, (fFlags & ~DIRREMOVE_FLAG_VALID_MASK), fFlags);
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("[Dir %s]: Failed to report removing status, rc=%Rrc\n", szDir, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for rmdir operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+
+ VGSvcVerbose(6, "Removing directory '%s' returned rc=%Rrc\n", szDir, rc);
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the message.
+ */
+ char szFile[RTPATH_MAX];
+ char szAccess[64];
+ char szDisposition[64];
+ char szSharing[64];
+ uint32_t uCreationMode = 0;
+ uint64_t offOpen = 0;
+ uint32_t uHandle = 0;
+ int rc = VbglR3GuestCtrlFileGetOpen(pHostCtx,
+ /* File to open. */
+ szFile, sizeof(szFile),
+ /* Open mode. */
+ szAccess, sizeof(szAccess),
+ /* Disposition. */
+ szDisposition, sizeof(szDisposition),
+ /* Sharing. */
+ szSharing, sizeof(szSharing),
+ /* Creation mode. */
+ &uCreationMode,
+ /* Offset. */
+ &offOpen);
+ VGSvcVerbose(4, "[File %s]: szAccess=%s, szDisposition=%s, szSharing=%s, offOpen=%RU64, rc=%Rrc\n",
+ szFile, szAccess, szDisposition, szSharing, offOpen, rc);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAllocZ(sizeof(VBOXSERVICECTRLFILE));
+ if (pFile)
+ {
+ pFile->hFile = NIL_RTFILE; /* Not zero or NULL! */
+ if (szFile[0])
+ {
+ RTStrCopy(pFile->szName, sizeof(pFile->szName), szFile);
+
+/** @todo
+ * Implement szSharing!
+ */
+ uint64_t fFlags;
+ rc = RTFileModeToFlagsEx(szAccess, szDisposition, NULL /* pszSharing, not used yet */, &fFlags);
+ VGSvcVerbose(4, "[File %s] Opening with fFlags=0x%x, rc=%Rrc\n", pFile->szName, fFlags, rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileOpen(&pFile->hFile, pFile->szName, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /* Seeking is optional. However, the whole operation
+ * will fail if we don't succeed seeking to the wanted position. */
+ if (offOpen)
+ rc = RTFileSeek(pFile->hFile, (int64_t)offOpen, RTFILE_SEEK_BEGIN, NULL /* Current offset */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Succeeded!
+ */
+ uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID);
+ pFile->uHandle = uHandle;
+ RTListAppend(&pSession->lstFiles, &pFile->Node);
+ VGSvcVerbose(2, "[File %s] Opened (ID=%RU32)\n", pFile->szName, pFile->uHandle);
+ }
+ else
+ VGSvcError("[File %s] Seeking to offset %RU64 failed: rc=%Rrc\n", pFile->szName, offOpen, rc);
+ }
+ else
+ VGSvcError("[File %s] Opening failed with rc=%Rrc\n", pFile->szName, rc);
+ }
+ }
+ else
+ {
+ VGSvcError("[File %s] empty filename!\n", szFile);
+ rc = VERR_INVALID_NAME;
+ }
+
+ /* clean up if we failed. */
+ if (RT_FAILURE(rc))
+ {
+ if (pFile->hFile != NIL_RTFILE)
+ RTFileClose(pFile->hFile);
+ RTMemFree(pFile);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbOpen(pHostCtx, rc, uHandle);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("[File %s]: Failed to report file open status, rc=%Rrc\n", szFile, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for open file operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+
+ VGSvcVerbose(4, "[File %s] Opening (open mode='%s', disposition='%s', creation mode=0x%x) returned rc=%Rrc\n",
+ szFile, szAccess, szDisposition, uCreationMode, rc);
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the message.
+ */
+ uint32_t uHandle = 0;
+ int rc = VbglR3GuestCtrlFileGetClose(pHostCtx, &uHandle /* File handle to close */);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ VGSvcVerbose(2, "[File %s] Closing (handle=%RU32)\n", pFile ? pFile->szName : "<Not found>", uHandle);
+ rc = vgsvcGstCtrlSessionFileDestroy(pFile);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbClose(pHostCtx, rc);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file close status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for close file operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uHandle = 0;
+ uint32_t cbToRead;
+ int rc = VbglR3GuestCtrlFileGetRead(pHostCtx, &uHandle, &cbToRead);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the file and do the reading.
+ *
+ * If the request is larger than our scratch buffer, try grow it - just
+ * ignore failure as the host better respect our buffer limits.
+ */
+ size_t cbRead = 0;
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ if (*pcbScratchBuf < cbToRead)
+ vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
+
+ rc = RTFileRead(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
+ VGSvcVerbose(5, "[File %s] Read %zu/%RU32 bytes, rc=%Rrc\n", pFile->szName, cbRead, cbToRead, rc);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result and data back to the host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file read status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file read operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileReadAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uHandle = 0;
+ uint32_t cbToRead;
+ uint64_t offReadAt;
+ int rc = VbglR3GuestCtrlFileGetReadAt(pHostCtx, &uHandle, &cbToRead, &offReadAt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the file and do the reading.
+ *
+ * If the request is larger than our scratch buffer, try grow it - just
+ * ignore failure as the host better respect our buffer limits.
+ */
+ size_t cbRead = 0;
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ if (*pcbScratchBuf < cbToRead)
+ vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
+
+ rc = RTFileReadAt(pFile->hFile, (RTFOFF)offReadAt, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
+ VGSvcVerbose(5, "[File %s] Read %zu bytes @ %RU64, rc=%Rrc\n", pFile->szName, cbRead, offReadAt, rc);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result and data back to the host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file read at status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file read at operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileWrite(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request and data to write.
+ */
+ uint32_t uHandle = 0;
+ uint32_t cbToWrite;
+ int rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
+ rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the file and do the writing.
+ */
+ size_t cbWritten = 0;
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ rc = RTFileWrite(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
+ VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 => %Rrc, cbWritten=%zu\n",
+ pFile->szName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), rc, cbWritten);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file write operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileWriteAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request and data to write.
+ */
+ uint32_t uHandle = 0;
+ uint32_t cbToWrite;
+ uint64_t offWriteAt;
+ int rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
+ rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the file and do the writing.
+ */
+ size_t cbWritten = 0;
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ rc = RTFileWriteAt(pFile->hFile, (RTFOFF)offWriteAt, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
+ VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 @ %RU64 => %Rrc, cbWritten=%zu\n",
+ pFile->szName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), offWriteAt, rc, cbWritten);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file write at operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileSeek(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uHandle = 0;
+ uint32_t uSeekMethod;
+ uint64_t offSeek; /* Will be converted to int64_t. */
+ int rc = VbglR3GuestCtrlFileGetSeek(pHostCtx, &uHandle, &uSeekMethod, &offSeek);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t offActual = 0;
+
+ /*
+ * Validate and convert the seek method to IPRT speak.
+ */
+ static const uint8_t s_abMethods[GUEST_FILE_SEEKTYPE_END + 1] =
+ {
+ UINT8_MAX, RTFILE_SEEK_BEGIN, UINT8_MAX, UINT8_MAX, RTFILE_SEEK_CURRENT,
+ UINT8_MAX, UINT8_MAX, UINT8_MAX, GUEST_FILE_SEEKTYPE_END
+ };
+ if ( uSeekMethod < RT_ELEMENTS(s_abMethods)
+ && s_abMethods[uSeekMethod] != UINT8_MAX)
+ {
+ /*
+ * Locate the file and do the seek.
+ */
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ rc = RTFileSeek(pFile->hFile, (int64_t)offSeek, s_abMethods[uSeekMethod], &offActual);
+ VGSvcVerbose(5, "[File %s]: Seeking to offSeek=%RI64, uSeekMethodIPRT=%u, rc=%Rrc\n",
+ pFile->szName, offSeek, s_abMethods[uSeekMethod], rc);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ {
+ VGSvcError("Invalid seek method: %#x\n", uSeekMethod);
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbSeek(pHostCtx, rc, offActual);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file seek status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file seek operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleFileTell(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uHandle = 0;
+ int rc = VbglR3GuestCtrlFileGetTell(pHostCtx, &uHandle);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the file and ask for the current position.
+ */
+ uint64_t offCurrent = 0;
+ PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
+ if (pFile)
+ {
+ offCurrent = RTFileTell(pFile->hFile);
+ VGSvcVerbose(5, "[File %s]: Telling offCurrent=%RU64\n", pFile->szName, offCurrent);
+ }
+ else
+ {
+ VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
+ rc = VERR_NOT_FOUND;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlFileCbTell(pHostCtx, rc, offCurrent);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report file tell status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for file tell operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ char szSource[RTPATH_MAX];
+ char szDest[RTPATH_MAX];
+ uint32_t fFlags = 0; /* PATHRENAME_FLAG_XXX */
+ int rc = VbglR3GuestCtrlPathGetRename(pHostCtx, szSource, sizeof(szSource), szDest, sizeof(szDest), &fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Validate the flags (kudos for using the same as IPRT), then do the renaming.
+ */
+ AssertCompile(PATHRENAME_FLAG_NO_REPLACE == RTPATHRENAME_FLAGS_NO_REPLACE);
+ AssertCompile(PATHRENAME_FLAG_REPLACE == RTPATHRENAME_FLAGS_REPLACE);
+ AssertCompile(PATHRENAME_FLAG_NO_SYMLINKS == RTPATHRENAME_FLAGS_NO_SYMLINKS);
+ AssertCompile(PATHRENAME_FLAG_VALID_MASK == (RTPATHRENAME_FLAGS_NO_REPLACE | RTPATHRENAME_FLAGS_REPLACE | RTPATHRENAME_FLAGS_NO_SYMLINKS));
+ if (!(fFlags & ~PATHRENAME_FLAG_VALID_MASK))
+ {
+ VGSvcVerbose(4, "Renaming '%s' to '%s', fFlags=%#x, rc=%Rrc\n", szSource, szDest, fFlags, rc);
+ rc = RTPathRename(szSource, szDest, fFlags);
+ }
+ else
+ {
+ VGSvcError("Invalid rename flags: %#x\n", fFlags);
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Report result back to host.
+ */
+ int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report renaming status, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for rename operation: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ VGSvcVerbose(5, "Renaming '%s' to '%s' returned rc=%Rrc\n", szSource, szDest, rc);
+ return rc;
+}
+
+
+/**
+ * Handles getting the user's documents directory.
+ *
+ * @returns VBox status code.
+ * @param pSession Guest session.
+ * @param pHostCtx Host context.
+ */
+static int vgsvcGstCtrlSessionHandlePathUserDocuments(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ int rc = VbglR3GuestCtrlPathGetUserDocuments(pHostCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the path and pass it back to the host..
+ */
+ char szPath[RTPATH_MAX];
+ rc = RTPathUserDocuments(szPath, sizeof(szPath));
+#ifdef DEBUG
+ VGSvcVerbose(2, "User documents is '%s', rc=%Rrc\n", szPath, rc);
+#endif
+
+ int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
+ RT_SUCCESS(rc) ? (uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report user documents, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for user documents path request: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+/**
+ * Handles getting the user's home directory.
+ *
+ * @returns VBox status code.
+ * @param pSession Guest session.
+ * @param pHostCtx Host context.
+ */
+static int vgsvcGstCtrlSessionHandlePathUserHome(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ int rc = VbglR3GuestCtrlPathGetUserHome(pHostCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the path and pass it back to the host..
+ */
+ char szPath[RTPATH_MAX];
+ rc = RTPathUserHome(szPath, sizeof(szPath));
+
+#ifdef DEBUG
+ VGSvcVerbose(2, "User home is '%s', rc=%Rrc\n", szPath, rc);
+#endif
+ /* Report back in any case. */
+ int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
+ RT_SUCCESS(rc) ?(uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Failed to report user home, rc=%Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for user home directory path request: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+/**
+ * Handles starting a guest processes.
+ *
+ * @returns VBox status code.
+ * @param pSession Guest session.
+ * @param pHostCtx Host context.
+ */
+static int vgsvcGstCtrlSessionHandleProcExec(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+/** @todo this hardcoded stuff needs redoing. */
+
+ /* Initialize maximum environment block size -- needed as input
+ * parameter to retrieve the stuff from the host. On output this then
+ * will contain the actual block size. */
+ VBOXSERVICECTRLPROCSTARTUPINFO startupInfo;
+ RT_ZERO(startupInfo);
+ startupInfo.cbEnv = sizeof(startupInfo.szEnv);
+
+ int rc = VbglR3GuestCtrlProcGetStart(pHostCtx,
+ /* Command */
+ startupInfo.szCmd, sizeof(startupInfo.szCmd),
+ /* Flags */
+ &startupInfo.uFlags,
+ /* Arguments */
+ startupInfo.szArgs, sizeof(startupInfo.szArgs), &startupInfo.uNumArgs,
+ /* Environment */
+ startupInfo.szEnv, &startupInfo.cbEnv, &startupInfo.uNumEnvVars,
+ /* Credentials; for hosts with VBox < 4.3 (protocol version 1).
+ * For protocol v2 and up the credentials are part of the session
+ * opening call. */
+ startupInfo.szUser, sizeof(startupInfo.szUser),
+ startupInfo.szPassword, sizeof(startupInfo.szPassword),
+ /* Timeout (in ms) */
+ &startupInfo.uTimeLimitMS,
+ /* Process priority */
+ &startupInfo.uPriority,
+ /* Process affinity */
+ startupInfo.uAffinity, sizeof(startupInfo.uAffinity), &startupInfo.uNumAffinity);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "Request to start process szCmd=%s, fFlags=0x%x, szArgs=%s, szEnv=%s, uTimeout=%RU32\n",
+ startupInfo.szCmd, startupInfo.uFlags,
+ startupInfo.uNumArgs ? startupInfo.szArgs : "<None>",
+ startupInfo.uNumEnvVars ? startupInfo.szEnv : "<None>",
+ startupInfo.uTimeLimitMS);
+
+ bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
+ rc = VGSvcGstCtrlSessionProcessStartAllowed(pSession, &fStartAllowed);
+ if (RT_SUCCESS(rc))
+ {
+ if (fStartAllowed)
+ rc = VGSvcGstCtrlProcessStart(pSession, &startupInfo, pHostCtx->uContextID);
+ else
+ rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
+ }
+
+ /* We're responsible for signaling errors to the host (it will wait for ever otherwise). */
+ if (RT_FAILURE(rc))
+ {
+ VGSvcError("Starting process failed with rc=%Rrc, protocol=%RU32, parameters=%RU32\n",
+ rc, pHostCtx->uProtocol, pHostCtx->uNumParms);
+ int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx, 0 /*nil-PID*/, PROC_STS_ERROR, rc, NULL /*pvData*/, 0 /*cbData*/);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Error sending start process status to host, rc=%Rrc\n", rc2);
+ }
+ }
+ else
+ {
+ VGSvcError("Failed to retrieve parameters for process start: %Rrc (cParms=%u)\n", rc, pHostCtx->uNumParms);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+/**
+ * Sends stdin input to a specific guest process.
+ *
+ * @returns VBox status code.
+ * @param pSession The session which is in charge.
+ * @param pHostCtx The host context to use.
+ * @param ppvScratchBuf The scratch buffer, we may grow it.
+ * @param pcbScratchBuf The scratch buffer size for retrieving the input
+ * data.
+ */
+static int vgsvcGstCtrlSessionHandleProcInput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the data from the host.
+ */
+ uint32_t uPID;
+ uint32_t fFlags;
+ uint32_t cbInput;
+ int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbInput))
+ rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & INPUT_FLAG_EOF)
+ VGSvcVerbose(4, "Got last process input block for PID=%RU32 (%RU32 bytes) ...\n", uPID, cbInput);
+
+ /*
+ * Locate the process and feed it.
+ */
+ PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
+ if (pProcess)
+ {
+ rc = VGSvcGstCtrlProcessHandleInput(pProcess, pHostCtx, RT_BOOL(fFlags & INPUT_FLAG_EOF),
+ *ppvScratchBuf, RT_MIN(cbInput, *pcbScratchBuf));
+ if (RT_FAILURE(rc))
+ VGSvcError("Error handling input message for PID=%RU32, rc=%Rrc\n", uPID, rc);
+ VGSvcGstCtrlProcessRelease(pProcess);
+ }
+ else
+ {
+ VGSvcError("Could not find PID %u for feeding %u bytes to it.\n", uPID, cbInput);
+ rc = VERR_PROCESS_NOT_FOUND;
+ VbglR3GuestCtrlProcCbStatusInput(pHostCtx, uPID, INPUT_STS_ERROR, rc, 0);
+ }
+ }
+ else
+ {
+ VGSvcError("Failed to retrieve parameters for process input: %Rrc (scratch %u bytes)\n", rc, *pcbScratchBuf);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+
+ VGSvcVerbose(6, "Feeding input to PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
+ return rc;
+}
+
+
+/**
+ * Gets stdout/stderr output of a specific guest process.
+ *
+ * @returns VBox status code.
+ * @param pSession The session which is in charge.
+ * @param pHostCtx The host context to use.
+ */
+static int vgsvcGstCtrlSessionHandleProcOutput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uPID;
+ uint32_t uHandleID;
+ uint32_t fFlags;
+ int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &fFlags);
+#ifdef DEBUG_andy
+ VGSvcVerbose(4, "Getting output for PID=%RU32, CID=%RU32, uHandleID=%RU32, fFlags=%RU32\n",
+ uPID, pHostCtx->uContextID, uHandleID, fFlags);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the process and hand it the output request.
+ */
+ PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
+ if (pProcess)
+ {
+ rc = VGSvcGstCtrlProcessHandleOutput(pProcess, pHostCtx, uHandleID, _64K /* cbToRead */, fFlags);
+ if (RT_FAILURE(rc))
+ VGSvcError("Error getting output for PID=%RU32, rc=%Rrc\n", uPID, rc);
+ VGSvcGstCtrlProcessRelease(pProcess);
+ }
+ else
+ {
+ VGSvcError("Could not find PID %u for draining handle %u (%#x).\n", uPID, uHandleID, uHandleID);
+ rc = VERR_PROCESS_NOT_FOUND;
+/** @todo r=bird:
+ *
+ * No way to report status status code for output requests?
+ *
+ */
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for process output request: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+
+#ifdef DEBUG_andy
+ VGSvcVerbose(4, "Getting output for PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
+#endif
+ return rc;
+}
+
+
+/**
+ * Tells a guest process to terminate.
+ *
+ * @returns VBox status code.
+ * @param pSession The session which is in charge.
+ * @param pHostCtx The host context to use.
+ */
+static int vgsvcGstCtrlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uPID;
+ int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the process and terminate it.
+ */
+ PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
+ if (pProcess)
+ {
+ rc = VGSvcGstCtrlProcessHandleTerm(pProcess);
+
+ VGSvcGstCtrlProcessRelease(pProcess);
+ }
+ else
+ {
+ VGSvcError("Could not find PID %u for termination.\n", uPID);
+ rc = VERR_PROCESS_NOT_FOUND;
+/** @todo r=bird:
+ *
+ * No way to report status status code for output requests?
+ *
+ */
+ }
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for process termination request: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+#ifdef DEBUG_andy
+ VGSvcVerbose(4, "Terminating PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
+#endif
+ return rc;
+}
+
+
+static int vgsvcGstCtrlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+
+ /*
+ * Retrieve the request.
+ */
+ uint32_t uPID;
+ uint32_t uWaitFlags;
+ uint32_t uTimeoutMS;
+ int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the process and the realize that this call makes no sense
+ * since we'll notify the host when a process terminates anyway and
+ * hopefully don't need any additional encouragement.
+ */
+ PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
+ if (pProcess)
+ {
+ rc = VERR_NOT_IMPLEMENTED; /** @todo */
+ VGSvcGstCtrlProcessRelease(pProcess);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ {
+ VGSvcError("Error fetching parameters for process wait request: %Rrc\n", rc);
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
+ }
+ return rc;
+}
+
+
+int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
+ void **ppvScratchBuf, uint32_t *pcbScratchBuf, volatile bool *pfShutdown)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(*ppvScratchBuf, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfShutdown, VERR_INVALID_POINTER);
+
+
+ /*
+ * Only anonymous sessions (that is, sessions which run with local
+ * service privileges) or spawned session processes can do certain
+ * operations.
+ */
+ bool const fImpersonated = RT_BOOL(pSession->fFlags & ( VBOXSERVICECTRLSESSION_FLAG_SPAWN
+ | VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS));
+ int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
+
+ switch (uMsg)
+ {
+ case HOST_MSG_SESSION_CLOSE:
+ /* Shutdown (this spawn). */
+ rc = VGSvcGstCtrlSessionClose(pSession);
+ *pfShutdown = true; /* Shutdown in any case. */
+ break;
+
+ case HOST_MSG_DIR_REMOVE:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_EXEC_CMD:
+ rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_EXEC_SET_INPUT:
+ rc = vgsvcGstCtrlSessionHandleProcInput(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
+ break;
+
+ case HOST_MSG_EXEC_GET_OUTPUT:
+ rc = vgsvcGstCtrlSessionHandleProcOutput(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_EXEC_TERMINATE:
+ rc = vgsvcGstCtrlSessionHandleProcTerminate(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_EXEC_WAIT_FOR:
+ rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_FILE_OPEN:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_FILE_CLOSE:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileClose(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_FILE_READ:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileRead(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
+ break;
+
+ case HOST_MSG_FILE_READ_AT:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileReadAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
+ break;
+
+ case HOST_MSG_FILE_WRITE:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileWrite(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
+ break;
+
+ case HOST_MSG_FILE_WRITE_AT:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileWriteAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
+ break;
+
+ case HOST_MSG_FILE_SEEK:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileSeek(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_FILE_TELL:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandleFileTell(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_PATH_RENAME:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_PATH_USER_DOCUMENTS:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandlePathUserDocuments(pSession, pHostCtx);
+ break;
+
+ case HOST_MSG_PATH_USER_HOME:
+ if (fImpersonated)
+ rc = vgsvcGstCtrlSessionHandlePathUserHome(pSession, pHostCtx);
+ break;
+
+ default: /* Not supported, see next code block. */
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc != VERR_NOT_SUPPORTED) /* Note: Reply to host must must be sent by above handler. */
+ VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc);
+ else
+ {
+ /* We must skip and notify host here as best we can... */
+ VGSvcVerbose(1, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n", uMsg, pHostCtx->uNumParms);
+ if (VbglR3GuestCtrlSupportsOptimizations(pHostCtx->uClientID))
+ VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, VERR_NOT_SUPPORTED, uMsg);
+ else
+ VbglR3GuestCtrlMsgSkipOld(pHostCtx->uClientID);
+ rc = VINF_SUCCESS;
+ }
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc);
+
+ return rc;
+}
+
+
+/**
+ * Thread main routine for a spawned guest session process.
+ *
+ * This thread runs in the main executable to control the spawned session process.
+ *
+ * @returns VBox status code.
+ * @param hThreadSelf Thread handle.
+ * @param pvUser Pointer to a VBOXSERVICECTRLSESSIONTHREAD structure.
+ *
+ */
+static DECLCALLBACK(int) vgsvcGstCtrlSessionThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser;
+ AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+
+ uint32_t const idSession = pThread->StartupInfo.uSessionID;
+ uint32_t const idClient = g_idControlSvcClient;
+ VGSvcVerbose(3, "Session ID=%RU32 thread running\n", idSession);
+
+ /* Let caller know that we're done initializing, regardless of the result. */
+ int rc2 = RTThreadUserSignal(hThreadSelf);
+ AssertRC(rc2);
+
+ /*
+ * Wait for the child process to stop or the shutdown flag to be signalled.
+ */
+ RTPROCSTATUS ProcessStatus = { 0, RTPROCEXITREASON_NORMAL };
+ bool fProcessAlive = true;
+ bool fSessionCancelled = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
+ uint32_t cMsShutdownTimeout = 30 * 1000; /** @todo Make this configurable. Later. */
+ uint64_t msShutdownStart = 0;
+ uint64_t const msStart = RTTimeMilliTS();
+ size_t offSecretKey = 0;
+ int rcWait;
+ for (;;)
+ {
+ /* Secret key feeding. */
+ if (offSecretKey < sizeof(pThread->abKey))
+ {
+ size_t cbWritten = 0;
+ rc2 = RTPipeWrite(pThread->hKeyPipe, &pThread->abKey[offSecretKey], sizeof(pThread->abKey) - offSecretKey, &cbWritten);
+ if (RT_SUCCESS(rc2))
+ offSecretKey += cbWritten;
+ }
+
+ /* Poll child process status. */
+ rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+ if ( rcWait == VINF_SUCCESS
+ || rcWait == VERR_PROCESS_NOT_FOUND)
+ {
+ fProcessAlive = false;
+ break;
+ }
+ AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING || rcWait == VERR_INTERRUPTED,
+ ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait));
+
+ /* Shutting down? */
+ if (ASMAtomicReadBool(&pThread->fShutdown))
+ {
+ if (!msShutdownStart)
+ {
+ VGSvcVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n",
+ pThread->hProcess, idSession);
+
+ VBGLR3GUESTCTRLCMDCTX hostCtx =
+ {
+ /* .idClient = */ idClient,
+ /* .idContext = */ VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
+ /* .uProtocol = */ pThread->StartupInfo.uProtocol,
+ /* .cParams = */ 2
+ };
+ rc2 = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* fFlags */);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n",
+ pThread->hProcess, idSession, rc2);
+
+ if (rc2 == VERR_NOT_SUPPORTED)
+ {
+ /* Terminate guest session process in case it's not supported by a too old host. */
+ rc2 = RTProcTerminate(pThread->hProcess);
+ VGSvcVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n",
+ pThread->hProcess, rc2);
+ }
+ break;
+ }
+
+ VGSvcVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32 ms timeout) ...\n",
+ idSession, cMsShutdownTimeout);
+ msShutdownStart = RTTimeMilliTS();
+ continue; /* Don't waste time on waiting. */
+ }
+ if (RTTimeMilliTS() - msShutdownStart > cMsShutdownTimeout)
+ {
+ VGSvcVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", idSession);
+ break;
+ }
+ }
+
+ /* Cancel the prepared session stuff after 30 seconds. */
+ if ( !fSessionCancelled
+ && RTTimeMilliTS() - msStart >= 30000)
+ {
+ VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
+ fSessionCancelled = true;
+ }
+
+/** @todo r=bird: This 100ms sleep is _extremely_ sucky! */
+ RTThreadSleep(100); /* Wait a bit. */
+ }
+
+ if (!fSessionCancelled)
+ VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
+
+ if (!fProcessAlive)
+ {
+ VGSvcVerbose(2, "Guest session process (ID=%RU32) terminated with rc=%Rrc, reason=%d, status=%d\n",
+ idSession, rcWait, ProcessStatus.enmReason, ProcessStatus.iStatus);
+ if (ProcessStatus.iStatus == RTEXITCODE_INIT)
+ {
+ VGSvcError("Guest session process (ID=%RU32) failed to initialize. Here some hints:\n", idSession);
+ VGSvcError("- Is logging enabled and the output directory is read-only by the guest session user?\n");
+ /** @todo Add more here. */
+ }
+ }
+
+ uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED;
+ uint32_t uSessionRc = VINF_SUCCESS; /** uint32_t vs. int. */
+
+ if (fProcessAlive)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ VGSvcVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", idSession, i + 1);
+
+ rc2 = RTProcTerminate(pThread->hProcess);
+ if (RT_SUCCESS(rc2))
+ break;
+ /** @todo r=bird: What's the point of sleeping 3 second after the last attempt? */
+ RTThreadSleep(3000);
+ }
+
+ VGSvcVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", idSession, rc2);
+ uSessionStatus = RT_SUCCESS(rc2) ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA;
+ }
+ else if (RT_SUCCESS(rcWait))
+ {
+ switch (ProcessStatus.enmReason)
+ {
+ case RTPROCEXITREASON_NORMAL:
+ uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
+ break;
+
+ case RTPROCEXITREASON_ABEND:
+ uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
+ break;
+
+ case RTPROCEXITREASON_SIGNAL:
+ uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
+ break;
+
+ default:
+ AssertMsgFailed(("Unhandled process termination reason (%d)\n", ProcessStatus.enmReason));
+ uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
+ break;
+ }
+ }
+ else
+ {
+ /* If we didn't find the guest process anymore, just assume it terminated normally. */
+ uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
+ }
+
+ VGSvcVerbose(3, "Guest session ID=%RU32 thread ended with sessionStatus=%RU32, sessionRc=%Rrc\n",
+ idSession, uSessionStatus, uSessionRc);
+
+ /*
+ * Report final status.
+ */
+ Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
+ VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession) };
+ rc2 = VbglR3GuestCtrlSessionNotify(&ctx, uSessionStatus, uSessionRc);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Reporting session ID=%RU32 final status failed with rc=%Rrc\n", idSession, rc2);
+
+ VGSvcVerbose(3, "Session ID=%RU32 thread ending\n", idSession);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Reads the secret key the parent VBoxService instance passed us and pass it
+ * along as a authentication token to the host service.
+ *
+ * For older hosts, this sets up the message filtering.
+ *
+ * @returns VBox status code.
+ * @param idClient The HGCM client ID.
+ * @param idSession The session ID.
+ */
+static int vgsvcGstCtrlSessionReadKeyAndAccept(uint32_t idClient, uint32_t idSession)
+{
+ /*
+ * Read it.
+ */
+ RTHANDLE Handle;
+ int rc = RTHandleGetStandard(RTHANDLESTD_INPUT, &Handle);
+ if (RT_SUCCESS(rc))
+ {
+ if (Handle.enmType == RTHANDLETYPE_PIPE)
+ {
+ uint8_t abSecretKey[RT_SIZEOFMEMB(VBOXSERVICECTRLSESSIONTHREAD, abKey)];
+ rc = RTPipeReadBlocking(Handle.u.hPipe, abSecretKey, sizeof(abSecretKey), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "Got secret key from standard input.\n");
+
+ /*
+ * Do the accepting, if appropriate.
+ */
+ if (g_fControlSupportsOptimizations)
+ {
+ rc = VbglR3GuestCtrlSessionAccept(idClient, idSession, abSecretKey, sizeof(abSecretKey));
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "Session %u accepted (client ID %u)\n", idClient, idSession);
+ else
+ VGSvcError("Failed to accept session %u (client ID %u): %Rrc\n", idClient, idSession, rc);
+ }
+ else
+ {
+ /* For legacy hosts, we do the filtering thingy. */
+ rc = VbglR3GuestCtrlMsgFilterSet(idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
+ VBOX_GUESTCTRL_FILTER_BY_SESSION(idSession), 0);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "Session %u filtering successfully enabled\n", idSession);
+ else
+ VGSvcError("Failed to set session filter: %Rrc\n", rc);
+ }
+ }
+ else
+ VGSvcError("Error reading secret key from standard input: %Rrc\n", rc);
+ }
+ else
+ {
+ VGSvcError("Standard input is not a pipe!\n");
+ rc = VERR_INVALID_HANDLE;
+ }
+ RTHandleClose(&Handle);
+ }
+ else
+ VGSvcError("RTHandleGetStandard failed on standard input: %Rrc\n", rc);
+ return rc;
+}
+
+/**
+ * Main message handler for the guest control session process.
+ *
+ * @returns exit code.
+ * @param pSession Pointer to g_Session.
+ * @thread main.
+ */
+static RTEXITCODE vgsvcGstCtrlSessionSpawnWorker(PVBOXSERVICECTRLSESSION pSession)
+{
+ AssertPtrReturn(pSession, RTEXITCODE_FAILURE);
+ VGSvcVerbose(0, "Hi, this is guest session ID=%RU32\n", pSession->StartupInfo.uSessionID);
+
+ /*
+ * Connect to the host service.
+ */
+ uint32_t idClient;
+ int rc = VbglR3GuestCtrlConnect(&idClient);
+ if (RT_FAILURE(rc))
+ return VGSvcError("Error connecting to guest control service, rc=%Rrc\n", rc);
+ g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(idClient);
+ g_idControlSvcClient = idClient;
+
+ rc = vgsvcGstCtrlSessionReadKeyAndAccept(idClient, pSession->StartupInfo.uSessionID);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(1, "Using client ID=%RU32\n", idClient);
+
+ /*
+ * Report started status.
+ * If session status cannot be posted to the host for some reason, bail out.
+ */
+ VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID) };
+ rc = VbglR3GuestCtrlSessionNotify(&ctx, GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate a scratch buffer for messages which also send payload data with them.
+ * This buffer may grow if the host sends us larger chunks of data.
+ */
+ uint32_t cbScratchBuf = _64K;
+ void *pvScratchBuf = RTMemAlloc(cbScratchBuf);
+ if (pvScratchBuf)
+ {
+ /*
+ * Message processing loop.
+ */
+ VBGLR3GUESTCTRLCMDCTX CtxHost = { idClient, 0 /* Context ID */, pSession->StartupInfo.uProtocol, 0 };
+ for (;;)
+ {
+ VGSvcVerbose(3, "Waiting for host msg ...\n");
+ uint32_t uMsg = 0;
+ rc = VbglR3GuestCtrlMsgPeekWait(idClient, &uMsg, &CtxHost.uNumParms, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved (%Rrc)\n", uMsg, CtxHost.uNumParms, rc);
+
+ /*
+ * Pass it on to the session handler.
+ * Note! Only when handling HOST_SESSION_CLOSE is the rc used.
+ */
+ bool fShutdown = false;
+ rc = VGSvcGstCtrlSessionHandler(pSession, uMsg, &CtxHost, &pvScratchBuf, &cbScratchBuf, &fShutdown);
+ if (fShutdown)
+ break;
+ }
+ else /** @todo Shouldn't we have a plan for handling connection loss and such? Now, we'll just spin like crazy. */
+ VGSvcVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
+
+ /* Let others run (guests are often single CPU) ... */
+ RTThreadYield();
+ }
+
+ /*
+ * Shutdown.
+ */
+ RTMemFree(pvScratchBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ VGSvcVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID);
+ }
+ else
+ VGSvcError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
+ }
+ else
+ VGSvcError("Setting message filterAdd=0x%x failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
+
+ VGSvcVerbose(3, "Disconnecting client ID=%RU32 ...\n", idClient);
+ VbglR3GuestCtrlDisconnect(idClient);
+ g_idControlSvcClient = 0;
+
+ VGSvcVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Finds a (formerly) started guest process given by its PID and increases its
+ * reference count.
+ *
+ * Must be decreased by the caller with VGSvcGstCtrlProcessRelease().
+ *
+ * @returns Guest process if found, otherwise NULL.
+ * @param pSession Pointer to guest session where to search process in.
+ * @param uPID PID to search for.
+ *
+ * @note This does *not lock the process!
+ */
+PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID)
+{
+ AssertPtrReturn(pSession, NULL);
+
+ PVBOXSERVICECTRLPROCESS pProcess = NULL;
+ int rc = RTCritSectEnter(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICECTRLPROCESS pCurProcess;
+ RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
+ {
+ if (pCurProcess->uPID == uPID)
+ {
+ rc = RTCritSectEnter(&pCurProcess->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pCurProcess->cRefs++;
+ rc = RTCritSectLeave(&pCurProcess->CritSect);
+ AssertRC(rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ pProcess = pCurProcess;
+ break;
+ }
+ }
+
+ rc = RTCritSectLeave(&pSession->CritSect);
+ AssertRC(rc);
+ }
+
+ return pProcess;
+}
+
+
+int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
+ VGSvcVerbose(0, "Session %RU32 is about to close ...\n", pSession->StartupInfo.uSessionID);
+
+ int rc = RTCritSectEnter(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close all guest processes.
+ */
+ VGSvcVerbose(0, "Stopping all guest processes ...\n");
+
+ /* Signal all guest processes in the active list that we want to shutdown. */
+ size_t cProcesses = 0;
+ PVBOXSERVICECTRLPROCESS pProcess;
+ RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
+ {
+ VGSvcGstCtrlProcessStop(pProcess);
+ cProcesses++;
+ }
+
+ VGSvcVerbose(1, "%zu guest processes were signalled to stop\n", cProcesses);
+
+ /* Wait for all active threads to shutdown and destroy the active thread list. */
+ pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node);
+ while (pProcess)
+ {
+ PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
+ bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node);
+
+ int rc2 = RTCritSectLeave(&pSession->CritSect);
+ AssertRC(rc2);
+
+ rc2 = VGSvcGstCtrlProcessWait(pProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
+
+ int rc3 = RTCritSectEnter(&pSession->CritSect);
+ AssertRC(rc3);
+
+ if (RT_SUCCESS(rc2))
+ VGSvcGstCtrlProcessFree(pProcess);
+
+ if (fLast)
+ break;
+
+ pProcess = pNext;
+ }
+
+#ifdef DEBUG
+ pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node);
+ while (pProcess)
+ {
+ PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
+ bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node);
+
+ VGSvcVerbose(1, "Process %p (PID %RU32) still in list\n", pProcess, pProcess->uPID);
+ if (fLast)
+ break;
+
+ pProcess = pNext;
+ }
+#endif
+ AssertMsg(RTListIsEmpty(&pSession->lstProcesses),
+ ("Guest process list still contains entries when it should not\n"));
+
+ /*
+ * Close all left guest files.
+ */
+ VGSvcVerbose(0, "Closing all guest files ...\n");
+
+ PVBOXSERVICECTRLFILE pFile;
+ pFile = RTListGetFirst(&pSession->lstFiles, VBOXSERVICECTRLFILE, Node);
+ while (pFile)
+ {
+ PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node);
+ bool fLast = RTListNodeIsLast(&pSession->lstFiles, &pFile->Node);
+
+ int rc2 = vgsvcGstCtrlSessionFileDestroy(pFile);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Unable to close file '%s'; rc=%Rrc\n", pFile->szName, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ /* Keep going. */
+ }
+
+ if (fLast)
+ break;
+
+ pFile = pNext;
+ }
+
+ AssertMsg(RTListIsEmpty(&pSession->lstFiles), ("Guest file list still contains entries when it should not\n"));
+
+ int rc2 = RTCritSectLeave(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return rc;
+}
+
+
+int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
+ int rc = VGSvcGstCtrlSessionClose(pSession);
+
+ /* Destroy critical section. */
+ RTCritSectDelete(&pSession->CritSect);
+
+ return rc;
+}
+
+
+int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
+ RTListInit(&pSession->lstProcesses);
+ RTListInit(&pSession->lstFiles);
+
+ pSession->fFlags = fFlags;
+
+ /* Init critical section for protecting the thread lists. */
+ int rc = RTCritSectInit(&pSession->CritSect);
+ AssertRC(rc);
+
+ return rc;
+}
+
+
+/**
+ * Adds a guest process to a session's process list.
+ *
+ * @return VBox status code.
+ * @param pSession Guest session to add process to.
+ * @param pProcess Guest process to add.
+ */
+int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose( 3, "Adding process (PID %RU32) to session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
+
+ /* Add process to session list. */
+ RTListAppend(&pSession->lstProcesses, &pProcess->Node);
+
+ int rc2 = RTCritSectLeave(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Removes a guest process from a session's process list.
+ *
+ * @return VBox status code.
+ * @param pSession Guest session to remove process from.
+ * @param pProcess Guest process to remove.
+ */
+int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
+ Assert(pProcess->cRefs == 0);
+
+ RTListNodeRemove(&pProcess->Node);
+
+ int rc2 = RTCritSectLeave(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Determines whether starting a new guest process according to the
+ * maximum number of concurrent guest processes defined is allowed or not.
+ *
+ * @return VBox status code.
+ * @param pSession The guest session.
+ * @param pbAllowed True if starting (another) guest process
+ * is allowed, false if not.
+ */
+int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pbAllowed)
+{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+ AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check if we're respecting our memory policy by checking
+ * how many guest processes are started and served already.
+ */
+ bool fLimitReached = false;
+ if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
+ {
+ uint32_t uProcsRunning = 0;
+ PVBOXSERVICECTRLPROCESS pProcess;
+ RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
+ uProcsRunning++;
+
+ VGSvcVerbose(3, "Maximum served guest processes set to %u, running=%u\n", pSession->uProcsMaxKept, uProcsRunning);
+
+ int32_t iProcsLeft = (pSession->uProcsMaxKept - uProcsRunning - 1);
+ if (iProcsLeft < 0)
+ {
+ VGSvcVerbose(3, "Maximum running guest processes reached (%u)\n", pSession->uProcsMaxKept);
+ fLimitReached = true;
+ }
+ }
+
+ *pbAllowed = !fLimitReached;
+
+ int rc2 = RTCritSectLeave(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Creates the process for a guest session.
+ *
+ * @return VBox status code.
+ * @param pSessionStartupInfo Session startup info.
+ * @param pSessionThread The session thread under construction.
+ * @param uCtrlSessionThread The session thread debug ordinal.
+ */
+static int vgsvcVGSvcGstCtrlSessionThreadCreateProcess(const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
+ PVBOXSERVICECTRLSESSIONTHREAD pSessionThread, uint32_t uCtrlSessionThread)
+{
+ RT_NOREF(uCtrlSessionThread);
+
+ /*
+ * Is this an anonymous session? Anonymous sessions run with the same
+ * privileges as the main VBoxService executable.
+ */
+ bool const fAnonymous = pSessionThread->StartupInfo.szUser[0] == '\0';
+ if (fAnonymous)
+ {
+ Assert(!strlen(pSessionThread->StartupInfo.szPassword));
+ Assert(!strlen(pSessionThread->StartupInfo.szDomain));
+
+ VGSvcVerbose(3, "New anonymous guest session ID=%RU32 created, fFlags=%x, using protocol %RU32\n",
+ pSessionStartupInfo->uSessionID,
+ pSessionStartupInfo->fFlags,
+ pSessionStartupInfo->uProtocol);
+ }
+ else
+ {
+ VGSvcVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, fFlags=%x, using protocol %RU32\n",
+ pSessionStartupInfo->uSessionID,
+ pSessionStartupInfo->szUser,
+#ifdef DEBUG
+ pSessionStartupInfo->szPassword,
+#else
+ "XXX", /* Never show passwords in release mode. */
+#endif
+ pSessionStartupInfo->szDomain,
+ pSessionStartupInfo->fFlags,
+ pSessionStartupInfo->uProtocol);
+ }
+
+ /*
+ * Spawn a child process for doing the actual session handling.
+ * Start by assembling the argument list.
+ */
+ char szExeName[RTPATH_MAX];
+ char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
+ AssertReturn(pszExeName, VERR_FILENAME_TOO_LONG);
+
+ char szParmSessionID[32];
+ RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->StartupInfo.uSessionID);
+
+ char szParmSessionProto[32];
+ RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
+ pSessionThread->StartupInfo.uProtocol);
+#ifdef DEBUG
+ char szParmThreadId[32];
+ RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", uCtrlSessionThread);
+#endif
+ unsigned idxArg = 0; /* Next index in argument vector. */
+ char const *apszArgs[24];
+
+ apszArgs[idxArg++] = pszExeName;
+ apszArgs[idxArg++] = "guestsession";
+ apszArgs[idxArg++] = szParmSessionID;
+ apszArgs[idxArg++] = szParmSessionProto;
+#ifdef DEBUG
+ apszArgs[idxArg++] = szParmThreadId;
+#endif
+ if (!fAnonymous) /* Do we need to pass a user name? */
+ {
+ apszArgs[idxArg++] = "--user";
+ apszArgs[idxArg++] = pSessionThread->StartupInfo.szUser;
+
+ if (strlen(pSessionThread->StartupInfo.szDomain))
+ {
+ apszArgs[idxArg++] = "--domain";
+ apszArgs[idxArg++] = pSessionThread->StartupInfo.szDomain;
+ }
+ }
+
+ /* Add same verbose flags as parent process. */
+ char szParmVerbose[32];
+ if (g_cVerbosity > 0)
+ {
+ unsigned cVs = RT_MIN(g_cVerbosity, RT_ELEMENTS(szParmVerbose) - 2);
+ szParmVerbose[0] = '-';
+ memset(&szParmVerbose[1], 'v', cVs);
+ szParmVerbose[1 + cVs] = '\0';
+ apszArgs[idxArg++] = szParmVerbose;
+ }
+
+ /* Add log file handling. Each session will have an own
+ * log file, naming based on the parent log file. */
+ char szParmLogFile[sizeof(g_szLogFile) + 128];
+ if (g_szLogFile[0])
+ {
+ const char *pszSuffix = RTPathSuffix(g_szLogFile);
+ if (!pszSuffix)
+ pszSuffix = strchr(g_szLogFile, '\0');
+ size_t cchBase = pszSuffix - g_szLogFile;
+#ifndef DEBUG
+ RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%s%s",
+ cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, pszSuffix);
+#else
+ RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%RU32-%s%s",
+ cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, uCtrlSessionThread,
+ pSessionStartupInfo->szUser, pszSuffix);
+#endif
+ apszArgs[idxArg++] = "--logfile";
+ apszArgs[idxArg++] = szParmLogFile;
+ }
+
+#ifdef DEBUG
+ if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
+ apszArgs[idxArg++] = "--dump-stdout";
+ if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
+ apszArgs[idxArg++] = "--dump-stderr";
+#endif
+ apszArgs[idxArg] = NULL;
+ Assert(idxArg < RT_ELEMENTS(apszArgs));
+
+ if (g_cVerbosity > 3)
+ {
+ VGSvcVerbose(4, "Spawning parameters:\n");
+ for (idxArg = 0; apszArgs[idxArg]; idxArg++)
+ VGSvcVerbose(4, " %s\n", apszArgs[idxArg]);
+ }
+
+ /*
+ * Flags.
+ */
+ uint32_t const fProcCreate = RTPROC_FLAGS_PROFILE
+#ifdef RT_OS_WINDOWS
+ | RTPROC_FLAGS_SERVICE
+ | RTPROC_FLAGS_HIDDEN
+#endif
+ ;
+
+ /*
+ * Configure standard handles.
+ */
+ RTHANDLE hStdIn;
+ int rc = RTPipeCreate(&hStdIn.u.hPipe, &pSessionThread->hKeyPipe, RTPIPE_C_INHERIT_READ);
+ if (RT_SUCCESS(rc))
+ {
+ hStdIn.enmType = RTHANDLETYPE_PIPE;
+
+ RTHANDLE hStdOutAndErr;
+ rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
+
+ /*
+ * Windows: If a domain name is given, construct an UPN (User Principle Name)
+ * with the domain name built-in, e.g. "joedoe@example.com".
+ */
+ const char *pszUser = pSessionThread->StartupInfo.szUser;
+#ifdef RT_OS_WINDOWS
+ char *pszUserUPN = NULL;
+ if (pSessionThread->StartupInfo.szDomain[0])
+ {
+ int cchbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s",
+ pSessionThread->StartupInfo.szUser,
+ pSessionThread->StartupInfo.szDomain);
+ if (cchbUserUPN > 0)
+ {
+ pszUser = pszUserUPN;
+ VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+#endif
+ {
+ /*
+ * Finally, create the process.
+ */
+ rc = RTProcCreateEx(pszExeName, apszArgs, RTENV_DEFAULT, fProcCreate,
+ &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
+ !fAnonymous ? pszUser : NULL,
+ !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL,
+ &pSessionThread->hProcess);
+ }
+#ifdef RT_OS_WINDOWS
+ RTStrFree(pszUserUPN);
+#endif
+ RTFileClose(hStdOutAndErr.u.hFile);
+ }
+
+ RTPipeClose(hStdIn.u.hPipe);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a guest session.
+ *
+ * This will spawn a new VBoxService.exe instance under behalf of the given user
+ * which then will act as a session host. On successful open, the session will
+ * be added to the given session thread list.
+ *
+ * @return VBox status code.
+ * @param pList Which list to use to store the session thread in.
+ * @param pSessionStartupInfo Session startup info.
+ * @param ppSessionThread Returns newly created session thread on success.
+ * Optional.
+ */
+int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
+ PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
+{
+ AssertPtrReturn(pList, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
+ /* ppSessionThread is optional. */
+
+#ifdef VBOX_STRICT
+ /* Check for existing session in debug mode. Should never happen because of
+ * Main consistency. */
+ PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
+ RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
+ {
+ AssertMsgReturn(pSessionCur->StartupInfo.uSessionID != pSessionStartupInfo->uSessionID,
+ ("Guest session thread ID=%RU32 (%p) already exists when it should not\n",
+ pSessionCur->StartupInfo.uSessionID, pSessionCur), VERR_ALREADY_EXISTS);
+ }
+#endif
+
+ /* Static counter to help tracking session thread <-> process relations. */
+ static uint32_t s_uCtrlSessionThread = 0;
+#if 1
+ if (++s_uCtrlSessionThread == 100000)
+#else /* This must be some joke, right? ;-) */
+ if (s_uCtrlSessionThread++ == UINT32_MAX)
+#endif
+ s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */
+
+ /*
+ * Allocate and initialize the session thread structure.
+ */
+ int rc;
+ PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(*pSessionThread));
+ if (pSessionThread)
+ {
+ //pSessionThread->fShutdown = false;
+ //pSessionThread->fStarted = false;
+ //pSessionThread->fStopped = false;
+ pSessionThread->hKeyPipe = NIL_RTPIPE;
+ pSessionThread->Thread = NIL_RTTHREAD;
+ pSessionThread->hProcess = NIL_RTPROCESS;
+
+ /* Copy over session startup info. */
+ memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo, sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
+
+ /* Generate the secret key. */
+ RTRandBytes(pSessionThread->abKey, sizeof(pSessionThread->abKey));
+
+ rc = RTCritSectInit(&pSessionThread->CritSect);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Give the session key to the host so it can validate the client.
+ */
+ if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
+ {
+ for (uint32_t i = 0; i < 10; i++)
+ {
+ rc = VbglR3GuestCtrlSessionPrepare(g_idControlSvcClient, pSessionStartupInfo->uSessionID,
+ pSessionThread->abKey, sizeof(pSessionThread->abKey));
+ if (rc != VERR_OUT_OF_RESOURCES)
+ break;
+ RTThreadSleep(100);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start the session child process.
+ */
+ rc = vgsvcVGSvcGstCtrlSessionThreadCreateProcess(pSessionStartupInfo, pSessionThread, s_uCtrlSessionThread);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start the session thread.
+ */
+ rc = RTThreadCreateF(&pSessionThread->Thread, vgsvcGstCtrlSessionThread, pSessionThread /*pvUser*/, 0 /*cbStack*/,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CtrlSess%u", s_uCtrlSessionThread);
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait for the thread to initialize. */
+ rc = RTThreadUserWait(pSessionThread->Thread, RT_MS_1MIN);
+ if ( RT_SUCCESS(rc)
+ && !ASMAtomicReadBool(&pSessionThread->fShutdown))
+ {
+ VGSvcVerbose(2, "Thread for session ID=%RU32 started\n", pSessionThread->StartupInfo.uSessionID);
+
+ ASMAtomicXchgBool(&pSessionThread->fStarted, true);
+
+ /* Add session to list. */
+ RTListAppend(pList, &pSessionThread->Node);
+ if (ppSessionThread) /* Return session if wanted. */
+ *ppSessionThread = pSessionThread;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Bail out.
+ */
+ VGSvcError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
+ pSessionThread->StartupInfo.uSessionID, rc);
+ if (RT_SUCCESS_NP(rc))
+ rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
+ }
+ else
+ VGSvcError("Creating session thread failed, rc=%Rrc\n", rc);
+
+ RTProcTerminate(pSessionThread->hProcess);
+ uint32_t cMsWait = 1;
+ while ( RTProcWait(pSessionThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL) == VERR_PROCESS_RUNNING
+ && cMsWait <= 9) /* 1023 ms */
+ {
+ RTThreadSleep(cMsWait);
+ cMsWait <<= 1;
+ }
+ }
+
+ if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
+ VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, pSessionStartupInfo->uSessionID);
+ }
+ else
+ VGSvcVerbose(3, "VbglR3GuestCtrlSessionPrepare failed: %Rrc\n", rc);
+ RTPipeClose(pSessionThread->hKeyPipe);
+ pSessionThread->hKeyPipe = NIL_RTPIPE;
+ RTCritSectDelete(&pSessionThread->CritSect);
+ }
+ RTMemFree(pSessionThread);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ VGSvcVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Waits for a formerly opened guest session process to close.
+ *
+ * @return VBox status code.
+ * @param pThread Guest session thread to wait for.
+ * @param uTimeoutMS Waiting timeout (in ms).
+ * @param fFlags Closing flags.
+ */
+int VGSvcGstCtrlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uTimeoutMS, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+ /** @todo Validate closing flags. */
+
+ AssertMsgReturn(pThread->Thread != NIL_RTTHREAD,
+ ("Guest session thread of session %p does not exist when it should\n", pThread),
+ VERR_NOT_FOUND);
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * The spawned session process should have received the same closing request,
+ * so just wait for the process to close.
+ */
+ if (ASMAtomicReadBool(&pThread->fStarted))
+ {
+ /* Ask the thread to shutdown. */
+ ASMAtomicXchgBool(&pThread->fShutdown, true);
+
+ VGSvcVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
+ pThread->StartupInfo.uSessionID, uTimeoutMS);
+
+ int rcThread;
+ rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rcThread);
+ else
+ VGSvcError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rc);
+ }
+
+ return rc;
+}
+
+/**
+ * Waits for the specified session thread to end and remove
+ * it from the session thread list.
+ *
+ * @return VBox status code.
+ * @param pThread Session thread to destroy.
+ * @param fFlags Closing flags.
+ */
+int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t fFlags)
+{
+ AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+
+ int rc = VGSvcGstCtrlSessionThreadWait(pThread, 5 * 60 * 1000 /* 5 minutes timeout */, fFlags);
+
+ /* Remove session from list and destroy object. */
+ RTListNodeRemove(&pThread->Node);
+
+ RTMemFree(pThread);
+ pThread = NULL;
+
+ return rc;
+}
+
+/**
+ * Close all open guest session threads.
+ *
+ * @note Caller is responsible for locking!
+ *
+ * @return VBox status code.
+ * @param pList Which list to close the session threads for.
+ * @param fFlags Closing flags.
+ */
+int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t fFlags)
+{
+ AssertPtrReturn(pList, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ /*int rc = VbglR3GuestCtrlClose
+ if (RT_FAILURE(rc))
+ VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
+
+ PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
+ PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
+ RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
+ {
+ int rc2 = VGSvcGstCtrlSessionThreadDestroy(pSessIt, fFlags);
+ if (RT_FAILURE(rc2))
+ {
+ VGSvcError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ /* Keep going. */
+ }
+ }
+
+ VGSvcVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Main function for the session process.
+ *
+ * @returns exit code.
+ * @param argc Argument count.
+ * @param argv Argument vector (UTF-8).
+ */
+RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--domain", VBOXSERVICESESSIONOPT_DOMAIN, RTGETOPT_REQ_STRING },
+#ifdef DEBUG
+ { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
+ { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
+#endif
+ { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
+ { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
+ { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
+ { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
+#ifdef DEBUG
+ { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
+#endif /* DEBUG */
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
+ };
+
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ uint32_t fSession = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
+
+ /* Protocol and session ID must be specified explicitly. */
+ g_Session.StartupInfo.uProtocol = UINT32_MAX;
+ g_Session.StartupInfo.uSessionID = UINT32_MAX;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case VBOXSERVICESESSIONOPT_DOMAIN:
+ /* Information not needed right now, skip. */
+ break;
+#ifdef DEBUG
+ case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
+ fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
+ break;
+
+ case VBOXSERVICESESSIONOPT_DUMP_STDERR:
+ fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
+ break;
+#endif
+ case VBOXSERVICESESSIONOPT_SESSION_ID:
+ g_Session.StartupInfo.uSessionID = ValueUnion.u32;
+ break;
+
+ case VBOXSERVICESESSIONOPT_SESSION_PROTO:
+ g_Session.StartupInfo.uProtocol = ValueUnion.u32;
+ break;
+#ifdef DEBUG
+ case VBOXSERVICESESSIONOPT_THREAD_ID:
+ /* Not handled. Mainly for processs listing. */
+ break;
+#endif
+ case VBOXSERVICESESSIONOPT_LOG_FILE:
+ {
+ int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
+ break;
+ }
+
+ case VBOXSERVICESESSIONOPT_USERNAME:
+ /* Information not needed right now, skip. */
+ break;
+
+ /** @todo Implement help? */
+
+ case 'v':
+ g_cVerbosity++;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* Ignore; might be "guestsession" main command. */
+ /** @todo r=bird: We DO NOT ignore stuff on the command line! */
+ break;
+
+ default:
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown command '%s'", ValueUnion.psz);
+ }
+ }
+
+ /* Check that we've got all the required options. */
+ if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
+
+ if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
+
+ /* Init the session object. */
+ int rc = VGSvcGstCtrlSessionInit(&g_Session, fSession);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
+
+ rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file '%s', rc=%Rrc\n",
+ g_szLogFile[0] ? g_szLogFile : "<None>", rc);
+
+ RTEXITCODE rcExit = vgsvcGstCtrlSessionSpawnWorker(&g_Session);
+
+ VGSvcLogDestroy();
+ return rcExit;
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp
new file mode 100644
index 00000000..d912ec2a
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp
@@ -0,0 +1,644 @@
+/* $Id: VBoxServiceCpuHotPlug.cpp $ */
+/** @file
+ * VBoxService - Guest Additions CPU Hot-Plugging Service.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vgsvc_cpuhotplug VBoxService - CPU Hot-Plugging
+ *
+ * The CPU Hot-Plugging subservice helps execute and coordinate CPU hot-plugging
+ * between the guest OS and the VMM.
+ *
+ * CPU Hot-Plugging is useful for reallocating CPU resources from one VM to
+ * other VMs or/and the host. It talks to the VMM via VMMDev, new hot-plugging
+ * events being signalled with an interrupt (no polling).
+ *
+ * Currently only supported for linux guests.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+
+#ifdef RT_OS_LINUX
+# include <iprt/linux/sysfs.h>
+# include <errno.h> /* For the sysfs API */
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef RT_OS_LINUX
+
+/** @name Paths to access the CPU device
+ * @{
+ */
+# define SYSFS_ACPI_CPU_PATH "/sys/devices"
+# define SYSFS_CPU_PATH "/sys/devices/system/cpu"
+/** @} */
+
+/** Path component for the ACPI CPU path. */
+typedef struct SYSFSCPUPATHCOMP
+{
+ /** Flag whether the name is suffixed with a number */
+ bool fNumberedSuffix;
+ /** Name of the component */
+ const char *pcszName;
+} SYSFSCPUPATHCOMP, *PSYSFSCPUPATHCOMP;
+/** Pointer to a const component. */
+typedef const SYSFSCPUPATHCOMP *PCSYSFSCPUPATHCOMP;
+
+/**
+ * Structure which defines how the entries are assembled.
+ */
+typedef struct SYSFSCPUPATH
+{
+ /** Id when probing for the correct path. */
+ uint32_t uId;
+ /** Array holding the possible components. */
+ PCSYSFSCPUPATHCOMP aComponentsPossible;
+ /** Number of entries in the array, excluding the terminator. */
+ unsigned cComponents;
+ /** Directory handle */
+ RTDIR hDir;
+ /** Current directory to try. */
+ char *pszPath;
+} SYSFSCPUPATH, *PSYSFSCPUPATH;
+
+/** Content of uId if the path wasn't probed yet. */
+# define ACPI_CPU_PATH_NOT_PROBED UINT32_MAX
+#endif /* RT_OS_LINUX*/
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef RT_OS_LINUX
+/** Possible combinations of all path components for level 1. */
+static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl1[] =
+{
+ /** LNXSYSTEM:<id> */
+ { true, "LNXSYSTM:*" }
+};
+
+/** Possible combinations of all path components for level 2. */
+static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl2[] =
+{
+ /** device:<id> */
+ { true, "device:*" },
+ /** LNXSYBUS:<id> */
+ { true, "LNXSYBUS:*" }
+};
+
+/** Possible combinations of all path components for level 3 */
+static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl3[] =
+{
+ /** ACPI0004:<id> */
+ { true, "ACPI0004:*" }
+};
+
+/** Possible combinations of all path components for level 4 */
+static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl4[] =
+{
+ /** LNXCPU:<id> */
+ { true, "LNXCPU:*" },
+ /** ACPI_CPU:<id> */
+ { true, "ACPI_CPU:*" }
+};
+
+/** All possible combinations. */
+static SYSFSCPUPATH g_aAcpiCpuPath[] =
+{
+ /** Level 1 */
+ { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl1, RT_ELEMENTS(g_aAcpiCpuPathLvl1), NULL, NULL },
+ /** Level 2 */
+ { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl2, RT_ELEMENTS(g_aAcpiCpuPathLvl2), NULL, NULL },
+ /** Level 3 */
+ { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl3, RT_ELEMENTS(g_aAcpiCpuPathLvl3), NULL, NULL },
+ /** Level 4 */
+ { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl4, RT_ELEMENTS(g_aAcpiCpuPathLvl4), NULL, NULL },
+};
+
+/**
+ * Possible directories to get to the topology directory for reading core and package id.
+ *
+ * @remark: This is not part of the path above because the eject file is not in one of the directories
+ * below and would make the hot unplug code fail.
+ */
+static const char *g_apszTopologyPath[] =
+{
+ "sysdev",
+ "physical_node"
+};
+
+#endif /* RT_OS_LINUX*/
+
+
+#ifdef RT_OS_LINUX
+
+/**
+ * Probes for the correct path to the ACPI CPU object in sysfs for the
+ * various different kernel versions and distro's.
+ *
+ * @returns VBox status code.
+ */
+static int vgsvcCpuHotPlugProbePath(void)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Probe for the correct path if we didn't already. */
+ if (RT_UNLIKELY(g_aAcpiCpuPath[0].uId == ACPI_CPU_PATH_NOT_PROBED))
+ {
+ char *pszPath = NULL; /** < Current path, increasing while we dig deeper. */
+
+ pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
+ if (!pszPath)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Simple algorithm to find the path.
+ * Performance is not a real problem because it is
+ * only executed once.
+ */
+ for (unsigned iLvlCurr = 0; iLvlCurr < RT_ELEMENTS(g_aAcpiCpuPath); iLvlCurr++)
+ {
+ PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
+
+ for (unsigned iCompCurr = 0; iCompCurr < pAcpiCpuPathLvl->cComponents; iCompCurr++)
+ {
+ PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[iCompCurr];
+
+ /* Open the directory */
+ RTDIR hDirCurr = NIL_RTDIR;
+ char *pszPathTmp = RTPathJoinA(pszPath, pPathComponent->pcszName);
+ if (pszPathTmp)
+ {
+ rc = RTDirOpenFiltered(&hDirCurr, pszPathTmp, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ RTStrFree(pszPathTmp);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Search if the current directory contains one of the possible parts. */
+ size_t cchName = strlen(pPathComponent->pcszName);
+ RTDIRENTRY DirFolderContent;
+ bool fFound = false;
+
+ /* Get rid of the * filter which is in the path component. */
+ if (pPathComponent->fNumberedSuffix)
+ cchName--;
+
+ while (RT_SUCCESS(RTDirRead(hDirCurr, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
+ {
+ if ( DirFolderContent.cbName >= cchName
+ && !strncmp(DirFolderContent.szName, pPathComponent->pcszName, cchName))
+ {
+ /* Found, use the complete name to dig deeper. */
+ fFound = true;
+ pAcpiCpuPathLvl->uId = iCompCurr;
+ char *pszPathLvl = RTPathJoinA(pszPath, DirFolderContent.szName);
+ if (pszPathLvl)
+ {
+ RTStrFree(pszPath);
+ pszPath = pszPathLvl;
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+ }
+ RTDirClose(hDirCurr);
+
+ if (fFound)
+ break;
+ } /* For every possible component. */
+
+ /* No matching component for this part, no need to continue */
+ if (RT_FAILURE(rc))
+ break;
+ } /* For every level */
+
+ VGSvcVerbose(1, "Final path after probing %s rc=%Rrc\n", pszPath, rc);
+ RTStrFree(pszPath);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Returns the path of the ACPI CPU device with the given core and package ID.
+ *
+ * @returns VBox status code.
+ * @param ppszPath Where to store the path.
+ * @param idCpuCore The core ID of the CPU.
+ * @param idCpuPackage The package ID of the CPU.
+ */
+static int vgsvcCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage)
+{
+ int rc = VINF_SUCCESS;
+
+ AssertPtrReturn(ppszPath, VERR_INVALID_PARAMETER);
+
+ rc = vgsvcCpuHotPlugProbePath();
+ if (RT_SUCCESS(rc))
+ {
+ /* Build the path from all components. */
+ bool fFound = false;
+ unsigned iLvlCurr = 0;
+ char *pszPath = NULL;
+ char *pszPathDir = NULL;
+ PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
+
+ /* Init everything. */
+ Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);
+ pszPath = RTPathJoinA(SYSFS_ACPI_CPU_PATH, pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId].pcszName);
+ if (!pszPath)
+ return VERR_NO_STR_MEMORY;
+
+ pAcpiCpuPathLvl->pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
+ if (!pAcpiCpuPathLvl->pszPath)
+ {
+ RTStrFree(pszPath);
+ return VERR_NO_STR_MEMORY;
+ }
+
+ /* Open the directory */
+ rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->hDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszPath);
+
+ /* Search for CPU */
+ while (!fFound)
+ {
+ /* Get the next directory. */
+ RTDIRENTRY DirFolderContent;
+ rc = RTDirRead(pAcpiCpuPathLvl->hDir, &DirFolderContent, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create the new path. */
+ char *pszPathCurr = RTPathJoinA(pAcpiCpuPathLvl->pszPath, DirFolderContent.szName);
+ if (!pszPathCurr)
+ {
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+
+ /* If this is the last level check for the given core and package id. */
+ if (iLvlCurr == RT_ELEMENTS(g_aAcpiCpuPath) - 1)
+ {
+ /* Get the sysdev */
+ uint32_t idCore = 0;
+ uint32_t idPackage = 0;
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apszTopologyPath); i++)
+ {
+ int64_t i64Core = 0;
+ int64_t i64Package = 0;
+
+ int rc2 = RTLinuxSysFsReadIntFile(10, &i64Core, "%s/%s/topology/core_id",
+ pszPathCurr, g_apszTopologyPath[i]);
+ if (RT_SUCCESS(rc2))
+ rc2 = RTLinuxSysFsReadIntFile(10, &i64Package, "%s/%s/topology/physical_package_id",
+ pszPathCurr, g_apszTopologyPath[i]);
+
+ if (RT_SUCCESS(rc2))
+ {
+ idCore = (uint32_t)i64Core;
+ idPackage = (uint32_t)i64Package;
+ break;
+ }
+ }
+
+ if ( idCore == idCpuCore
+ && idPackage == idCpuPackage)
+ {
+ /* Return the path */
+ pszPath = pszPathCurr;
+ fFound = true;
+ VGSvcVerbose(3, "CPU found\n");
+ break;
+ }
+ else
+ {
+ /* Get the next directory. */
+ RTStrFree(pszPathCurr);
+ VGSvcVerbose(3, "CPU doesn't match, next directory\n");
+ }
+ }
+ else
+ {
+ /* Go deeper */
+ iLvlCurr++;
+
+ VGSvcVerbose(3, "Going deeper (iLvlCurr=%u)\n", iLvlCurr);
+
+ pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
+
+ Assert(pAcpiCpuPathLvl->hDir == NIL_RTDIR);
+ Assert(!pAcpiCpuPathLvl->pszPath);
+ pAcpiCpuPathLvl->pszPath = pszPathCurr;
+ PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId];
+
+ Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);
+
+ pszPathDir = RTPathJoinA(pszPathCurr, pPathComponent->pcszName);
+ if (!pszPathDir)
+ {
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+
+ VGSvcVerbose(3, "New path %s\n", pszPathDir);
+
+ /* Open the directory */
+ rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->hDir, pszPathDir, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ else
+ {
+ /* Go back one level and try to get the next entry. */
+ Assert(iLvlCurr > 0);
+
+ RTDirClose(pAcpiCpuPathLvl->hDir);
+ RTStrFree(pAcpiCpuPathLvl->pszPath);
+ pAcpiCpuPathLvl->hDir = NIL_RTDIR;
+ pAcpiCpuPathLvl->pszPath = NULL;
+
+ iLvlCurr--;
+ pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
+ VGSvcVerbose(3, "Directory not found, going back (iLvlCurr=%u)\n", iLvlCurr);
+ }
+ } /* while not found */
+ } /* Successful init */
+
+ /* Cleanup */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aAcpiCpuPath); i++)
+ {
+ if (g_aAcpiCpuPath[i].hDir)
+ RTDirClose(g_aAcpiCpuPath[i].hDir);
+ if (g_aAcpiCpuPath[i].pszPath)
+ RTStrFree(g_aAcpiCpuPath[i].pszPath);
+ g_aAcpiCpuPath[i].hDir = NIL_RTDIR;
+ g_aAcpiCpuPath[i].pszPath = NULL;
+ }
+ if (pszPathDir)
+ RTStrFree(pszPathDir);
+ if (RT_FAILURE(rc) && pszPath)
+ RTStrFree(pszPath);
+
+ if (RT_SUCCESS(rc))
+ *ppszPath = pszPath;
+ }
+
+ return rc;
+}
+
+#endif /* RT_OS_LINUX */
+
+/**
+ * Handles VMMDevCpuEventType_Plug.
+ *
+ * @param idCpuCore The CPU core ID.
+ * @param idCpuPackage The CPU package ID.
+ */
+static void vgsvcCpuHotPlugHandlePlugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
+{
+#ifdef RT_OS_LINUX
+ /*
+ * The topology directory (containing the physical and core id properties)
+ * is not available until the CPU is online. So we just iterate over all directories
+ * and enable every CPU which is not online already.
+ * Because the directory might not be available immediately we try a few times.
+ *
+ */
+ /** @todo Maybe use udev to monitor hot-add events from the kernel */
+ bool fCpuOnline = false;
+ unsigned cTries = 5;
+
+ do
+ {
+ RTDIR hDirDevices = NULL;
+ int rc = RTDirOpen(&hDirDevices, SYSFS_CPU_PATH);
+ if (RT_SUCCESS(rc))
+ {
+ RTDIRENTRY DirFolderContent;
+ while (RT_SUCCESS(RTDirRead(hDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
+ {
+ /** @todo r-bird: This code is bringing all CPUs online; the idCpuCore and
+ * idCpuPackage parameters are unused!
+ * aeichner: These files are not available at this point unfortunately. (see comment above)
+ * bird: Yes, but isn't that easily dealt with by doing:
+ * if (matching_topology() || !have_topology_directory())
+ * bring_cpu_online()
+ * That could save you the cpu0 and cpuidle checks to.
+ */
+ /*
+ * Check if this is a CPU object.
+ * cpu0 is excluded because it is not possible to change the state
+ * of the first CPU on Linux (it doesn't even have an online file)
+ * and cpuidle is no CPU device. Prevents error messages later.
+ */
+ if( !strncmp(DirFolderContent.szName, "cpu", 3)
+ && strncmp(DirFolderContent.szName, "cpu0", 4)
+ && strncmp(DirFolderContent.szName, "cpuidle", 7))
+ {
+ /* Get the sysdev */
+ RTFILE hFileCpuOnline = NIL_RTFILE;
+
+ rc = RTFileOpenF(&hFileCpuOnline, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
+ "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName);
+ if (RT_SUCCESS(rc))
+ {
+ /* Write a 1 to online the CPU */
+ rc = RTFileWrite(hFileCpuOnline, "1", 1, NULL);
+ RTFileClose(hFileCpuOnline);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(1, "CpuHotPlug: CPU %u/%u was brought online\n", idCpuPackage, idCpuCore);
+ fCpuOnline = true;
+ break;
+ }
+ /* Error means CPU not present or online already */
+ }
+ else
+ VGSvcError("CpuHotPlug: Failed to open '%s/%s/online' rc=%Rrc\n",
+ SYSFS_CPU_PATH, DirFolderContent.szName, rc);
+ }
+ }
+ RTDirClose(hDirDevices);
+ }
+ else
+ VGSvcError("CpuHotPlug: Failed to open path %s rc=%Rrc\n", SYSFS_CPU_PATH, rc);
+
+ /* Sleep a bit */
+ if (!fCpuOnline)
+ RTThreadSleep(10);
+
+ } while ( !fCpuOnline
+ && cTries-- > 0);
+#else
+# error "Port me"
+#endif
+}
+
+
+/**
+ * Handles VMMDevCpuEventType_Unplug.
+ *
+ * @param idCpuCore The CPU core ID.
+ * @param idCpuPackage The CPU package ID.
+ */
+static void vgsvcCpuHotPlugHandleUnplugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
+{
+#ifdef RT_OS_LINUX
+ char *pszCpuDevicePath = NULL;
+ int rc = vgsvcCpuHotPlugGetACPIDevicePath(&pszCpuDevicePath, idCpuCore, idCpuPackage);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hFileCpuEject;
+ rc = RTFileOpenF(&hFileCpuEject, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, "%s/eject", pszCpuDevicePath);
+ if (RT_SUCCESS(rc))
+ {
+ /* Write a 1 to eject the CPU */
+ rc = RTFileWrite(hFileCpuEject, "1", 1, NULL);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(1, "CpuHotPlug: CPU %u/%u was ejected\n", idCpuPackage, idCpuCore);
+ else
+ VGSvcError("CpuHotPlug: Failed to eject CPU %u/%u rc=%Rrc\n", idCpuPackage, idCpuCore, rc);
+
+ RTFileClose(hFileCpuEject);
+ }
+ else
+ VGSvcError("CpuHotPlug: Failed to open '%s/eject' rc=%Rrc\n", pszCpuDevicePath, rc);
+ RTStrFree(pszCpuDevicePath);
+ }
+ else
+ VGSvcError("CpuHotPlug: Failed to get CPU device path rc=%Rrc\n", rc);
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @interface_method_impl{VBOXSERVICE,pfnWorker} */
+static DECLCALLBACK(int) vgsvcCpuHotPlugWorker(bool volatile *pfShutdown)
+{
+ /*
+ * Tell the control thread that it can continue spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Enable the CPU hotplug notifier.
+ */
+ int rc = VbglR3CpuHotPlugInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * The Work Loop.
+ */
+ for (;;)
+ {
+ /* Wait for CPU hot-plugging event. */
+ uint32_t idCpuCore;
+ uint32_t idCpuPackage;
+ VMMDevCpuEventType enmEventType;
+ rc = VbglR3CpuHotPlugWaitForEvent(&enmEventType, &idCpuCore, &idCpuPackage);
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcVerbose(3, "CpuHotPlug: Event happened idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
+ idCpuCore, idCpuPackage, enmEventType);
+ switch (enmEventType)
+ {
+ case VMMDevCpuEventType_Plug:
+ vgsvcCpuHotPlugHandlePlugEvent(idCpuCore, idCpuPackage);
+ break;
+
+ case VMMDevCpuEventType_Unplug:
+ vgsvcCpuHotPlugHandleUnplugEvent(idCpuCore, idCpuPackage);
+ break;
+
+ default:
+ {
+ static uint32_t s_iErrors = 0;
+ if (s_iErrors++ < 10)
+ VGSvcError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
+ idCpuCore, idCpuPackage, enmEventType);
+ break;
+ }
+ }
+ }
+ else if (rc != VERR_INTERRUPTED && rc != VERR_TRY_AGAIN)
+ {
+ VGSvcError("CpuHotPlug: VbglR3CpuHotPlugWaitForEvent returned %Rrc\n", rc);
+ break;
+ }
+
+ if (*pfShutdown)
+ break;
+ }
+
+ VbglR3CpuHotPlugTerm();
+ return rc;
+}
+
+
+/** @interface_method_impl{VBOXSERVICE,pfnStop} */
+static DECLCALLBACK(void) vgsvcCpuHotPlugStop(void)
+{
+ VbglR3InterruptEventWaits();
+ return;
+}
+
+
+/**
+ * The 'CpuHotPlug' service description.
+ */
+VBOXSERVICE g_CpuHotPlug =
+{
+ /* pszName. */
+ "cpuhotplug",
+ /* pszDescription. */
+ "CPU hot-plugging monitor",
+ /* pszUsage. */
+ NULL,
+ /* pszOptions. */
+ NULL,
+ /* methods */
+ VGSvcDefaultPreInit,
+ VGSvcDefaultOption,
+ VGSvcDefaultInit,
+ vgsvcCpuHotPlugWorker,
+ vgsvcCpuHotPlugStop,
+ VGSvcDefaultTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h b/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h
new file mode 100644
index 00000000..dc719233
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h
@@ -0,0 +1,252 @@
+/* $Id: VBoxServiceInternal.h $ */
+/** @file
+ * VBoxService - Guest Additions Services.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceInternal_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <stdio.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <process.h> /* Needed for file version information. */
+#endif
+
+#include <iprt/list.h>
+#include <iprt/critsect.h>
+#include <iprt/path.h> /* RTPATH_MAX */
+#include <iprt/stdarg.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/HostServices/GuestControlSvc.h>
+
+/**
+ * A service descriptor.
+ */
+typedef struct
+{
+ /** The short service name. */
+ const char *pszName;
+ /** The longer service name. */
+ const char *pszDescription;
+ /** The usage options stuff for the --help screen. */
+ const char *pszUsage;
+ /** The option descriptions for the --help screen. */
+ const char *pszOptions;
+
+ /**
+ * Called before parsing arguments.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnPreInit)(void);
+
+ /**
+ * Tries to parse the given command line option.
+ *
+ * @returns 0 if we parsed, -1 if it didn't and anything else means exit.
+ * @param ppszShort If not NULL it points to the short option iterator. a short argument.
+ * If NULL examine argv[*pi].
+ * @param argc The argument count.
+ * @param argv The argument vector.
+ * @param pi The argument vector index. Update if any value(s) are eaten.
+ */
+ DECLCALLBACKMEMBER(int, pfnOption)(const char **ppszShort, int argc, char **argv, int *pi);
+
+ /**
+ * Called before parsing arguments.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnInit)(void);
+
+ /** Called from the worker thread.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if exitting because *pfShutdown was set.
+ * @param pfShutdown Pointer to a per service termination flag to check
+ * before and after blocking.
+ */
+ DECLCALLBACKMEMBER(int, pfnWorker)(bool volatile *pfShutdown);
+
+ /**
+ * Stops a service.
+ */
+ DECLCALLBACKMEMBER(void, pfnStop)(void);
+
+ /**
+ * Does termination cleanups.
+ *
+ * @remarks This may be called even if pfnInit hasn't been called!
+ */
+ DECLCALLBACKMEMBER(void, pfnTerm)(void);
+} VBOXSERVICE;
+/** Pointer to a VBOXSERVICE. */
+typedef VBOXSERVICE *PVBOXSERVICE;
+/** Pointer to a const VBOXSERVICE. */
+typedef VBOXSERVICE const *PCVBOXSERVICE;
+
+/* Default call-backs for services which do not need special behaviour. */
+DECLCALLBACK(int) VGSvcDefaultPreInit(void);
+DECLCALLBACK(int) VGSvcDefaultOption(const char **ppszShort, int argc, char **argv, int *pi);
+DECLCALLBACK(int) VGSvcDefaultInit(void);
+DECLCALLBACK(void) VGSvcDefaultTerm(void);
+
+/** The service name.
+ * @note Used on windows to name the service as well as the global mutex. */
+#define VBOXSERVICE_NAME "VBoxService"
+
+#ifdef RT_OS_WINDOWS
+/** The friendly service name. */
+# define VBOXSERVICE_FRIENDLY_NAME "VirtualBox Guest Additions Service"
+/** The service description (only W2K+ atm) */
+# define VBOXSERVICE_DESCRIPTION "Manages VM runtime information, time synchronization, guest control execution and miscellaneous utilities for guest operating systems."
+/** The following constant may be defined by including NtStatus.h. */
+# define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#endif /* RT_OS_WINDOWS */
+
+#ifdef VBOX_WITH_GUEST_PROPS
+/**
+ * A guest property cache.
+ */
+typedef struct VBOXSERVICEVEPROPCACHE
+{
+ /** The client ID for HGCM communication. */
+ uint32_t uClientID;
+ /** Head in a list of VBOXSERVICEVEPROPCACHEENTRY nodes. */
+ RTLISTANCHOR NodeHead;
+ /** Critical section for thread-safe use. */
+ RTCRITSECT CritSect;
+} VBOXSERVICEVEPROPCACHE;
+/** Pointer to a guest property cache. */
+typedef VBOXSERVICEVEPROPCACHE *PVBOXSERVICEVEPROPCACHE;
+
+/**
+ * An entry in the property cache (VBOXSERVICEVEPROPCACHE).
+ */
+typedef struct VBOXSERVICEVEPROPCACHEENTRY
+{
+ /** Node to successor.
+ * @todo r=bird: This is not really the node to the successor, but
+ * rather the OUR node in the list. If it helps, remember that
+ * its a doubly linked list. */
+ RTLISTNODE NodeSucc;
+ /** Name (and full path) of guest property. */
+ char *pszName;
+ /** The last value stored (for reference). */
+ char *pszValue;
+ /** Reset value to write if property is temporary. If NULL, it will be
+ * deleted. */
+ char *pszValueReset;
+ /** Flags. */
+ uint32_t fFlags;
+} VBOXSERVICEVEPROPCACHEENTRY;
+/** Pointer to a cached guest property. */
+typedef VBOXSERVICEVEPROPCACHEENTRY *PVBOXSERVICEVEPROPCACHEENTRY;
+
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+RT_C_DECLS_BEGIN
+
+extern char *g_pszProgName;
+extern unsigned g_cVerbosity;
+extern char g_szLogFile[RTPATH_MAX + 128];
+extern uint32_t g_DefaultInterval;
+extern VBOXSERVICE g_TimeSync;
+extern VBOXSERVICE g_Clipboard;
+extern VBOXSERVICE g_Control;
+extern VBOXSERVICE g_VMInfo;
+extern VBOXSERVICE g_CpuHotPlug;
+#ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
+extern VBOXSERVICE g_MemBalloon;
+extern VBOXSERVICE g_VMStatistics;
+#endif
+#ifdef VBOX_WITH_VBOXSERVICE_PAGE_SHARING
+extern VBOXSERVICE g_PageSharing;
+#endif
+#ifdef VBOX_WITH_SHARED_FOLDERS
+extern VBOXSERVICE g_AutoMount;
+#endif
+#ifdef DEBUG
+extern RTCRITSECT g_csLog; /* For guest process stdout dumping. */
+#endif
+
+extern RTEXITCODE VGSvcSyntax(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+extern RTEXITCODE VGSvcError(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+extern void VGSvcVerbose(unsigned iLevel, const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(2, 3);
+extern int VGSvcLogCreate(const char *pszLogFile);
+extern void VGSvcLogV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+extern void VGSvcLogDestroy(void);
+extern int VGSvcArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32,
+ uint32_t u32Min, uint32_t u32Max);
+
+/* Exposing the following bits because of windows: */
+extern int VGSvcStartServices(void);
+extern int VGSvcStopServices(void);
+extern void VGSvcMainWait(void);
+extern int VGSvcReportStatus(VBoxGuestFacilityStatus enmStatus);
+#ifdef RT_OS_WINDOWS
+extern void VGSvcWinResolveApis(void);
+extern RTEXITCODE VGSvcWinInstall(void);
+extern RTEXITCODE VGSvcWinUninstall(void);
+extern RTEXITCODE VGSvcWinEnterCtrlDispatcher(void);
+extern void VGSvcWinSetStopPendingStatus(uint32_t uCheckPoint);
+# ifdef TH32CS_SNAPHEAPLIST
+extern decltype(CreateToolhelp32Snapshot) *g_pfnCreateToolhelp32Snapshot;
+extern decltype(Process32First) *g_pfnProcess32First;
+extern decltype(Process32Next) *g_pfnProcess32Next;
+extern decltype(Module32First) *g_pfnModule32First;
+extern decltype(Module32Next) *g_pfnModule32Next;
+# endif
+extern decltype(GetSystemTimeAdjustment) *g_pfnGetSystemTimeAdjustment;
+extern decltype(SetSystemTimeAdjustment) *g_pfnSetSystemTimeAdjustment;
+# ifdef IPRT_INCLUDED_nt_nt_h
+extern decltype(ZwQuerySystemInformation) *g_pfnZwQuerySystemInformation;
+# endif
+extern ULONG (WINAPI *g_pfnGetAdaptersInfo)(struct _IP_ADAPTER_INFO *, PULONG);
+#ifdef WINSOCK_VERSION
+extern decltype(WSAStartup) *g_pfnWSAStartup;
+extern decltype(WSACleanup) *g_pfnWSACleanup;
+extern decltype(WSASocketA) *g_pfnWSASocketA;
+extern decltype(WSAIoctl) *g_pfnWSAIoctl;
+extern decltype(WSAGetLastError) *g_pfnWSAGetLastError;
+extern decltype(closesocket) *g_pfnclosesocket;
+extern decltype(inet_ntoa) *g_pfninet_ntoa;
+# endif /* WINSOCK_VERSION */
+
+#ifdef SE_INTERACTIVE_LOGON_NAME
+extern decltype(LsaNtStatusToWinError) *g_pfnLsaNtStatusToWinError;
+#endif
+
+# ifdef VBOX_WITH_GUEST_PROPS
+extern int VGSvcVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache, char **ppszUserList, uint32_t *pcUsersInList);
+extern int VGSvcVMInfoWinGetComponentVersions(uint32_t uClientID);
+# endif /* VBOX_WITH_GUEST_PROPS */
+
+#endif /* RT_OS_WINDOWS */
+
+#ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
+extern uint32_t VGSvcBalloonQueryPages(uint32_t cbPage);
+#endif
+#if defined(VBOX_WITH_VBOXSERVICE_PAGE_SHARING)
+extern RTEXITCODE VGSvcPageSharingWorkerChild(void);
+#endif
+extern int VGSvcVMInfoSignal(void);
+
+RT_C_DECLS_END
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceInternal_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp b/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp
new file mode 100644
index 00000000..885fb25e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp
@@ -0,0 +1,790 @@
+/* $Id: VBoxServicePageSharing.cpp $ */
+/** @file
+ * VBoxService - Guest page sharing.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc_pagesharing VBoxService - Page Sharing
+ *
+ * The Page Sharing subservice is responsible for finding memory mappings
+ * suitable page fusions.
+ *
+ * It is the driving force behind the Page Fusion feature in VirtualBox.
+ * Working with PGM and GMM (ring-0) thru the VMMDev interface. Every so often
+ * it reenumerates the memory mappings (executables and shared libraries) of the
+ * guest OS and reports additions and removals to GMM. For each mapping there
+ * is a filename and version as well as and address range and subsections. GMM
+ * will match the mapping with mapping with the same name and version from other
+ * VMs and see if there are any identical pages between the two.
+ *
+ * To increase the hit rate and reduce the volatility, the service launches a
+ * child process which loads all the Windows system DLLs it can. The child
+ * process is necessary as the DLLs are loaded without running the init code,
+ * and therefore not actually callable for other VBoxService code (may crash).
+ *
+ * This is currently only implemented on Windows. There is no technical reason
+ * for it not to be doable for all the other guests too, it's just a matter of
+ * customer demand and engineering time.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/ldr.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <VBox/err.h>
+#include <VBox/VMMDev.h>
+#include <VBox/VBoxGuestLib.h>
+
+#ifdef RT_OS_WINDOWS
+#include <iprt/nt/nt-and-windows.h>
+# include <tlhelp32.h>
+# include <psapi.h>
+# include <winternl.h>
+#endif
+
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+typedef struct
+{
+ AVLPVNODECORE Core;
+#ifdef RT_OS_WINDOWS
+ HMODULE hModule;
+ char szFileVersion[16];
+ MODULEENTRY32 Info;
+#endif
+} VGSVCPGSHKNOWNMOD, *PVGSVCPGSHKNOWNMOD;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
+
+static PAVLPVNODECORE g_pKnownModuleTree = NULL;
+static uint64_t g_idSession = 0;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser);
+
+
+#ifdef RT_OS_WINDOWS
+
+/**
+ * Registers a new module with the VMM
+ * @param pModule Module ptr
+ * @param fValidateMemory Validate/touch memory pages or not
+ */
+static void vgsvcPageSharingRegisterModule(PVGSVCPGSHKNOWNMOD pModule, bool fValidateMemory)
+{
+ VMMDEVSHAREDREGIONDESC aRegions[VMMDEVSHAREDREGIONDESC_MAX];
+ DWORD dwModuleSize = pModule->Info.modBaseSize;
+ BYTE *pBaseAddress = pModule->Info.modBaseAddr;
+
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule\n");
+
+ DWORD dwDummy;
+ DWORD cbVersion = GetFileVersionInfoSize(pModule->Info.szExePath, &dwDummy);
+ if (!cbVersion)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfoSize failed with %d\n", GetLastError());
+ return;
+ }
+ BYTE *pVersionInfo = (BYTE *)RTMemAllocZ(cbVersion);
+ if (!pVersionInfo)
+ return;
+
+ if (!GetFileVersionInfo(pModule->Info.szExePath, 0, cbVersion, pVersionInfo))
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfo failed with %d\n", GetLastError());
+ goto end;
+ }
+
+ /* Fetch default code page. */
+ struct LANGANDCODEPAGE
+ {
+ WORD wLanguage;
+ WORD wCodePage;
+ } *lpTranslate;
+
+ UINT cbTranslate;
+ BOOL fRet = VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &cbTranslate);
+ if ( !fRet
+ || cbTranslate < 4)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VerQueryValue failed with %d (cb=%d)\n", GetLastError(), cbTranslate);
+ goto end;
+ }
+
+ unsigned i;
+ UINT cbFileVersion;
+ char *pszFileVersion = NULL; /* Shut up MSC */
+ unsigned cTranslationBlocks = cbTranslate/sizeof(struct LANGANDCODEPAGE);
+
+ pModule->szFileVersion[0] = '\0';
+ for (i = 0; i < cTranslationBlocks; i++)
+ {
+ /* Fetch file version string. */
+ char szFileVersionLocation[256];
+
+/** @todo r=bird: Mixing ANSI and TCHAR crap again. This code is a mess. We
+ * always use the wide version of the API and convert to UTF-8/whatever. */
+
+ sprintf(szFileVersionLocation, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
+ fRet = VerQueryValue(pVersionInfo, szFileVersionLocation, (LPVOID *)&pszFileVersion, &cbFileVersion);
+ if (fRet)
+ {
+ RTStrCopy(pModule->szFileVersion, sizeof(pModule->szFileVersion), pszFileVersion);
+ break;
+ }
+ }
+ if (i == cTranslationBlocks)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: no file version found!\n");
+ goto end;
+ }
+
+ unsigned idxRegion = 0;
+
+ if (fValidateMemory)
+ {
+ do
+ {
+ MEMORY_BASIC_INFORMATION MemInfo;
+ SIZE_T cbRet = VirtualQuery(pBaseAddress, &MemInfo, sizeof(MemInfo));
+ Assert(cbRet);
+ if (!cbRet)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VirtualQueryEx failed with %d\n", GetLastError());
+ break;
+ }
+
+ if ( MemInfo.State == MEM_COMMIT
+ && MemInfo.Type == MEM_IMAGE)
+ {
+ switch (MemInfo.Protect)
+ {
+ case PAGE_EXECUTE:
+ case PAGE_EXECUTE_READ:
+ case PAGE_READONLY:
+ {
+ char *pRegion = (char *)MemInfo.BaseAddress;
+
+ /* Skip the first region as it only contains the image file header. */
+ if (pRegion != (char *)pModule->Info.modBaseAddr)
+ {
+ /* Touch all pages. */
+ while ((uintptr_t)pRegion < (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize)
+ {
+ /* Try to trick the optimizer to leave the page touching code in place. */
+ ASMProbeReadByte(pRegion);
+ pRegion += PAGE_SIZE;
+ }
+ }
+#ifdef RT_ARCH_X86
+ aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)MemInfo.BaseAddress;
+#else
+ aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)MemInfo.BaseAddress;
+#endif
+ aRegions[idxRegion].cbRegion = MemInfo.RegionSize;
+ idxRegion++;
+
+ break;
+ }
+
+ default:
+ break; /* ignore */
+ }
+ }
+
+ pBaseAddress = (BYTE *)MemInfo.BaseAddress + MemInfo.RegionSize;
+ if (dwModuleSize > MemInfo.RegionSize)
+ dwModuleSize -= MemInfo.RegionSize;
+ else
+ {
+ dwModuleSize = 0;
+ break;
+ }
+
+ if (idxRegion >= RT_ELEMENTS(aRegions))
+ break; /* out of room */
+ }
+ while (dwModuleSize);
+ }
+ else
+ {
+ /* We can't probe kernel memory ranges, so pretend it's one big region. */
+#ifdef RT_ARCH_X86
+ aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)pBaseAddress;
+#else
+ aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)pBaseAddress;
+#endif
+ aRegions[idxRegion].cbRegion = dwModuleSize;
+ idxRegion++;
+ }
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule %s %s base=%p size=%x cregions=%d\n", pModule->Info.szModule, pModule->szFileVersion, pModule->Info.modBaseAddr, pModule->Info.modBaseSize, idxRegion);
+ int rc = VbglR3RegisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (uintptr_t)pModule->Info.modBaseAddr,
+ pModule->Info.modBaseSize, idxRegion, aRegions);
+ if (RT_FAILURE(rc))
+ VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule failed with %Rrc\n", rc);
+
+end:
+ RTMemFree(pVersionInfo);
+ return;
+}
+
+
+/**
+ * Inspect all loaded modules for the specified process
+ *
+ * @param dwProcessId Process id
+ * @param ppNewTree The module tree we're assembling from modules found
+ * in this process. Modules found are moved from
+ * g_pKnownModuleTree or created new.
+ */
+static void vgsvcPageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
+{
+ /* Get a list of all the modules in this process. */
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE /* no child process handle inheritance */, dwProcessId);
+ if (hProcess == NULL)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
+ return;
+ }
+
+ HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
+ if (hSnapshot == INVALID_HANDLE_VALUE)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
+ CloseHandle(hProcess);
+ return;
+ }
+
+ VGSvcVerbose(3, "vgsvcPageSharingInspectModules\n");
+
+ MODULEENTRY32 ModuleInfo;
+ BOOL bRet;
+
+ ModuleInfo.dwSize = sizeof(ModuleInfo);
+ bRet = g_pfnModule32First(hSnapshot, &ModuleInfo);
+ do
+ {
+ /** @todo when changing this make sure VBoxService.exe is excluded! */
+ char *pszDot = strrchr(ModuleInfo.szModule, '.');
+ if ( pszDot
+ && (pszDot[1] == 'e' || pszDot[1] == 'E'))
+ continue; /* ignore executables for now. */
+
+ /* Found it before? */
+ PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
+ if (!pRec)
+ {
+ pRec = RTAvlPVRemove(&g_pKnownModuleTree, ModuleInfo.modBaseAddr);
+ if (!pRec)
+ {
+ /* New module; register it. */
+ PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
+ Assert(pModule);
+ if (!pModule)
+ break;
+
+ pModule->Info = ModuleInfo;
+ pModule->Core.Key = ModuleInfo.modBaseAddr;
+ pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
+ if (pModule->hModule)
+ vgsvcPageSharingRegisterModule(pModule, true /* validate pages */);
+
+ VGSvcVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
+ VGSvcVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
+ VGSvcVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
+ VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t) ModuleInfo.modBaseAddr );
+ VGSvcVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
+
+ pRec = &pModule->Core;
+ }
+ bool ret = RTAvlPVInsert(ppNewTree, pRec);
+ Assert(ret); NOREF(ret);
+ }
+ } while (g_pfnModule32Next(hSnapshot, &ModuleInfo));
+
+ CloseHandle(hSnapshot);
+ CloseHandle(hProcess);
+}
+
+
+/**
+ * Inspect all running processes for executables and dlls that might be worth sharing
+ * with other VMs.
+ *
+ */
+static void vgsvcPageSharingInspectGuest(void)
+{
+ VGSvcVerbose(3, "vgsvcPageSharingInspectGuest\n");
+ PAVLPVNODECORE pNewTree = NULL;
+
+ /*
+ * Check loaded modules for all running processes.
+ */
+ if ( g_pfnProcess32First
+ && g_pfnProcess32Next
+ && g_pfnModule32First
+ && g_pfnModule32Next
+ && g_pfnCreateToolhelp32Snapshot)
+ {
+ HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hSnapshot == INVALID_HANDLE_VALUE)
+ {
+ VGSvcVerbose(3, "vgsvcPageSharingInspectGuest: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
+ return;
+ }
+
+ DWORD const dwProcessId = GetCurrentProcessId();
+
+ PROCESSENTRY32 ProcessInfo;
+ ProcessInfo.dwSize = sizeof(ProcessInfo);
+ g_pfnProcess32First(hSnapshot, &ProcessInfo);
+
+ do
+ {
+ /* Skip our own process. */
+ if (ProcessInfo.th32ProcessID != dwProcessId)
+ vgsvcPageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
+ }
+ while (g_pfnProcess32Next(hSnapshot, &ProcessInfo));
+
+ CloseHandle(hSnapshot);
+ }
+
+ /*
+ * Check all loaded kernel modules.
+ */
+ if (g_pfnZwQuerySystemInformation)
+ {
+ ULONG cbBuffer = 0;
+ PVOID pBuffer = NULL;
+ PRTL_PROCESS_MODULES pSystemModules;
+
+ NTSTATUS ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer);
+ if (!cbBuffer)
+ {
+ VGSvcVerbose(1, "ZwQuerySystemInformation returned length 0\n");
+ goto skipkernelmodules;
+ }
+
+ pBuffer = RTMemAllocZ(cbBuffer);
+ if (!pBuffer)
+ goto skipkernelmodules;
+
+ ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer);
+ if (ret != STATUS_SUCCESS)
+ {
+ VGSvcVerbose(1, "ZwQuerySystemInformation returned %x (1)\n", ret);
+ goto skipkernelmodules;
+ }
+
+ pSystemModules = (PRTL_PROCESS_MODULES)pBuffer;
+ for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++)
+ {
+ VGSvcVerbose(4, "\n\n KERNEL MODULE NAME: %s", pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName] );
+ VGSvcVerbose(4, "\n executable = %s", pSystemModules->Modules[i].FullPathName );
+ VGSvcVerbose(4, "\n flags = 0x%08X\n", pSystemModules->Modules[i].Flags);
+
+ /* User-mode modules seem to have no flags set; skip them as we detected them above. */
+ if (pSystemModules->Modules[i].Flags == 0)
+ continue;
+
+ /* Found it before? */
+ PAVLPVNODECORE pRec = RTAvlPVGet(&pNewTree, pSystemModules->Modules[i].ImageBase);
+ if (!pRec)
+ {
+ pRec = RTAvlPVRemove(&g_pKnownModuleTree, pSystemModules->Modules[i].ImageBase);
+ if (!pRec)
+ {
+ /* New module; register it. */
+ char szFullFilePath[512];
+ PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
+ Assert(pModule);
+ if (!pModule)
+ break;
+
+/** @todo FullPathName not an UTF-8 string is! An ANSI string it is. */
+ strcpy(pModule->Info.szModule,
+ (const char *)&pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]);
+ GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
+
+ /* skip \Systemroot\system32 */
+ char *lpPath = strchr((char *)&pSystemModules->Modules[i].FullPathName[1], '\\');
+ if (!lpPath)
+ {
+ /* Seen just file names in XP; try to locate the file in the system32 and system32\drivers directories. */
+ strcat(szFullFilePath, "\\");
+ strcat(szFullFilePath, (const char *)pSystemModules->Modules[i].FullPathName);
+ VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
+ if (RTFileExists(szFullFilePath) == false)
+ {
+ GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
+ strcat(szFullFilePath, "\\drivers\\");
+ strcat(szFullFilePath, (const char *)pSystemModules->Modules[i].FullPathName);
+ VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
+ if (RTFileExists(szFullFilePath) == false)
+ {
+ VGSvcVerbose(1, "Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
+ RTMemFree(pModule);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ lpPath = strchr(lpPath + 1, '\\');
+ if (!lpPath)
+ {
+ VGSvcVerbose(1, "Unexpected kernel module name %s (2)\n", pSystemModules->Modules[i].FullPathName);
+ RTMemFree(pModule);
+ continue;
+ }
+
+ strcat(szFullFilePath, lpPath);
+ }
+
+ strcpy(pModule->Info.szExePath, szFullFilePath);
+ pModule->Info.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase;
+ pModule->Info.modBaseSize = pSystemModules->Modules[i].ImageSize;
+
+ pModule->Core.Key = pSystemModules->Modules[i].ImageBase;
+ vgsvcPageSharingRegisterModule(pModule, false /* don't check memory pages */);
+
+ VGSvcVerbose(3, "\n\n KERNEL MODULE NAME: %s", pModule->Info.szModule );
+ VGSvcVerbose(3, "\n executable = %s", pModule->Info.szExePath );
+ VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t)pModule->Info.modBaseAddr );
+ VGSvcVerbose(3, "\n flags = 0x%08X", pSystemModules->Modules[i].Flags);
+ VGSvcVerbose(3, "\n base size = %d", pModule->Info.modBaseSize );
+
+ pRec = &pModule->Core;
+ }
+ bool ret = RTAvlPVInsert(&pNewTree, pRec);
+ Assert(ret); NOREF(ret);
+ }
+ }
+skipkernelmodules:
+ if (pBuffer)
+ RTMemFree(pBuffer);
+ }
+
+ /* Delete leftover modules in the old tree. */
+ RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, NULL);
+
+ /* Check all registered modules. */
+ VbglR3CheckSharedModules();
+
+ /* Activate new module tree. */
+ g_pKnownModuleTree = pNewTree;
+}
+
+
+/**
+ * RTAvlPVDestroy callback.
+ */
+static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser)
+{
+ PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)pNode;
+ bool *pfUnregister = (bool *)pvUser;
+
+ VGSvcVerbose(3, "vgsvcPageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
+
+ /* Dereference module in the hypervisor. */
+ if ( !pfUnregister
+ || *pfUnregister)
+ {
+ int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion,
+ (uintptr_t)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
+ AssertRC(rc);
+ }
+
+ if (pModule->hModule)
+ FreeLibrary(pModule->hModule);
+ RTMemFree(pNode);
+ return 0;
+}
+
+
+#else /* !RT_OS_WINDOWS */
+
+static void vgsvcPageSharingInspectGuest(void)
+{
+ /** @todo other platforms */
+}
+
+#endif /* !RT_OS_WINDOWS */
+
+/** @interface_method_impl{VBOXSERVICE,pfnInit} */
+static DECLCALLBACK(int) vgsvcPageSharingInit(void)
+{
+ VGSvcVerbose(3, "vgsvcPageSharingInit\n");
+
+ int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
+ AssertRCReturn(rc, rc);
+
+#ifdef RT_OS_WINDOWS
+ rc = VbglR3GetSessionId(&g_idSession);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_IO_GEN_FAILURE)
+ VGSvcVerbose(0, "PageSharing: Page sharing support is not available by the host\n");
+ else
+ VGSvcError("vgsvcPageSharingInit: Failed with rc=%Rrc\n", rc);
+
+ rc = VERR_SERVICE_DISABLED;
+
+ RTSemEventMultiDestroy(g_PageSharingEvent);
+ g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
+
+ }
+#endif
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vgsvcPageSharingWorker(bool volatile *pfShutdown)
+{
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Now enter the loop retrieving runtime data continuously.
+ */
+ for (;;)
+ {
+ bool fEnabled = VbglR3PageSharingIsEnabled();
+ VGSvcVerbose(3, "vgsvcPageSharingWorker: enabled=%d\n", fEnabled);
+
+ if (fEnabled)
+ vgsvcPageSharingInspectGuest();
+
+ /*
+ * Block for a minute.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
+ if (*pfShutdown)
+ break;
+ if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
+ {
+ VGSvcError("vgsvcPageSharingWorker: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
+ break;
+ }
+#ifdef RT_OS_WINDOWS
+ uint64_t idNewSession = g_idSession;
+ rc = VbglR3GetSessionId(&idNewSession);
+ AssertRC(rc);
+
+ if (idNewSession != g_idSession)
+ {
+ bool fUnregister = false;
+
+ VGSvcVerbose(3, "vgsvcPageSharingWorker: VM was restored!!\n");
+ /* The VM was restored, so reregister all modules the next time. */
+ RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, &fUnregister);
+ g_pKnownModuleTree = NULL;
+
+ g_idSession = idNewSession;
+ }
+#endif
+ }
+
+ RTSemEventMultiDestroy(g_PageSharingEvent);
+ g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
+
+ VGSvcVerbose(3, "vgsvcPageSharingWorker: finished thread\n");
+ return 0;
+}
+
+#ifdef RT_OS_WINDOWS
+
+/**
+ * This gets control when VBoxService is launched with "pagefusion" by
+ * vgsvcPageSharingWorkerProcess().
+ *
+ * @returns RTEXITCODE_SUCCESS.
+ *
+ * @remarks It won't normally return since the parent drops the shutdown hint
+ * via RTProcTerminate().
+ */
+RTEXITCODE VGSvcPageSharingWorkerChild(void)
+{
+ VGSvcVerbose(3, "vgsvcPageSharingInitFork\n");
+
+ bool fShutdown = false;
+ vgsvcPageSharingInit();
+ vgsvcPageSharingWorker(&fShutdown);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vgsvcPageSharingWorkerProcess(bool volatile *pfShutdown)
+{
+ RTPROCESS hProcess = NIL_RTPROCESS;
+ int rc;
+
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Now enter the loop retrieving runtime data continuously.
+ */
+ for (;;)
+ {
+ bool fEnabled = VbglR3PageSharingIsEnabled();
+ VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: enabled=%d\n", fEnabled);
+
+ /*
+ * Start a 2nd VBoxService process to deal with page fusion as we do
+ * not wish to dummy load dlls into this process. (First load with
+ * DONT_RESOLVE_DLL_REFERENCES, 2nd normal -> dll init routines not called!)
+ */
+ if ( fEnabled
+ && hProcess == NIL_RTPROCESS)
+ {
+ char szExeName[256];
+ char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
+ if (pszExeName)
+ {
+ char const *papszArgs[3];
+ papszArgs[0] = pszExeName;
+ papszArgs[1] = "pagefusion";
+ papszArgs[2] = NULL;
+ rc = RTProcCreate(pszExeName, papszArgs, RTENV_DEFAULT, 0 /* normal child */, &hProcess);
+ if (RT_FAILURE(rc))
+ VGSvcError("vgsvcPageSharingWorkerProcess: RTProcCreate %s failed; rc=%Rrc\n", pszExeName, rc);
+ }
+ }
+
+ /*
+ * Block for a minute.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
+ if (*pfShutdown)
+ break;
+ if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
+ {
+ VGSvcError("vgsvcPageSharingWorkerProcess: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
+ break;
+ }
+ }
+
+ if (hProcess != NIL_RTPROCESS)
+ RTProcTerminate(hProcess);
+
+ VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: finished thread\n");
+ return 0;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcPageSharingStop(void)
+{
+ RTSemEventMultiSignal(g_PageSharingEvent);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcPageSharingTerm(void)
+{
+ if (g_PageSharingEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_PageSharingEvent);
+ g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'pagesharing' service description.
+ */
+VBOXSERVICE g_PageSharing =
+{
+ /* pszName. */
+ "pagesharing",
+ /* pszDescription. */
+ "Page Sharing",
+ /* pszUsage. */
+ NULL,
+ /* pszOptions. */
+ NULL,
+ /* methods */
+ VGSvcDefaultPreInit,
+ VGSvcDefaultOption,
+ vgsvcPageSharingInit,
+#ifdef RT_OS_WINDOWS
+ vgsvcPageSharingWorkerProcess,
+#else
+ vgsvcPageSharingWorker,
+#endif
+ vgsvcPageSharingStop,
+ vgsvcPageSharingTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp
new file mode 100644
index 00000000..3ef2346d
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp
@@ -0,0 +1,429 @@
+/* $Id: VBoxServicePropCache.cpp $ */
+/** @file
+ * VBoxServicePropCache - Guest property cache.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+#include "VBoxServicePropCache.h"
+
+
+
+/** @todo Docs */
+static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName,
+ uint32_t fFlags)
+{
+ RT_NOREF1(fFlags);
+ AssertPtrReturn(pCache, NULL);
+ AssertPtrReturn(pszName, NULL);
+
+ /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
+ * map.
+ * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
+ * implementation (AVL tree). However, this is not important at the
+ * moment. */
+ PVBOXSERVICEVEPROPCACHEENTRY pNode = NULL;
+ if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
+ {
+ PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
+ RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
+ {
+ if (strcmp(pNodeIt->pszName, pszName) == 0)
+ {
+ pNode = pNodeIt;
+ break;
+ }
+ }
+ RTCritSectLeave(&pCache->CritSect);
+ }
+ return pNode;
+}
+
+
+/** @todo Docs */
+static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
+{
+ AssertPtrReturn(pCache, NULL);
+ AssertPtrReturn(pszName, NULL);
+
+ PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
+ if (pNode)
+ {
+ pNode->pszName = RTStrDup(pszName);
+ if (!pNode->pszName)
+ {
+ RTMemFree(pNode);
+ return NULL;
+ }
+ pNode->pszValue = NULL;
+ pNode->fFlags = 0;
+ pNode->pszValueReset = NULL;
+
+ int rc = RTCritSectEnter(&pCache->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
+ rc = RTCritSectLeave(&pCache->CritSect);
+ }
+ }
+ return pNode;
+}
+
+
+/** @todo Docs */
+static int vgsvcPropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+
+ int rc;
+ if (pszValueFormat != NULL)
+ {
+ va_list va;
+ va_start(va, pszValueFormat);
+
+ char *pszValue;
+ if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
+ {
+ if (fFlags & VGSVCPROPCACHE_FLAGS_TRANSIENT)
+ {
+ /*
+ * Because a value can be temporary we have to make sure it also
+ * gets deleted when the property cache did not have the chance to
+ * gracefully clean it up (due to a hard VM reset etc), so set this
+ * guest property using the TRANSRESET flag..
+ */
+ rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSRESET");
+ if (rc == VERR_PARSE_ERROR)
+ {
+ /* Host does not support the "TRANSRESET" flag, so only
+ * use the "TRANSIENT" flag -- better than nothing :-). */
+ rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSIENT");
+ /** @todo r=bird: Remember that the host doesn't support
+ * this. */
+ }
+ }
+ else
+ rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue /* No transient flags set */);
+ RTStrFree(pszValue);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ va_end(va);
+ }
+ else
+ rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
+ return rc;
+}
+
+
+/**
+ * Creates a property cache.
+ *
+ * @returns IPRT status code.
+ * @param pCache Pointer to the cache.
+ * @param uClientId The HGCM handle of to the guest property service.
+ */
+int VGSvcPropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ /** @todo Prevent init the cache twice!
+ * r=bird: Use a magic. */
+ RTListInit(&pCache->NodeHead);
+ pCache->uClientID = uClientId;
+ return RTCritSectInit(&pCache->CritSect);
+}
+
+
+/**
+ * Updates a cache entry without submitting any changes to the host.
+ *
+ * This is handy for defining default values/flags.
+ *
+ * @returns VBox status code.
+ *
+ * @param pCache The property cache.
+ * @param pszName The property name.
+ * @param fFlags The property flags to set.
+ * @param pszValueReset The property reset value.
+ */
+int VGSvcPropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags, const char *pszValueReset)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
+ if (pNode == NULL)
+ pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
+
+ int rc;
+ if (pNode != NULL)
+ {
+ rc = RTCritSectEnter(&pCache->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pNode->fFlags = fFlags;
+ if (pszValueReset)
+ {
+ if (pNode->pszValueReset)
+ RTStrFree(pNode->pszValueReset);
+ pNode->pszValueReset = RTStrDup(pszValueReset);
+ AssertPtr(pNode->pszValueReset);
+ }
+ rc = RTCritSectLeave(&pCache->CritSect);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Updates the local guest property cache and writes it to HGCM if outdated.
+ *
+ * @returns VBox status code.
+ *
+ * @param pCache The property cache.
+ * @param pszName The property name.
+ * @param pszValueFormat The property format string. If this is NULL then
+ * the property will be deleted (if possible).
+ * @param ... Format arguments.
+ */
+int VGSvcPropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+
+ Assert(pCache->uClientID);
+
+ /*
+ * Format the value first.
+ */
+ char *pszValue = NULL;
+ if (pszValueFormat)
+ {
+ va_list va;
+ va_start(va, pszValueFormat);
+ RTStrAPrintfV(&pszValue, pszValueFormat, va);
+ va_end(va);
+ if (!pszValue)
+ return VERR_NO_STR_MEMORY;
+ }
+
+ PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
+
+ /* Lock the cache. */
+ int rc = RTCritSectEnter(&pCache->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pNode == NULL)
+ pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
+
+ AssertPtr(pNode);
+ if (pszValue) /* Do we have a value to check for? */
+ {
+ bool fUpdate = false;
+ /* Always update this property, no matter what? */
+ if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE)
+ fUpdate = true;
+ /* Did the value change so we have to update? */
+ else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
+ fUpdate = true;
+ /* No value stored at the moment but we have a value now? */
+ else if (pNode->pszValue == NULL)
+ fUpdate = true;
+
+ if (fUpdate)
+ {
+ /* Write the update. */
+ rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
+ VGSvcVerbose(4, "[PropCache %p]: Written '%s'='%s' (flags: %x), rc=%Rrc\n",
+ pCache, pNode->pszName, pszValue, pNode->fFlags, rc);
+ if (RT_SUCCESS(rc)) /* Only update the node's value on successful write. */
+ {
+ RTStrFree(pNode->pszValue);
+ pNode->pszValue = RTStrDup(pszValue);
+ if (!pNode->pszValue)
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VINF_NO_CHANGE; /* No update needed. */
+ }
+ else
+ {
+ /* No value specified. Deletion (or no action required). */
+ if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
+ {
+ rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName,
+ 0, /* Flags */ NULL /* Value */);
+ VGSvcVerbose(4, "[PropCache %p]: Deleted '%s'='%s' (flags: %x), rc=%Rrc\n",
+ pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc);
+ if (RT_SUCCESS(rc)) /* Only delete property value on successful Vbgl deletion. */
+ {
+ /* Delete property (but do not remove from cache) if not deleted yet. */
+ RTStrFree(pNode->pszValue);
+ pNode->pszValue = NULL;
+ }
+ }
+ else
+ rc = VINF_NO_CHANGE; /* No update needed. */
+ }
+
+ /* Release cache. */
+ RTCritSectLeave(&pCache->CritSect);
+ }
+
+ VGSvcVerbose(4, "[PropCache %p]: Updating '%s' resulted in rc=%Rrc\n", pCache, pszName, rc);
+
+ /* Delete temp stuff. */
+ RTStrFree(pszValue);
+ return rc;
+}
+
+
+/**
+ * Updates all cache values which are matching the specified path.
+ *
+ * @returns VBox status code.
+ *
+ * @param pCache The property cache.
+ * @param pszValue The value to set. A NULL will delete the value.
+ * @param fFlags Flags to set.
+ * @param pszPathFormat The path format string. May not be null and has
+ * to be an absolute path.
+ * @param ... Format arguments.
+ */
+int VGSvcPropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags,
+ const char *pszPathFormat, ...)
+{
+ RT_NOREF1(fFlags);
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_FOUND;
+ if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
+ {
+ /*
+ * Format the value first.
+ */
+ char *pszPath = NULL;
+ va_list va;
+ va_start(va, pszPathFormat);
+ RTStrAPrintfV(&pszPath, pszPathFormat, va);
+ va_end(va);
+ if (!pszPath)
+ {
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ {
+ /* Iterate through all nodes and compare their paths. */
+ PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
+ RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
+ {
+ if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
+ {
+ /** @todo Use some internal function to update the node directly, this is slow atm. */
+ rc = VGSvcPropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ RTStrFree(pszPath);
+ }
+ RTCritSectLeave(&pCache->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Flushes the cache by writing every item regardless of its state.
+ *
+ * @param pCache The property cache.
+ */
+int VGSvcPropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
+ {
+ PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
+ RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
+ {
+ rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNodeIt->pszName, pNodeIt->fFlags, pNodeIt->pszValue);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ RTCritSectLeave(&pCache->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Reset all temporary properties and destroy the cache.
+ *
+ * @param pCache The property cache.
+ */
+void VGSvcPropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
+{
+ AssertPtrReturnVoid(pCache);
+ Assert(pCache->uClientID);
+
+ /* Lock the cache. */
+ int rc = RTCritSectEnter(&pCache->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
+ while (pNode)
+ {
+ PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
+ ? NULL :
+ RTListNodeGetNext(&pNode->NodeSucc,
+ VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
+ RTListNodeRemove(&pNode->NodeSucc);
+
+ if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_TEMPORARY)
+ rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pNode->pszValueReset);
+
+ AssertPtr(pNode->pszName);
+ RTStrFree(pNode->pszName);
+ RTStrFree(pNode->pszValue);
+ RTStrFree(pNode->pszValueReset);
+ pNode->fFlags = 0;
+
+ RTMemFree(pNode);
+
+ pNode = pNext;
+ }
+ RTCritSectLeave(&pCache->CritSect);
+ }
+
+ /* Destroy critical section. */
+ RTCritSectDelete(&pCache->CritSect);
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h
new file mode 100644
index 00000000..ee6e3f81
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h
@@ -0,0 +1,56 @@
+/* $Id: */
+/** @file
+ * VBoxServicePropCache - Guest property cache.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServicePropCache_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServicePropCache_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxServiceInternal.h"
+
+#ifdef VBOX_WITH_GUEST_PROPS
+
+/** @name VGSVCPROPCACHE_FLAG_XXX - Guest Property Cache Flags.
+ * @{ */
+/** Indicates wheter a guest property is temporary and either should
+ * - a) get a "reset" value assigned (via VBoxServicePropCacheUpdateEntry)
+ * as soon as the property cache gets destroyed, or
+ * - b) get deleted when no reset value is specified.
+ */
+# define VGSVCPROPCACHE_FLAGS_TEMPORARY RT_BIT(1)
+/** Indicates whether a property every time needs to be updated, regardless
+ * if its real value changed or not. */
+# define VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE RT_BIT(2)
+/** The guest property gets deleted when
+ * - a) the property cache gets destroyed, or
+ * - b) the VM gets reset / shutdown / destroyed.
+ */
+# define VGSVCPROPCACHE_FLAGS_TRANSIENT RT_BIT(3)
+/** @} */
+
+int VGSvcPropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId);
+int VGSvcPropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags, const char *pszValueReset);
+int VGSvcPropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...);
+int VGSvcPropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags,
+ const char *pszPathFormat, ...);
+int VGSvcPropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache);
+void VGSvcPropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache);
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServicePropCache_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h b/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h
new file mode 100644
index 00000000..b5c106db
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h
@@ -0,0 +1,27 @@
+/* $Id: VBoxServiceResource-win.h $ */
+/** @file
+ * VBoxService - Guest Additions Service, resource IDs.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceResource_win_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceResource_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define IDI_VIRTUALBOX 101
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceResource_win_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp
new file mode 100644
index 00000000..881d0f94
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp
@@ -0,0 +1,736 @@
+/* $Id: VBoxServiceStats.cpp $ */
+/** @file
+ * VBoxStats - Guest statistics notification
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vgsvc_vmstats VBoxService - VM Statistics
+ *
+ * The VM statistics subservice helps out the performance collector API on the
+ * host side by providing metrics from inside the guest.
+ *
+ * See IPerformanceCollector, CollectorGuest and the "Guest/" submetrics that
+ * gets registered by Machine::i_registerMetrics in Main.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(RT_OS_WINDOWS)
+# include <iprt/win/windows.h>
+# include <psapi.h>
+# include <winternl.h>
+
+#elif defined(RT_OS_LINUX)
+# include <iprt/ctype.h>
+# include <iprt/stream.h>
+# include <unistd.h>
+
+#elif defined(RT_OS_SOLARIS)
+# include <kstat.h>
+# include <sys/sysinfo.h>
+# include <unistd.h>
+#else
+/** @todo port me. */
+
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/ldr.h>
+#include <VBox/param.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <VBox/err.h>
+#include <VBox/VMMDev.h> /* For VMMDevReportGuestStats and indirectly VbglR3StatReport. */
+#include <VBox/VBoxGuestLib.h>
+
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct _VBOXSTATSCONTEXT
+{
+ RTMSINTERVAL cMsStatInterval;
+
+ uint64_t au64LastCpuLoad_Idle[VMM_MAX_CPU_COUNT];
+ uint64_t au64LastCpuLoad_Kernel[VMM_MAX_CPU_COUNT];
+ uint64_t au64LastCpuLoad_User[VMM_MAX_CPU_COUNT];
+ uint64_t au64LastCpuLoad_Nice[VMM_MAX_CPU_COUNT];
+
+#ifdef RT_OS_WINDOWS
+ NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
+ ULONG SystemInformationLength, PULONG ReturnLength);
+ void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
+ BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
+#endif
+} VBOXSTATSCONTEXT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global data. */
+static VBOXSTATSCONTEXT g_VMStat = {0};
+
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_VMStatEvent = NIL_RTSEMEVENTMULTI;
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vgsvcVMStatsInit(void)
+{
+ VGSvcVerbose(3, "vgsvcVMStatsInit\n");
+
+ int rc = RTSemEventMultiCreate(&g_VMStatEvent);
+ AssertRCReturn(rc, rc);
+
+ g_VMStat.cMsStatInterval = 0; /* default; update disabled */
+ RT_ZERO(g_VMStat.au64LastCpuLoad_Idle);
+ RT_ZERO(g_VMStat.au64LastCpuLoad_Kernel);
+ RT_ZERO(g_VMStat.au64LastCpuLoad_User);
+ RT_ZERO(g_VMStat.au64LastCpuLoad_Nice);
+
+ rc = VbglR3StatQueryInterval(&g_VMStat.cMsStatInterval);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsInit: New statistics interval %u seconds\n", g_VMStat.cMsStatInterval);
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsInit: DeviceIoControl failed with %d\n", rc);
+
+#ifdef RT_OS_WINDOWS
+ /* NtQuerySystemInformation might be dropped in future releases, so load
+ it dynamically as per Microsoft's recommendation. */
+ *(void **)&g_VMStat.pfnNtQuerySystemInformation = RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation");
+ if (g_VMStat.pfnNtQuerySystemInformation)
+ VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnNtQuerySystemInformation = %x\n", g_VMStat.pfnNtQuerySystemInformation);
+ else
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsInit: ntdll.NtQuerySystemInformation not found!\n");
+ return VERR_SERVICE_DISABLED;
+ }
+
+ /* GlobalMemoryStatus is win2k and up, so load it dynamically */
+ *(void **)&g_VMStat.pfnGlobalMemoryStatusEx = RTLdrGetSystemSymbol("kernel32.dll", "GlobalMemoryStatusEx");
+ if (g_VMStat.pfnGlobalMemoryStatusEx)
+ VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.GlobalMemoryStatusEx = %x\n", g_VMStat.pfnGlobalMemoryStatusEx);
+ else
+ {
+ /** @todo Now fails in NT4; do we care? */
+ VGSvcVerbose(3, "vgsvcVMStatsInit: kernel32.GlobalMemoryStatusEx not found!\n");
+ return VERR_SERVICE_DISABLED;
+ }
+
+ /* GetPerformanceInfo is xp and up, so load it dynamically */
+ *(void **)&g_VMStat.pfnGetPerformanceInfo = RTLdrGetSystemSymbol("psapi.dll", "GetPerformanceInfo");
+ if (g_VMStat.pfnGetPerformanceInfo)
+ VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnGetPerformanceInfo= %x\n", g_VMStat.pfnGetPerformanceInfo);
+#endif /* RT_OS_WINDOWS */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gathers VM statistics and reports them to the host.
+ */
+static void vgsvcVMStatsReport(void)
+{
+#if defined(RT_OS_WINDOWS)
+ Assert(g_VMStat.pfnGlobalMemoryStatusEx && g_VMStat.pfnNtQuerySystemInformation);
+ if ( !g_VMStat.pfnGlobalMemoryStatusEx
+ || !g_VMStat.pfnNtQuerySystemInformation)
+ return;
+
+ /* Clear the report so we don't report garbage should NtQuerySystemInformation
+ behave in an unexpected manner. */
+ VMMDevReportGuestStats req;
+ RT_ZERO(req);
+
+ /* Query and report guest statistics */
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+
+ MEMORYSTATUSEX memStatus;
+ memStatus.dwLength = sizeof(memStatus);
+ g_VMStat.pfnGlobalMemoryStatusEx(&memStatus);
+
+ req.guestStats.u32PageSize = systemInfo.dwPageSize;
+ req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / _4K);
+ req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / _4K);
+ /* The current size of the committed memory limit, in bytes. This is physical
+ memory plus the size of the page file, minus a small overhead. */
+ req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal;
+ req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
+ req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
+ | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
+ | VBOX_GUEST_STAT_PAGE_FILE_SIZE
+ | VBOX_GUEST_STAT_MEMORY_LOAD;
+# ifdef VBOX_WITH_MEMBALLOON
+ req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
+# else
+ req.guestStats.u32PhysMemBalloon = 0;
+# endif
+
+ if (g_VMStat.pfnGetPerformanceInfo)
+ {
+ PERFORMANCE_INFORMATION perfInfo;
+
+ if (g_VMStat.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
+ {
+ req.guestStats.u32Processes = perfInfo.ProcessCount;
+ req.guestStats.u32Threads = perfInfo.ThreadCount;
+ req.guestStats.u32Handles = perfInfo.HandleCount;
+ req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
+ req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
+ req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
+ req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
+ req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES
+ | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL
+ | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED
+ | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE;
+ }
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: GetPerformanceInfo failed with %d\n", GetLastError());
+ }
+
+ /* Query CPU load information */
+ uint32_t cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
+ PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
+ pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct);
+ if (!pProcInfo)
+ return;
+
+ /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
+ bool fCpuInfoAvail = false;
+ DWORD cbReturned;
+ NTSTATUS rcNt = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
+ if ( !rcNt
+ && cbReturned == cbStruct)
+ {
+ for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++)
+ {
+ if (i >= VMM_MAX_CPU_COUNT)
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPUs %u..%u\n", i, systemInfo.dwNumberOfProcessors);
+ break;
+ }
+
+ if (g_VMStat.au64LastCpuLoad_Kernel[i] == 0)
+ {
+ /* first time */
+ g_VMStat.au64LastCpuLoad_Idle[i] = pProcInfo[i].IdleTime.QuadPart;
+ g_VMStat.au64LastCpuLoad_Kernel[i] = pProcInfo[i].KernelTime.QuadPart;
+ g_VMStat.au64LastCpuLoad_User[i] = pProcInfo[i].UserTime.QuadPart;
+
+ Sleep(250);
+
+ rcNt = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
+ Assert(!rcNt);
+ }
+
+ uint64_t deltaIdle = (pProcInfo[i].IdleTime.QuadPart - g_VMStat.au64LastCpuLoad_Idle[i]);
+ uint64_t deltaKernel = (pProcInfo[i].KernelTime.QuadPart - g_VMStat.au64LastCpuLoad_Kernel[i]);
+ uint64_t deltaUser = (pProcInfo[i].UserTime.QuadPart - g_VMStat.au64LastCpuLoad_User[i]);
+ deltaKernel -= deltaIdle; /* idle time is added to kernel time */
+ uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
+ if (ullTotalTime == 0) /* Prevent division through zero. */
+ ullTotalTime = 1;
+
+ req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
+ req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
+ req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
+
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
+ | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
+ | VBOX_GUEST_STAT_CPU_LOAD_USER;
+ req.guestStats.u32CpuId = i;
+ fCpuInfoAvail = true;
+ int rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", i);
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: VbglR3StatReport failed with rc=%Rrc\n", rc);
+
+ g_VMStat.au64LastCpuLoad_Idle[i] = pProcInfo[i].IdleTime.QuadPart;
+ g_VMStat.au64LastCpuLoad_Kernel[i] = pProcInfo[i].KernelTime.QuadPart;
+ g_VMStat.au64LastCpuLoad_User[i] = pProcInfo[i].UserTime.QuadPart;
+ }
+ }
+ RTMemFree(pProcInfo);
+
+ if (!fCpuInfoAvail)
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
+ int rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
+ }
+
+#elif defined(RT_OS_LINUX)
+ VMMDevReportGuestStats req;
+ RT_ZERO(req);
+ PRTSTREAM pStrm;
+ char szLine[256];
+ char *psz;
+
+ int rc = RTStrmOpen("/proc/meminfo", "r", &pStrm);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t u64Kb;
+ uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
+ for (;;)
+ {
+ rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
+ if (RT_FAILURE(rc))
+ break;
+ if (strstr(szLine, "MemTotal:") == szLine)
+ {
+ rc = RTStrToUInt64Ex(RTStrStripL(&szLine[9]), &psz, 0, &u64Kb);
+ if (RT_SUCCESS(rc))
+ u64Total = u64Kb * _1K;
+ }
+ else if (strstr(szLine, "MemFree:") == szLine)
+ {
+ rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
+ if (RT_SUCCESS(rc))
+ u64Free = u64Kb * _1K;
+ }
+ else if (strstr(szLine, "Buffers:") == szLine)
+ {
+ rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
+ if (RT_SUCCESS(rc))
+ u64Buffers = u64Kb * _1K;
+ }
+ else if (strstr(szLine, "Cached:") == szLine)
+ {
+ rc = RTStrToUInt64Ex(RTStrStripL(&szLine[7]), &psz, 0, &u64Kb);
+ if (RT_SUCCESS(rc))
+ u64Cached = u64Kb * _1K;
+ }
+ else if (strstr(szLine, "SwapTotal:") == szLine)
+ {
+ rc = RTStrToUInt64Ex(RTStrStripL(&szLine[10]), &psz, 0, &u64Kb);
+ if (RT_SUCCESS(rc))
+ u64PagedTotal = u64Kb * _1K;
+ }
+ }
+ req.guestStats.u32PhysMemTotal = u64Total / _4K;
+ req.guestStats.u32PhysMemAvail = (u64Free + u64Buffers + u64Cached) / _4K;
+ req.guestStats.u32MemSystemCache = (u64Buffers + u64Cached) / _4K;
+ req.guestStats.u32PageFileSize = u64PagedTotal / _4K;
+ RTStrmClose(pStrm);
+ }
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: memory info not available!\n");
+
+ req.guestStats.u32PageSize = getpagesize();
+ req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
+ | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
+ | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE
+ | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
+# ifdef VBOX_WITH_MEMBALLOON
+ req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
+# else
+ req.guestStats.u32PhysMemBalloon = 0;
+# endif
+
+
+ /** @todo req.guestStats.u32Threads */
+ /** @todo req.guestStats.u32Processes */
+ /* req.guestStats.u32Handles doesn't make sense here. */
+ /** @todo req.guestStats.u32MemoryLoad */
+ /** @todo req.guestStats.u32MemCommitTotal */
+ /** @todo req.guestStats.u32MemKernelTotal */
+ /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */
+ /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */
+
+ bool fCpuInfoAvail = false;
+ rc = RTStrmOpen("/proc/stat", "r", &pStrm);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
+ if (RT_FAILURE(rc))
+ break;
+ if ( strstr(szLine, "cpu") == szLine
+ && strlen(szLine) > 3
+ && RT_C_IS_DIGIT(szLine[3]))
+ {
+ uint32_t u32CpuId;
+ rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId);
+ if (u32CpuId < VMM_MAX_CPU_COUNT)
+ {
+ uint64_t u64User = 0;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User);
+
+ uint64_t u64Nice = 0;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice);
+
+ uint64_t u64System = 0;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System);
+
+ uint64_t u64Idle = 0;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle);
+
+ uint64_t u64DeltaIdle = u64Idle - g_VMStat.au64LastCpuLoad_Idle[u32CpuId];
+ uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[u32CpuId];
+ uint64_t u64DeltaUser = u64User - g_VMStat.au64LastCpuLoad_User[u32CpuId];
+ uint64_t u64DeltaNice = u64Nice - g_VMStat.au64LastCpuLoad_Nice[u32CpuId];
+
+ uint64_t u64DeltaAll = u64DeltaIdle
+ + u64DeltaSystem
+ + u64DeltaUser
+ + u64DeltaNice;
+ if (u64DeltaAll == 0) /* Prevent division through zero. */
+ u64DeltaAll = 1;
+
+ g_VMStat.au64LastCpuLoad_Idle[u32CpuId] = u64Idle;
+ g_VMStat.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
+ g_VMStat.au64LastCpuLoad_User[u32CpuId] = u64User;
+ g_VMStat.au64LastCpuLoad_Nice[u32CpuId] = u64Nice;
+
+ req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
+ req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
+ req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser
+ + u64DeltaNice) * 100 / u64DeltaAll);
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
+ | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
+ | VBOX_GUEST_STAT_CPU_LOAD_USER;
+ req.guestStats.u32CpuId = u32CpuId;
+ fCpuInfoAvail = true;
+ rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", u32CpuId);
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
+ }
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", u32CpuId);
+ }
+ }
+ RTStrmClose(pStrm);
+ }
+ if (!fCpuInfoAvail)
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
+ rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
+ }
+
+#elif defined(RT_OS_SOLARIS)
+ VMMDevReportGuestStats req;
+ RT_ZERO(req);
+ kstat_ctl_t *pStatKern = kstat_open();
+ if (pStatKern)
+ {
+ /*
+ * Memory statistics.
+ */
+ uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
+ int rc = -1;
+ kstat_t *pStatPages = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"system_pages");
+ if (pStatPages)
+ {
+ rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */);
+ if (rc != -1)
+ {
+ kstat_named_t *pStat = NULL;
+ pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"pagestotal");
+ if (pStat)
+ u64Total = pStat->value.ul;
+
+ pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"freemem");
+ if (pStat)
+ u64Free = pStat->value.ul;
+ }
+ }
+
+ kstat_t *pStatZFS = kstat_lookup(pStatKern, (char *)"zfs", 0 /* instance */, (char *)"arcstats");
+ if (pStatZFS)
+ {
+ rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */);
+ if (rc != -1)
+ {
+ kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, (char *)"size");
+ if (pStat)
+ u64Cached = pStat->value.ul;
+ }
+ }
+
+ /*
+ * The vminfo are accumulative counters updated every "N" ticks. Let's get the
+ * number of stat updates so far and use that to divide the swap counter.
+ */
+ kstat_t *pStatInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"sysinfo");
+ if (pStatInfo)
+ {
+ sysinfo_t SysInfo;
+ rc = kstat_read(pStatKern, pStatInfo, &SysInfo);
+ if (rc != -1)
+ {
+ kstat_t *pStatVMInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"vminfo");
+ if (pStatVMInfo)
+ {
+ vminfo_t VMInfo;
+ rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo);
+ if (rc != -1)
+ {
+ Assert(SysInfo.updates != 0);
+ u64PagedTotal = VMInfo.swap_avail / SysInfo.updates;
+ }
+ }
+ }
+ }
+
+ req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */
+ req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */
+ req.guestStats.u32MemSystemCache = u64Cached / _4K;
+ req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */
+ /** @todo req.guestStats.u32Threads */
+ /** @todo req.guestStats.u32Processes */
+ /** @todo req.guestStats.u32Handles -- ??? */
+ /** @todo req.guestStats.u32MemoryLoad */
+ /** @todo req.guestStats.u32MemCommitTotal */
+ /** @todo req.guestStats.u32MemKernelTotal */
+ /** @todo req.guestStats.u32MemKernelPaged */
+ /** @todo req.guestStats.u32MemKernelNonPaged */
+ req.guestStats.u32PageSize = getpagesize();
+
+ req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
+ | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
+ | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE
+ | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
+#ifdef VBOX_WITH_MEMBALLOON
+ req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
+#else
+ req.guestStats.u32PhysMemBalloon = 0;
+#endif
+
+ /*
+ * CPU statistics.
+ */
+ cpu_stat_t StatCPU;
+ RT_ZERO(StatCPU);
+ kstat_t *pStatNode = NULL;
+ uint32_t cCPUs = 0;
+ bool fCpuInfoAvail = false;
+ for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next)
+ {
+ if (!strcmp(pStatNode->ks_module, "cpu_stat"))
+ {
+ rc = kstat_read(pStatKern, pStatNode, &StatCPU);
+ if (rc == -1)
+ break;
+
+ if (cCPUs < VMM_MAX_CPU_COUNT)
+ {
+ uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE];
+ uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER];
+ uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL];
+
+ uint64_t u64DeltaIdle = u64Idle - g_VMStat.au64LastCpuLoad_Idle[cCPUs];
+ uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[cCPUs];
+ uint64_t u64DeltaUser = u64User - g_VMStat.au64LastCpuLoad_User[cCPUs];
+
+ uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
+ if (u64DeltaAll == 0) /* Prevent division through zero. */
+ u64DeltaAll = 1;
+
+ g_VMStat.au64LastCpuLoad_Idle[cCPUs] = u64Idle;
+ g_VMStat.au64LastCpuLoad_Kernel[cCPUs] = u64System;
+ g_VMStat.au64LastCpuLoad_User[cCPUs] = u64User;
+
+ req.guestStats.u32CpuId = cCPUs;
+ req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
+ req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
+ req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll);
+
+ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
+ | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
+ | VBOX_GUEST_STAT_CPU_LOAD_USER;
+ fCpuInfoAvail = true;
+ rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", cCPUs);
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
+ cCPUs++;
+ }
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", cCPUs);
+ }
+ }
+
+ /*
+ * Report whatever statistics were collected.
+ */
+ if (!fCpuInfoAvail)
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
+ rc = VbglR3StatReport(&req);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
+ else
+ VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
+ }
+
+ kstat_close(pStatKern);
+ }
+
+#else
+ /** @todo implement for other platforms. */
+
+#endif
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+DECLCALLBACK(int) vgsvcVMStatsWorker(bool volatile *pfShutdown)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Start monitoring of the stat event change event. */
+ rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0);
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(3, "vgsvcVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Now enter the loop retrieving runtime data continuously.
+ */
+ for (;;)
+ {
+ uint32_t fEvents = 0;
+ RTMSINTERVAL cWaitMillies;
+
+ /* Check if an update interval change is pending. */
+ rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
+ if ( RT_SUCCESS(rc)
+ && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST))
+ VbglR3StatQueryInterval(&g_VMStat.cMsStatInterval);
+
+ if (g_VMStat.cMsStatInterval)
+ {
+ vgsvcVMStatsReport();
+ cWaitMillies = g_VMStat.cMsStatInterval;
+ }
+ else
+ cWaitMillies = 3000;
+
+ /*
+ * Block for a while.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies);
+ if (*pfShutdown)
+ break;
+ if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
+ {
+ VGSvcError("vgsvcVMStatsWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
+ rc = rc2;
+ break;
+ }
+ }
+
+ /* Cancel monitoring of the stat event change event. */
+ rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST);
+ if (RT_FAILURE(rc))
+ VGSvcVerbose(3, "vgsvcVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
+
+ VGSvcVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
+ return 0;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcVMStatsStop(void)
+{
+ RTSemEventMultiSignal(g_VMStatEvent);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcVMStatsTerm(void)
+{
+ if (g_VMStatEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_VMStatEvent);
+ g_VMStatEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'vminfo' service description.
+ */
+VBOXSERVICE g_VMStatistics =
+{
+ /* pszName. */
+ "vmstats",
+ /* pszDescription. */
+ "Virtual Machine Statistics",
+ /* pszUsage. */
+ NULL,
+ /* pszOptions. */
+ NULL,
+ /* methods */
+ VGSvcDefaultPreInit,
+ VGSvcDefaultOption,
+ vgsvcVMStatsInit,
+ vgsvcVMStatsWorker,
+ vgsvcVMStatsStop,
+ vgsvcVMStatsTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp
new file mode 100644
index 00000000..ca2ea383
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp
@@ -0,0 +1,815 @@
+/* $Id: VBoxServiceTimeSync.cpp $ */
+/** @file
+ * VBoxService - Guest Additions TimeSync Service.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_vgsvc_timesync VBoxService - The Time Sync Service
+ *
+ * The time sync subservice synchronizes the guest OS walltime with the host.
+ *
+ * The time sync service plays along with the Time Manager (TM) in the VMM
+ * to keep the guest time accurate using the host machine as a reference.
+ * Communication is facilitated by VMMDev. TM will try its best to make sure
+ * all timer ticks get delivered so that there isn't normally any need to
+ * adjust the guest time.
+ *
+ * There are three normal (= acceptable) cases:
+ * -# When the service starts up. This is because ticks and such might
+ * be lost during VM and OS startup. (Need to figure out exactly why!)
+ * -# When the TM is unable to deliver all the ticks and swallows a
+ * backlog of ticks. The threshold for this is configurable with
+ * a default of 60 seconds.
+ * -# The time is adjusted on the host. This can be caused manually by
+ * the user or by some time sync daemon (NTP, LAN server, etc.).
+ *
+ * There are a number of very odd case where adjusting is needed. Here
+ * are some of them:
+ * -# Timer device emulation inaccuracies (like rounding).
+ * -# Inaccuracies in time source VirtualBox uses.
+ * -# The Guest and/or Host OS doesn't perform proper time keeping. This
+ * can come about as a result of OS and/or hardware issues.
+ *
+ * The TM is our source for the host time and will make adjustments for
+ * current timer delivery lag. The simplistic approach taken by TM is to
+ * adjust the host time by the current guest timer delivery lag, meaning that
+ * if the guest is behind 1 second with PIT/RTC/++ ticks, this should be
+ * reflected in the guest wall time as well.
+ *
+ * Now, there is any amount of trouble we can cause by changing the time.
+ * Most applications probably use the wall time when they need to measure
+ * things. A walltime that is being juggled about every so often, even if just
+ * a little bit, could occasionally upset these measurements by for instance
+ * yielding negative results.
+ *
+ * This bottom line here is that the time sync service isn't really supposed
+ * to do anything and will try avoid having to do anything when possible.
+ *
+ * The implementation uses the latency it takes to query host time as the
+ * absolute maximum precision to avoid messing up under timer tick catchup
+ * and/or heavy host/guest load. (Rationale is that a *lot* of stuff may
+ * happen on our way back from ring-3 and TM/VMMDev since we're taking the
+ * route thru the inner EM loop with its force flag processing.)
+ *
+ * But this latency has to be measured from our perspective, which means it
+ * could just as easily come out as 0. (OS/2 and Windows guests only update
+ * the current time when the timer ticks for instance.) The good thing is
+ * that this isn't really a problem since we won't ever do anything unless
+ * the drift is noticeable.
+ *
+ * It now boils down to these three (configuration) factors:
+ * -# g_cMsTimeSyncMinAdjust - The minimum drift we will ever bother with.
+ * -# g_TimeSyncLatencyFactor - The factor we multiply the latency by to
+ * calculate the dynamic minimum adjust factor.
+ * -# g_cMsTimeSyncMaxLatency - When to start discarding the data as utterly
+ * useless and take a rest (someone is too busy to give us good data).
+ * -# g_TimeSyncSetThreshold - The threshold at which we will just set the time
+ * instead of trying to adjust it (milliseconds).
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#else
+# include <unistd.h>
+# include <errno.h>
+# include <time.h>
+# include <sys/time.h>
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <VBox/err.h>
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The timesync interval (milliseconds). */
+static uint32_t g_TimeSyncInterval = 0;
+/**
+ * @see pg_vgsvc_timesync
+ *
+ * @remark OS/2: There is either a 1 second resolution on the DosSetDateTime
+ * API or a bug in my settimeofday implementation. Thus, don't
+ * bother unless there is at least a 1 second drift.
+ */
+#ifdef RT_OS_OS2
+static uint32_t g_cMsTimeSyncMinAdjust = 1000;
+#else
+static uint32_t g_cMsTimeSyncMinAdjust = 100;
+#endif
+/** @see pg_vgsvc_timesync */
+static uint32_t g_TimeSyncLatencyFactor = 8;
+/** @see pg_vgsvc_timesync */
+static uint32_t g_cMsTimeSyncMaxLatency = 250;
+/** @see pg_vgsvc_timesync */
+static uint32_t g_TimeSyncSetThreshold = 20*60*1000;
+/** Whether the next adjustment should just set the time instead of trying to
+ * adjust it. This is used to implement --timesync-set-start.
+ * For purposes of setting the kernel timezone, OS/2 always starts with this. */
+#ifdef RT_OS_OS2
+static bool volatile g_fTimeSyncSetOnStart = true;
+#else
+static bool volatile g_fTimeSyncSetOnStart = false;
+#endif
+/** Whether to set the time when the VM was restored. */
+static bool g_fTimeSyncSetOnRestore = true;
+/** The logging verbosity level.
+ * This uses the global verbosity level by default. */
+static uint32_t g_cTimeSyncVerbosity = 0;
+
+/** Current error count. Used to decide when to bitch and when not to. */
+static uint32_t g_cTimeSyncErrors = 0;
+
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
+
+/** The VM session ID. Changes whenever the VM is restored or reset. */
+static uint64_t g_idTimeSyncSession;
+
+#ifdef RT_OS_WINDOWS
+/** Process token. */
+static HANDLE g_hTokenProcess = NULL;
+/** Old token privileges. */
+static TOKEN_PRIVILEGES g_TkOldPrivileges;
+/** Backup values for time adjustment. */
+static DWORD g_dwWinTimeAdjustment;
+static DWORD g_dwWinTimeIncrement;
+static BOOL g_bWinTimeAdjustmentDisabled;
+#endif
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnPreInit}
+ */
+static DECLCALLBACK(int) vgsvcTimeSyncPreInit(void)
+{
+ /* Use global verbosity as default. */
+ g_cTimeSyncVerbosity = g_cVerbosity;
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ /** @todo Merge this function with vgsvcTimeSyncOption() to generalize
+ * the "command line args override guest property values" behavior. */
+
+ /*
+ * Read the service options from the VM's guest properties.
+ * Note that these options can be overridden by the command line options later.
+ */
+ uint32_t uGuestPropSvcClientID;
+ int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
+ {
+ VGSvcVerbose(0, "VMInfo: Guest property service is not available, skipping\n");
+ rc = VINF_SUCCESS;
+ }
+ else
+ VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
+ }
+ else
+ {
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-interval",
+ &g_TimeSyncInterval, 50, UINT32_MAX - 1);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-min-adjust",
+ &g_cMsTimeSyncMinAdjust, 0, 3600000);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-latency-factor",
+ &g_TimeSyncLatencyFactor, 1, 1024);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-max-latency",
+ &g_cMsTimeSyncMaxLatency, 1, 3600000);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
+ &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000 /* a week */);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-start");
+ if (RT_SUCCESS(rc))
+ g_fTimeSyncSetOnStart = true;
+ }
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-no-set-start");
+ if (RT_SUCCESS(rc))
+ g_fTimeSyncSetOnStart = false;
+ }
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore");
+ if (RT_SUCCESS(rc))
+ g_fTimeSyncSetOnRestore = true;
+ }
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-no-set-on-restore");
+ if (RT_SUCCESS(rc))
+ g_fTimeSyncSetOnRestore = false;
+ }
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ uint32_t uValue;
+ rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-verbosity",
+ &uValue, 0 /*uMin*/, 255 /*uMax*/);
+ if (RT_SUCCESS(rc))
+ g_cTimeSyncVerbosity = uValue;
+ }
+ VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
+ }
+
+ if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
+ rc = VINF_SUCCESS;
+ return rc;
+#else
+ /* Nothing to do here yet. */
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * Displays a verbose message based on the currently
+ * set timesync verbosity level.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+static void vgsvcTimeSyncLog(unsigned iLevel, const char *pszFormat, ...)
+{
+ if (iLevel <= g_cTimeSyncVerbosity)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ VGSvcLogV(pszFormat, va);
+ va_end(va);
+ }
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnOption}
+ */
+static DECLCALLBACK(int) vgsvcTimeSyncOption(const char **ppszShort, int argc, char **argv, int *pi)
+{
+ int rc = VINF_SUCCESS;
+ if (ppszShort)
+ rc = -1 ;/* no short options */
+ else if (!strcmp(argv[*pi], "--timesync-interval"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncInterval, 50, UINT32_MAX - 1);
+ else if (!strcmp(argv[*pi], "--timesync-min-adjust"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsTimeSyncMinAdjust, 0, 3600000);
+ else if (!strcmp(argv[*pi], "--timesync-latency-factor"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncLatencyFactor, 1, 1024);
+ else if (!strcmp(argv[*pi], "--timesync-max-latency"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsTimeSyncMaxLatency, 1, 3600000);
+ else if (!strcmp(argv[*pi], "--timesync-set-threshold"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000); /* a week */
+ else if (!strcmp(argv[*pi], "--timesync-set-start"))
+ g_fTimeSyncSetOnStart = true;
+ else if (!strcmp(argv[*pi], "--timesync-no-set-start"))
+ g_fTimeSyncSetOnStart = false;
+ else if (!strcmp(argv[*pi], "--timesync-set-on-restore"))
+ g_fTimeSyncSetOnRestore = true;
+ else if (!strcmp(argv[*pi], "--timesync-no-set-on-restore"))
+ g_fTimeSyncSetOnRestore = false;
+ else if (!strcmp(argv[*pi], "--timesync-verbosity"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cTimeSyncVerbosity, 0 /*uMin*/, 255 /*uMax*/);
+ else
+ rc = -1;
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vgsvcTimeSyncInit(void)
+{
+ /*
+ * If not specified, find the right interval default.
+ * Then create the event sem to block on.
+ */
+ if (!g_TimeSyncInterval)
+ g_TimeSyncInterval = g_DefaultInterval * 1000;
+ if (!g_TimeSyncInterval)
+ g_TimeSyncInterval = 10 * 1000;
+
+ VbglR3GetSessionId(&g_idTimeSyncSession);
+ /* The status code is ignored as this information is not available with VBox < 3.2.10. */
+
+ int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
+ AssertRC(rc);
+#ifdef RT_OS_WINDOWS
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Adjust privileges of this process so we can make system time adjustments.
+ */
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &g_hTokenProcess))
+ {
+ TOKEN_PRIVILEGES tkPriv;
+ RT_ZERO(tkPriv);
+ tkPriv.PrivilegeCount = 1;
+ tkPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkPriv.Privileges[0].Luid))
+ {
+ DWORD cbRet = sizeof(g_TkOldPrivileges);
+ if (AdjustTokenPrivileges(g_hTokenProcess, FALSE, &tkPriv, sizeof(TOKEN_PRIVILEGES), &g_TkOldPrivileges, &cbRet))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ VGSvcError("vgsvcTimeSyncInit: Adjusting token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
+ dwErr, rc);
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ VGSvcError("vgsvcTimeSyncInit: Looking up token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
+ dwErr, rc);
+ }
+ if (RT_FAILURE(rc))
+ {
+ CloseHandle(g_hTokenProcess);
+ g_hTokenProcess = NULL;
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ VGSvcError("vgsvcTimeSyncInit: Opening process token (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
+ dwErr, rc);
+ g_hTokenProcess = NULL;
+ }
+ }
+
+ if (g_pfnGetSystemTimeAdjustment)
+ {
+ if (g_pfnGetSystemTimeAdjustment(&g_dwWinTimeAdjustment, &g_dwWinTimeIncrement, &g_bWinTimeAdjustmentDisabled))
+ vgsvcTimeSyncLog(0, "vgsvcTimeSyncInit: Initially %ld (100ns) units per %ld (100 ns) units interval, disabled=%d\n",
+ g_dwWinTimeAdjustment, g_dwWinTimeIncrement, g_bWinTimeAdjustmentDisabled ? 1 : 0);
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ VGSvcError("vgsvcTimeSyncInit: Could not get time adjustment values! Last error: %ld!\n", dwErr);
+ }
+ }
+#endif /* RT_OS_WINDOWS */
+
+ return rc;
+}
+
+
+/**
+ * Try adjusting the time using adjtime or similar.
+ *
+ * @returns true on success, false on failure.
+ *
+ * @param pDrift The time adjustment.
+ */
+static bool vgsvcTimeSyncAdjust(PCRTTIMESPEC pDrift)
+{
+#ifdef RT_OS_WINDOWS
+/** @todo r=bird: g_hTokenProcess cannot be NULL here.
+ * vgsvcTimeSyncInit will fail and the service will not be started with
+ * it being NULL. vgsvcTimeSyncInit OTOH will *NOT* be called until the
+ * service thread has terminated. If anything
+ * else is the case, there is buggy code somewhere.*/
+ if (g_hTokenProcess == NULL) /* Is the token already closed when shutting down? */
+ return false;
+
+ /* The API appeared in NT 3.50. */
+ if ( !g_pfnSetSystemTimeAdjustment
+ || !g_pfnGetSystemTimeAdjustment)
+ return false;
+
+ DWORD dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwWinTimeIncrement;
+ BOOL fWinTimeAdjustmentDisabled;
+ if (g_pfnGetSystemTimeAdjustment(&dwWinTimeAdjustment, &dwWinTimeIncrement, &fWinTimeAdjustmentDisabled))
+ {
+ DWORD dwDiffMax = g_dwWinTimeAdjustment * 0.50;
+ DWORD dwDiffNew = dwWinTimeAdjustment * 0.10;
+
+ if (RTTimeSpecGetMilli(pDrift) > 0)
+ {
+ dwWinNewTimeAdjustment = dwWinTimeAdjustment + dwDiffNew;
+ if (dwWinNewTimeAdjustment > (g_dwWinTimeAdjustment + dwDiffMax))
+ {
+ dwWinNewTimeAdjustment = g_dwWinTimeAdjustment + dwDiffMax;
+ dwDiffNew = dwDiffMax;
+ }
+ }
+ else
+ {
+ dwWinNewTimeAdjustment = dwWinTimeAdjustment - dwDiffNew;
+ if (dwWinNewTimeAdjustment < (g_dwWinTimeAdjustment - dwDiffMax))
+ {
+ dwWinNewTimeAdjustment = g_dwWinTimeAdjustment - dwDiffMax;
+ dwDiffNew = dwDiffMax;
+ }
+ }
+
+ vgsvcTimeSyncLog(3, "vgsvcTimeSyncAdjust: Drift=%lldms\n", RTTimeSpecGetMilli(pDrift));
+ vgsvcTimeSyncLog(3, "vgsvcTimeSyncAdjust: OrgTA=%ld, CurTA=%ld, NewTA=%ld, DiffNew=%ld, DiffMax=%ld\n",
+ g_dwWinTimeAdjustment, dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwDiffNew, dwDiffMax);
+ if (g_pfnSetSystemTimeAdjustment(dwWinNewTimeAdjustment, FALSE /* Periodic adjustments enabled. */))
+ {
+ g_cTimeSyncErrors = 0;
+ return true;
+ }
+
+ if (g_cTimeSyncErrors++ < 10)
+ VGSvcError("vgsvcTimeSyncAdjust: SetSystemTimeAdjustment failed, error=%u\n", GetLastError());
+ }
+ else if (g_cTimeSyncErrors++ < 10)
+ VGSvcError("vgsvcTimeSyncAdjust: GetSystemTimeAdjustment failed, error=%ld\n", GetLastError());
+
+#elif defined(RT_OS_OS2) || defined(RT_OS_HAIKU)
+ /* No API for doing gradual time adjustments. */
+
+#else /* PORTME */
+ /*
+ * Try using adjtime(), most unix-like systems have this.
+ */
+ struct timeval tv;
+ RTTimeSpecGetTimeval(pDrift, &tv);
+ if (adjtime(&tv, NULL) == 0)
+ {
+ vgsvcTimeSyncLog(1, "vgsvcTimeSyncAdjust: adjtime by %RDtimespec\n", pDrift);
+ g_cTimeSyncErrors = 0;
+ return true;
+ }
+#endif
+
+ /* failed */
+ return false;
+}
+
+
+/**
+ * Cancels any pending time adjustment.
+ *
+ * Called when we've caught up and before calls to vgsvcTimeSyncSet.
+ */
+static void vgsvcTimeSyncCancelAdjust(void)
+{
+#ifdef RT_OS_WINDOWS
+/** @todo r=bird: g_hTokenProcess cannot be NULL here. See argumentation in
+ * vgsvcTimeSyncAdjust. */
+ if (g_hTokenProcess == NULL) /* No process token (anymore)? */
+ return;
+ if (!g_pfnSetSystemTimeAdjustment)
+ return;
+ if (g_pfnSetSystemTimeAdjustment(0, TRUE /* Periodic adjustments disabled. */))
+ vgsvcTimeSyncLog(5, "vgsvcTimeSyncCancelAdjust: Windows Time Adjustment is now disabled.\n");
+ else if (g_cTimeSyncErrors++ < 10)
+ VGSvcError("vgsvcTimeSyncCancelAdjust: SetSystemTimeAdjustment(,disable) failed, error=%u\n", GetLastError());
+#endif /* !RT_OS_WINDOWS */
+}
+
+
+/**
+ * Set the wall clock to compensate for drift.
+ *
+ * @returns true on success, false on failure.
+ *
+ * @param pDrift The time adjustment.
+ */
+static void vgsvcTimeSyncSet(PCRTTIMESPEC pDrift)
+{
+ /*
+ * Query the current time, adjust it by adding the drift and set it.
+ */
+ RTTIMESPEC NewGuestTime;
+ int rc = RTTimeSet(RTTimeSpecAdd(RTTimeNow(&NewGuestTime), pDrift));
+ if (RT_SUCCESS(rc))
+ {
+ /* Succeeded - reset the error count and log the change. */
+ g_cTimeSyncErrors = 0;
+
+ if (g_cTimeSyncVerbosity >= 1)
+ {
+ char sz[64];
+ RTTIME Time;
+ vgsvcTimeSyncLog(1, "time set to %s\n", RTTimeToString(RTTimeExplode(&Time, &NewGuestTime), sz, sizeof(sz)));
+#ifdef DEBUG
+ RTTIMESPEC Tmp;
+ vgsvcTimeSyncLog(3, " now %s\n", RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
+#endif
+ }
+ }
+ else if (g_cTimeSyncErrors++ < 10)
+ VGSvcError("vgsvcTimeSyncSet: RTTimeSet(%RDtimespec) failed: %Rrc\n", &NewGuestTime, rc);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+DECLCALLBACK(int) vgsvcTimeSyncWorker(bool volatile *pfShutdown)
+{
+ RTTIME Time;
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Tell the control thread that it can continue spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /*
+ * Initialize the last host and guest times to prevent log message.
+ * We also track whether we set the time in the previous loop.
+ */
+ RTTIMESPEC HostLast;
+ if (RT_FAILURE(VbglR3GetHostTime(&HostLast)))
+ RTTimeSpecSetNano(&HostLast, 0);
+ RTTIMESPEC GuestLast;
+ RTTimeNow(&GuestLast);
+ bool fSetTimeLastLoop = false;
+
+ /*
+ * The Work Loop.
+ */
+ for (;;)
+ {
+ /*
+ * Try to get a reliable time reading.
+ */
+ int cTries = 3;
+ do
+ {
+ /*
+ * Query the session id (first to keep lantency low) and the time.
+ */
+ uint64_t idNewSession = g_idTimeSyncSession;
+ if (g_fTimeSyncSetOnRestore)
+ VbglR3GetSessionId(&idNewSession);
+
+ RTTIMESPEC GuestNow0;
+ RTTimeNow(&GuestNow0);
+
+ RTTIMESPEC HostNow;
+ int rc2 = VbglR3GetHostTime(&HostNow);
+ if (RT_FAILURE(rc2))
+ {
+ if (g_cTimeSyncErrors++ < 10)
+ VGSvcError("vgsvcTimeSyncWorker: VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
+ break;
+ }
+
+ RTTIMESPEC GuestNow;
+ RTTimeNow(&GuestNow);
+
+ /*
+ * Calc latency and check if it's ok.
+ */
+ RTTIMESPEC GuestElapsed = GuestNow;
+ RTTimeSpecSub(&GuestElapsed, &GuestNow0);
+ if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_cMsTimeSyncMaxLatency)
+ {
+ /*
+ * If we were just restored, set the adjustment threshold to zero to force a resync.
+ */
+ uint32_t TimeSyncSetThreshold = g_TimeSyncSetThreshold;
+ if ( g_fTimeSyncSetOnRestore
+ && idNewSession != g_idTimeSyncSession)
+ {
+ vgsvcTimeSyncLog(2, "vgsvcTimeSyncWorker: The VM session ID changed, forcing resync.\n");
+ g_idTimeSyncSession = idNewSession;
+ TimeSyncSetThreshold = 0;
+ }
+
+ /*
+ * Calculate the adjustment threshold and the current drift.
+ */
+ uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
+ if (MinAdjust < g_cMsTimeSyncMinAdjust)
+ MinAdjust = g_cMsTimeSyncMinAdjust;
+
+ RTTIMESPEC Drift = HostNow;
+ RTTimeSpecSub(&Drift, &GuestNow);
+ if (RTTimeSpecGetMilli(&Drift) < 0)
+ MinAdjust += g_cMsTimeSyncMinAdjust; /* extra buffer against moving time backwards. */
+
+ RTTIMESPEC AbsDrift = Drift;
+ RTTimeSpecAbsolute(&AbsDrift);
+
+ if (g_cTimeSyncVerbosity >= 4)
+ {
+ char sz1[64];
+ char sz2[64];
+ vgsvcTimeSyncLog(4, "vgsvcTimeSyncWorker: Host: %s (MinAdjust: %RU32 ms), Guest: %s => %RDtimespec drift\n",
+ RTTimeToString(RTTimeExplode(&Time, &HostNow), sz1, sizeof(sz1)), MinAdjust,
+ RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz2, sizeof(sz2)), &Drift);
+ }
+
+ bool fSetTimeInThisLoop = false;
+ uint64_t AbsDriftMilli = RTTimeSpecGetMilli(&AbsDrift);
+ if ( AbsDriftMilli > MinAdjust
+ || g_fTimeSyncSetOnStart)
+ {
+ /*
+ * Ok, the drift is above the threshold.
+ *
+ * Try a gradual adjustment first, if that fails or the drift is
+ * too big, fall back on just setting the time.
+ */
+ if ( AbsDriftMilli > TimeSyncSetThreshold
+ || g_fTimeSyncSetOnStart
+ || !vgsvcTimeSyncAdjust(&Drift))
+ {
+ vgsvcTimeSyncCancelAdjust();
+ vgsvcTimeSyncSet(&Drift);
+ fSetTimeInThisLoop = true;
+ }
+
+ /*
+ * Log radical host time changes.
+ */
+ int64_t cNsHostDelta = RTTimeSpecGetNano(&HostNow) - RTTimeSpecGetNano(&HostLast);
+ if ((uint64_t)RT_ABS(cNsHostDelta) > RT_NS_1HOUR / 2)
+ vgsvcTimeSyncLog(0, "vgsvcTimeSyncWorker: Radical host time change: %'RI64ns (HostNow=%RDtimespec HostLast=%RDtimespec)\n",
+ cNsHostDelta, &HostNow, &HostLast);
+ }
+ else
+ vgsvcTimeSyncCancelAdjust();
+ HostLast = HostNow;
+
+ /*
+ * Log radical guest time changes (we could be the cause of these, mind).
+ * Note! Right now we don't care about an extra log line after we called
+ * vgsvcTimeSyncSet. fSetTimeLastLoop helps show it though.
+ */
+ int64_t cNsGuestDelta = RTTimeSpecGetNano(&GuestNow) - RTTimeSpecGetNano(&GuestLast);
+ if ((uint64_t)RT_ABS(cNsGuestDelta) > RT_NS_1HOUR / 2)
+ vgsvcTimeSyncLog(0, "vgsvcTimeSyncWorker: Radical guest time change: %'RI64ns (GuestNow=%RDtimespec GuestLast=%RDtimespec fSetTimeLastLoop=%RTbool)\n",
+ cNsGuestDelta, &GuestNow, &GuestLast, fSetTimeLastLoop);
+ GuestLast = GuestNow;
+ fSetTimeLastLoop = fSetTimeInThisLoop;
+ break;
+ }
+ vgsvcTimeSyncLog(3, "vgsvcTimeSyncWorker: %RDtimespec: latency too high (%RDtimespec, max %ums) sleeping 1s\n",
+ &GuestNow, &GuestElapsed, g_cMsTimeSyncMaxLatency);
+ RTThreadSleep(1000);
+ } while (--cTries > 0);
+
+ /* Clear the set-next/set-start flag. */
+ g_fTimeSyncSetOnStart = false;
+
+ /*
+ * Block for a while.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
+ if (*pfShutdown)
+ break;
+ if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
+ {
+ VGSvcError("vgsvcTimeSyncWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
+ rc = rc2;
+ break;
+ }
+ }
+
+ vgsvcTimeSyncCancelAdjust();
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vgsvcTimeSyncStop(void)
+{
+ if (g_TimeSyncEvent != NIL_RTSEMEVENTMULTI)
+ RTSemEventMultiSignal(g_TimeSyncEvent);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vgsvcTimeSyncTerm(void)
+{
+#ifdef RT_OS_WINDOWS
+ /*
+ * Restore the SE_SYSTEMTIME_NAME token privileges (if init succeeded).
+ */
+ if (g_hTokenProcess)
+ {
+ if (!AdjustTokenPrivileges(g_hTokenProcess, FALSE, &g_TkOldPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ VGSvcError("vgsvcTimeSyncTerm: Restoring token privileges (SE_SYSTEMTIME_NAME) failed with code %u!\n", dwErr);
+ }
+ CloseHandle(g_hTokenProcess);
+ g_hTokenProcess = NULL;
+ }
+#endif /* !RT_OS_WINDOWS */
+
+ if (g_TimeSyncEvent != NIL_RTSEMEVENTMULTI)
+ {
+ RTSemEventMultiDestroy(g_TimeSyncEvent);
+ g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'timesync' service description.
+ */
+VBOXSERVICE g_TimeSync =
+{
+ /* pszName. */
+ "timesync",
+ /* pszDescription. */
+ "Time synchronization",
+ /* pszUsage. */
+ " [--timesync-interval <ms>] [--timesync-min-adjust <ms>]\n"
+ " [--timesync-latency-factor <x>] [--timesync-max-latency <ms>]\n"
+ " [--timesync-set-threshold <ms>]\n"
+ " [--timesync-set-start|--timesync-no-set-start]\n"
+ " [--timesync-set-on-restore|--timesync-no-set-on-restore]\n"
+ " [--timesync-verbosity <level>]"
+ ,
+ /* pszOptions. */
+ " --timesync-interval Specifies the interval at which to synchronize the\n"
+ " time with the host. The default is 10000 ms.\n"
+ " --timesync-min-adjust The minimum absolute drift value measured in\n"
+ " milliseconds to make adjustments for.\n"
+ " The default is 1000 ms on OS/2 and 100 ms elsewhere.\n"
+ " --timesync-latency-factor\n"
+ " The factor to multiply the time query latency with\n"
+ " to calculate the dynamic minimum adjust time.\n"
+ " The default is 8 times.\n"
+ " --timesync-max-latency The max host timer query latency to accept.\n"
+ " The default is 250 ms.\n"
+ " --timesync-set-threshold\n"
+ " The absolute drift threshold, given as milliseconds,\n"
+ " where to start setting the time instead of trying to\n"
+ " adjust it. The default is 20 min.\n"
+ " --timesync-set-start, --timesync-no-set-start \n"
+ " Set the time when starting the time sync service.\n"
+#ifdef RT_OS_OS2
+ " Default: --timesync-set-start\n"
+#else
+ " Default: --timesync-no-set-start\n"
+#endif
+ " --timesync-set-on-restore, --timesync-no-set-on-restore\n"
+ " Whether to immediately set the time when the VM is\n"
+ " restored or not. Default: --timesync-set-on-restore\n"
+ " --timesync-verbosity Sets the verbosity level. Defaults to service wide\n"
+ " verbosity level.\n"
+ ,
+ /* methods */
+ vgsvcTimeSyncPreInit,
+ vgsvcTimeSyncOption,
+ vgsvcTimeSyncInit,
+ vgsvcTimeSyncWorker,
+ vgsvcTimeSyncStop,
+ vgsvcTimeSyncTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
new file mode 100644
index 00000000..bc91b710
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
@@ -0,0 +1,1763 @@
+/* $Id: VBoxServiceToolBox.cpp $ */
+/** @file
+ * VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/symlink.h>
+
+#ifndef RT_OS_WINDOWS
+# include <sys/stat.h> /* need umask */
+#endif
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/version.h>
+
+#include <VBox/GuestHost/GuestControl.h>
+
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceToolBox.h"
+#include "VBoxServiceUtils.h"
+
+using namespace guestControl;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Generic option indices for commands. */
+enum
+{
+ VBOXSERVICETOOLBOXOPT_MACHINE_READABLE = 1000,
+ VBOXSERVICETOOLBOXOPT_VERBOSE
+};
+
+/** Options indices for "vbox_cat". */
+typedef enum VBOXSERVICETOOLBOXCATOPT
+{
+ VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED = 1000
+} VBOXSERVICETOOLBOXCATOPT;
+
+/** Flags for "vbox_ls". */
+typedef enum VBOXSERVICETOOLBOXLSFLAG
+{
+ VBOXSERVICETOOLBOXLSFLAG_NONE,
+ VBOXSERVICETOOLBOXLSFLAG_RECURSIVE,
+ VBOXSERVICETOOLBOXLSFLAG_SYMLINKS
+} VBOXSERVICETOOLBOXLSFLAG;
+
+/** Flags for fs object output. */
+typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
+{
+ VBOXSERVICETOOLBOXOUTPUTFLAG_NONE,
+ VBOXSERVICETOOLBOXOUTPUTFLAG_LONG,
+ VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE
+} VBOXSERVICETOOLBOXOUTPUTFLAG;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a tool handler function. */
+typedef RTEXITCODE (*PFNHANDLER)(int , char **);
+
+/** Definition for a specific toolbox tool. */
+typedef struct VBOXSERVICETOOLBOXTOOL
+{
+ /** Friendly name of the tool. */
+ const char *pszName;
+ /** Main handler to be invoked to use the tool. */
+ RTEXITCODE (*pfnHandler)(int argc, char **argv);
+ /** Conversion routine to convert the tool's exit code back to an IPRT rc. Optional.
+ *
+ * @todo r=bird: You better revert this, i.e. having pfnHandler return a VBox
+ * status code and have a routine for converting it to RTEXITCODE.
+ * Unless, what you really want to do here is to get a cached status, in
+ * which case you better call it what it is.
+ */
+ int (*pfnExitCodeConvertToRc)(RTEXITCODE rcExit);
+} VBOXSERVICETOOLBOXTOOL;
+/** Pointer to a const tool definition. */
+typedef VBOXSERVICETOOLBOXTOOL const *PCVBOXSERVICETOOLBOXTOOL;
+
+/**
+ * An file/directory entry. Used to cache
+ * file names/paths for later processing.
+ */
+typedef struct VBOXSERVICETOOLBOXPATHENTRY
+{
+ /** Our node. */
+ RTLISTNODE Node;
+ /** Name of the entry. */
+ char *pszName;
+} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
+
+typedef struct VBOXSERVICETOOLBOXDIRENTRY
+{
+ /** Our node. */
+ RTLISTNODE Node;
+ /** The actual entry. */
+ RTDIRENTRYEX dirEntry;
+} VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;
+
+/** ID cache entry. */
+typedef struct VGSVCTOOLBOXUIDENTRY
+{
+ /** The identifier name. */
+ uint32_t id;
+ /** Set if UID, clear if GID. */
+ bool fIsUid;
+ /** The name. */
+ char szName[128 - 4 - 1];
+} VGSVCTOOLBOXUIDENTRY;
+typedef VGSVCTOOLBOXUIDENTRY *PVGSVCTOOLBOXUIDENTRY;
+
+
+/** ID cache. */
+typedef struct VGSVCTOOLBOXIDCACHE
+{
+ /** Number of valid cache entries. */
+ uint32_t cEntries;
+ /** The next entry to replace. */
+ uint32_t iNextReplace;
+ /** The cache entries. */
+ VGSVCTOOLBOXUIDENTRY aEntries[16];
+} VGSVCTOOLBOXIDCACHE;
+typedef VGSVCTOOLBOXIDCACHE *PVGSVCTOOLBOXIDCACHE;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static RTEXITCODE vgsvcToolboxCat(int argc, char **argv);
+static RTEXITCODE vgsvcToolboxLs(int argc, char **argv);
+static RTEXITCODE vgsvcToolboxRm(int argc, char **argv);
+static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv);
+static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv);
+static RTEXITCODE vgsvcToolboxStat(int argc, char **argv);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Tool definitions. */
+static VBOXSERVICETOOLBOXTOOL const g_aTools[] =
+{
+ { VBOXSERVICE_TOOL_CAT, vgsvcToolboxCat , NULL },
+ { VBOXSERVICE_TOOL_LS, vgsvcToolboxLs , NULL },
+ { VBOXSERVICE_TOOL_RM, vgsvcToolboxRm , NULL },
+ { VBOXSERVICE_TOOL_MKTEMP, vgsvcToolboxMkTemp, NULL },
+ { VBOXSERVICE_TOOL_MKDIR, vgsvcToolboxMkDir , NULL },
+ { VBOXSERVICE_TOOL_STAT, vgsvcToolboxStat , NULL }
+};
+
+
+
+
+/**
+ * Displays a common header for all help text to stdout.
+ */
+static void vgsvcToolboxShowUsageHeader(void)
+{
+ RTPrintf(VBOX_PRODUCT " Guest Toolbox Version "
+ VBOX_VERSION_STRING "\n"
+ "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n");
+ RTPrintf("Usage:\n\n");
+}
+
+
+/**
+ * Displays a help text to stdout.
+ */
+static void vgsvcToolboxShowUsage(void)
+{
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf(" VBoxService [--use-toolbox] vbox_<command> [<general options>] <parameters>\n\n"
+ "General options:\n\n"
+ " --machinereadable produce all output in machine-readable form\n"
+ " -V print version number and exit\n"
+ "\n"
+ "Commands:\n\n"
+ " vbox_cat [<general options>] <file>...\n"
+ " vbox_ls [<general options>] [--dereference|-L] [-l] [-R]\n"
+ " [--verbose|-v] [<file>...]\n"
+ " vbox_rm [<general options>] [-r|-R] <file>...\n"
+ " vbox_mktemp [<general options>] [--directory|-d] [--mode|-m <mode>]\n"
+ " [--secure|-s] [--tmpdir|-t <path>] <template>\n"
+ " vbox_mkdir [<general options>] [--mode|-m <mode>] [--parents|-p]\n"
+ " [--verbose|-v] <directory>...\n"
+ " vbox_stat [<general options>] [--file-system|-f]\n"
+ " [--dereference|-L] [--terse|-t] [--verbose|-v] <file>...\n"
+ "\n");
+}
+
+
+/**
+ * Displays the program's version number.
+ */
+static void vgsvcToolboxShowVersion(void)
+{
+ RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+}
+
+
+/**
+ * Initializes the parseable stream(s).
+ *
+ * @return IPRT status code.
+ */
+static int vgsvcToolboxStrmInit(void)
+{
+ /* Set stdout's mode to binary. This is required for outputting all the machine-readable
+ * data correctly. */
+ int rc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Current code set, not changed */);
+ if (RT_FAILURE(rc))
+ RTMsgError("Unable to set stdout to binary mode, rc=%Rrc\n", rc);
+
+ return rc;
+}
+
+
+/**
+ * Prints a parseable stream header which contains the actual tool
+ * which was called/used along with its stream version.
+ *
+ * @param pszToolName Name of the tool being used, e.g. "vbt_ls".
+ * @param uVersion Stream version name. Handy for distinguishing
+ * different stream versions later.
+ */
+static void vgsvcToolboxPrintStrmHeader(const char *pszToolName, uint32_t uVersion)
+{
+ AssertPtrReturnVoid(pszToolName);
+ RTPrintf("hdr_id=%s%chdr_ver=%u%c", pszToolName, 0, uVersion, 0);
+}
+
+
+/**
+ * Prints a standardized termination sequence indicating that the
+ * parseable stream just ended.
+ *
+ */
+static void vgsvcToolboxPrintStrmTermination()
+{
+ RTPrintf("%c%c%c%c", 0, 0, 0, 0);
+}
+
+
+/**
+ * Parse a file mode string from the command line (currently octal only)
+ * and print an error message and return an error if necessary.
+ */
+static int vgsvcToolboxParseMode(const char *pcszMode, RTFMODE *pfMode)
+{
+ int rc = RTStrToUInt32Ex(pcszMode, NULL, 8 /* Base */, pfMode);
+ if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
+ RTMsgError("Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n", pcszMode);
+ return rc;
+}
+
+
+/**
+ * Destroys a path buffer list.
+ *
+ * @return IPRT status code.
+ * @param pList Pointer to list to destroy.
+ */
+static void vgsvcToolboxPathBufDestroy(PRTLISTNODE pList)
+{
+ AssertPtr(pList);
+ /** @todo use RTListForEachSafe */
+ PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
+ while (pNode)
+ {
+ PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
+ ? NULL
+ : RTListNodeGetNext(&pNode->Node, VBOXSERVICETOOLBOXPATHENTRY, Node);
+ RTListNodeRemove(&pNode->Node);
+
+ RTStrFree(pNode->pszName);
+
+ RTMemFree(pNode);
+ pNode = pNext;
+ }
+}
+
+
+/**
+ * Adds a path entry (file/directory/whatever) to a given path buffer list.
+ *
+ * @return IPRT status code.
+ * @param pList Pointer to list to add entry to.
+ * @param pszName Name of entry to add.
+ */
+static int vgsvcToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
+{
+ AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
+ if (pNode)
+ {
+ pNode->pszName = RTStrDup(pszName);
+ AssertPtr(pNode->pszName);
+
+ RTListAppend(pList, &pNode->Node);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Performs the actual output operation of "vbox_cat".
+ *
+ * @return IPRT status code.
+ * @param hInput Handle of input file (if any) to use;
+ * else stdin will be used.
+ * @param hOutput Handle of output file (if any) to use;
+ * else stdout will be used.
+ */
+static int vgsvcToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
+{
+ int rc = VINF_SUCCESS;
+ if (hInput == NIL_RTFILE)
+ {
+ rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
+ if (RT_FAILURE(rc))
+ RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
+ }
+
+ if (hOutput == NIL_RTFILE)
+ {
+ rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
+ if (RT_FAILURE(rc))
+ RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t abBuf[_64K];
+ size_t cbRead;
+ for (;;)
+ {
+ rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
+ if (RT_SUCCESS(rc) && cbRead > 0)
+ {
+ rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Error while writing output, rc=%Rrc\n", rc);
+ break;
+ }
+ }
+ else
+ {
+ if (rc == VERR_BROKEN_PIPE)
+ rc = VINF_SUCCESS;
+ else if (RT_FAILURE(rc))
+ RTMsgError("Error while reading input, rc=%Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/** @todo Document options! */
+static char g_paszCatHelp[] =
+ " VBoxService [--use-toolbox] vbox_cat [<general options>] <file>...\n\n"
+ "Concatenate files, or standard input, to standard output.\n"
+ "\n";
+
+
+/**
+ * Main function for tool "vbox_cat".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE vgsvcToolboxCat(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ /* Sorted by short ops. */
+ { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
+ { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
+ { NULL, 'e', RTGETOPT_REQ_NOTHING},
+ { NULL, 'E', RTGETOPT_REQ_NOTHING},
+ { "--flags", 'f', RTGETOPT_REQ_STRING},
+ { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
+ { "--number", 'n', RTGETOPT_REQ_NOTHING},
+ { "--output", 'o', RTGETOPT_REQ_STRING},
+ { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
+ { NULL, 't', RTGETOPT_REQ_NOTHING},
+ { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
+ { NULL, 'u', RTGETOPT_REQ_NOTHING},
+ { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, 0 /*fFlags*/);
+
+ int rc = VINF_SUCCESS;
+
+ const char *pszOutput = NULL;
+ RTFILE hOutput = NIL_RTFILE;
+ uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
+ | RTFILE_O_WRITE
+ | RTFILE_O_DENY_WRITE;
+
+ /* Init directory list. */
+ RTLISTANCHOR inputList;
+ RTListInit(&inputList);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'E':
+ case 'n':
+ case 's':
+ case 't':
+ case 'T':
+ case 'v':
+ RTMsgError("Sorry, option '%s' is not implemented yet!\n",
+ ValueUnion.pDef->pszLong);
+ rc = VERR_INVALID_PARAMETER;
+ break;
+
+ case 'h':
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf("%s", g_paszCatHelp);
+ return RTEXITCODE_SUCCESS;
+
+ case 'o':
+ pszOutput = ValueUnion.psz;
+ break;
+
+ case 'u':
+ /* Ignored. */
+ break;
+
+ case 'V':
+ vgsvcToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
+ fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* Add file(s) to buffer. This enables processing multiple paths
+ * at once.
+ *
+ * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+ * processing this loop it's safe to immediately exit on syntax errors
+ * or showing the help text (see above). */
+ rc = vgsvcToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pszOutput)
+ {
+ rc = RTFileOpen(&hOutput, pszOutput, fFlags);
+ if (RT_FAILURE(rc))
+ RTMsgError("Could not create output file '%s', rc=%Rrc\n", pszOutput, rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Process each input file. */
+ RTFILE hInput = NIL_RTFILE;
+ PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+ RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
+ {
+ rc = RTFileOpen(&hInput, pNodeIt->pszName,
+ RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vgsvcToolboxCatOutput(hInput, hOutput);
+ RTFileClose(hInput);
+ }
+ else
+ {
+ PCRTSTATUSMSG pMsg = RTErrGet(rc);
+ if (pMsg)
+ RTMsgError("Could not open input file '%s': %s\n", pNodeIt->pszName, pMsg->pszMsgFull);
+ else
+ RTMsgError("Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /* If no input files were defined, process stdin. */
+ if (RTListNodeIsFirst(&inputList, &inputList))
+ rc = vgsvcToolboxCatOutput(hInput, hOutput);
+ }
+ }
+
+ if (hOutput != NIL_RTFILE)
+ RTFileClose(hOutput);
+ vgsvcToolboxPathBufDestroy(&inputList);
+
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_ACCESS_DENIED:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED;
+
+ case VERR_FILE_NOT_FOUND:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND;
+
+ case VERR_PATH_NOT_FOUND:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND;
+
+ case VERR_SHARING_VIOLATION:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION;
+
+ case VERR_IS_A_DIRECTORY:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY;
+
+ default:
+#ifdef DEBUG_andy
+ AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
+#endif
+ break;
+ }
+
+ return RTEXITCODE_FAILURE;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Resolves the UID to a name as best as we can.
+ *
+ * @returns Read-only name string. Only valid till the next cache call.
+ * @param pIdCache The ID cache.
+ * @param uid The UID to resolve.
+ * @param pszEntry The filename of the UID.
+ * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
+ */
+static const char *vgsvcToolboxIdCacheGetUidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTUID uid,
+ const char *pszEntry, const char *pszRelativeTo)
+{
+ /* Check cached entries. */
+ for (uint32_t i = 0; i < pIdCache->cEntries; i++)
+ if ( pIdCache->aEntries[i].id == uid
+ && pIdCache->aEntries[i].fIsUid)
+ return pIdCache->aEntries[i].szName;
+
+ /* Miss. */
+ RTFSOBJINFO ObjInfo;
+ RT_ZERO(ObjInfo); /* shut up msc */
+ int rc;
+ if (!pszRelativeTo)
+ rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
+ else
+ {
+ char szPath[RTPATH_MAX];
+ rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
+ if (RT_SUCCESS(rc))
+ rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
+ }
+
+ if ( RT_SUCCESS(rc)
+ && ObjInfo.Attr.u.UnixOwner.uid == uid)
+ {
+ uint32_t i = pIdCache->cEntries;
+ if (i < RT_ELEMENTS(pIdCache->aEntries))
+ pIdCache->cEntries = i + 1;
+ else
+ i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
+ pIdCache->aEntries[i].id = uid;
+ pIdCache->aEntries[i].fIsUid = true;
+ RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixOwner.szName);
+ return pIdCache->aEntries[i].szName;
+ }
+ return "";
+}
+
+
+/**
+ * Resolves the GID to a name as best as we can.
+ *
+ * @returns Read-only name string. Only valid till the next cache call.
+ * @param pIdCache The ID cache.
+ * @param gid The GID to resolve.
+ * @param pszEntry The filename of the GID.
+ * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
+ */
+static const char *vgsvcToolboxIdCacheGetGidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTGID gid,
+ const char *pszEntry, const char *pszRelativeTo)
+{
+ /* Check cached entries. */
+ for (uint32_t i = 0; i < pIdCache->cEntries; i++)
+ if ( pIdCache->aEntries[i].id == gid
+ && !pIdCache->aEntries[i].fIsUid)
+ return pIdCache->aEntries[i].szName;
+
+ /* Miss. */
+ RTFSOBJINFO ObjInfo;
+ RT_ZERO(ObjInfo); /* shut up msc */
+ int rc;
+ if (!pszRelativeTo)
+ rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
+ else
+ {
+ char szPath[RTPATH_MAX];
+ rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
+ if (RT_SUCCESS(rc))
+ rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
+ }
+
+ if ( RT_SUCCESS(rc)
+ && ObjInfo.Attr.u.UnixGroup.gid == gid)
+ {
+ uint32_t i = pIdCache->cEntries;
+ if (i < RT_ELEMENTS(pIdCache->aEntries))
+ pIdCache->cEntries = i + 1;
+ else
+ i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
+ pIdCache->aEntries[i].id = gid;
+ pIdCache->aEntries[i].fIsUid = false;
+ RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixGroup.szName);
+ return pIdCache->aEntries[i].szName;
+ }
+ return "";
+}
+
+
+/**
+ * Prints information (based on given flags) of a file system object (file/directory/...)
+ * to stdout.
+ *
+ * @return IPRT status code.
+ * @param pszName Object name.
+ * @param cchName Length of pszName.
+ * @param fOutputFlags Output / handling flags of type
+ * VBOXSERVICETOOLBOXOUTPUTFLAG.
+ * @param pszRelativeTo What pszName is relative to.
+ * @param pIdCache The ID cache.
+ * @param pObjInfo Pointer to object information.
+ */
+static int vgsvcToolboxPrintFsInfo(const char *pszName, size_t cchName, uint32_t fOutputFlags, const char *pszRelativeTo,
+ PVGSVCTOOLBOXIDCACHE pIdCache, PRTFSOBJINFO pObjInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(cchName, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+
+ RTFMODE fMode = pObjInfo->Attr.fMode;
+ char chFileType;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FIFO: chFileType = 'f'; break;
+ case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
+ case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
+ case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
+ case RTFS_TYPE_FILE: chFileType = '-'; break;
+ case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
+ case RTFS_TYPE_SOCKET: chFileType = 's'; break;
+ case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
+ default: chFileType = '?'; break;
+ }
+ /** @todo sticy bits++ */
+
+/** @todo r=bird: turns out the host doesn't use or need cname_len, so perhaps we could drop it? */
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
+ {
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ RTPrintf("ftype=%c%cnode_id=%RU64%inode_dev=%RU32%ccname_len=%zu%cname=%s%c",
+ chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
+ (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0, cchName, 0, pszName, 0);
+ RTPrintf("%c%c", 0, 0);
+ }
+ else
+ RTPrintf("%c %#18llx %3zu %s\n", chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cchName, pszName);
+ }
+ else
+ {
+ char szTimeBirth[RTTIME_STR_LEN];
+ char szTimeChange[RTTIME_STR_LEN];
+ char szTimeModification[RTTIME_STR_LEN];
+ char szTimeAccess[RTTIME_STR_LEN];
+
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ RTPrintf("ftype=%c%c", chFileType, 0);
+ if (pObjInfo->Attr.u.Unix.INodeId || pObjInfo->Attr.u.Unix.INodeIdDevice)
+ RTPrintf("node_id=%RU64%cinode_dev=%RU32%c", (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
+ (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0);
+ RTPrintf("owner_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
+ RTPrintf("group_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
+ RTPrintf("other_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
+ /** @todo sticky bits. */
+ RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
+ fMode & RTFS_DOS_READONLY ? 'R' : '-',
+ fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
+ fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
+ fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
+ fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
+ fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
+ fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
+ fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
+ fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
+ fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
+ fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
+ fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
+ fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
+ fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
+ RTPrintf("hlinks=%RU32%cst_size=%RI64%calloc=%RI64%c",
+ pObjInfo->Attr.u.Unix.cHardlinks, 0,
+ pObjInfo->cbObject, 0,
+ pObjInfo->cbAllocated, 0);
+ RTPrintf("st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
+ RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)), 0,
+ RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)), 0,
+ RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)), 0,
+ RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)), 0);
+ if (pObjInfo->Attr.u.Unix.uid != NIL_RTUID)
+ RTPrintf("uid=%RU32%cusername=%s%c", pObjInfo->Attr.u.Unix.uid, 0,
+ vgsvcToolboxIdCacheGetUidName(pIdCache, pObjInfo->Attr.u.Unix.uid, pszName, pszRelativeTo), 0);
+ if (pObjInfo->Attr.u.Unix.gid != NIL_RTGID)
+ RTPrintf("gid=%RU32%cgroupname=%s%c", pObjInfo->Attr.u.Unix.gid, 0,
+ vgsvcToolboxIdCacheGetGidName(pIdCache, pObjInfo->Attr.u.Unix.gid, pszName, pszRelativeTo), 0);
+ if ( (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode) || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
+ && pObjInfo->Attr.u.Unix.Device)
+ RTPrintf("st_rdev=%RU32%c", pObjInfo->Attr.u.Unix.Device, 0);
+ if (pObjInfo->Attr.u.Unix.GenerationId)
+ RTPrintf("st_gen=%RU32%c", pObjInfo->Attr.u.Unix.GenerationId, 0);
+ if (pObjInfo->Attr.u.Unix.fFlags)
+ RTPrintf("st_flags=%RU32%c", pObjInfo->Attr.u.Unix.fFlags, 0);
+ RTPrintf("cname_len=%zu%cname=%s%c", cchName, 0, pszName, 0);
+ RTPrintf("%c%c", 0, 0); /* End of data block. */
+ }
+ else
+ {
+ RTPrintf("%c", chFileType);
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
+ RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
+ fMode & RTFS_DOS_READONLY ? 'R' : '-',
+ fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
+ fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
+ fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
+ fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
+ fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
+ fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
+ fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
+ fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
+ fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
+ fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
+ fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
+ fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
+ fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
+ RTPrintf(" %d %4d %4d %10lld %10lld",
+ pObjInfo->Attr.u.Unix.cHardlinks,
+ pObjInfo->Attr.u.Unix.uid,
+ pObjInfo->Attr.u.Unix.gid,
+ pObjInfo->cbObject,
+ pObjInfo->cbAllocated);
+ RTPrintf(" %s %s %s %s",
+ RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)),
+ RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)),
+ RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)),
+ RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)) );
+ RTPrintf(" %2zu %s\n", cchName, pszName);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Helper routine for ls tool doing the actual parsing and output of
+ * a specified directory.
+ *
+ * @return IPRT status code.
+ * @param pszDir Directory (path) to ouptut.
+ * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
+ * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
+ * @param pIdCache The ID cache.
+ */
+static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
+{
+ AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
+
+ if (fFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ RTPrintf("dname=%s%c", pszDir, 0);
+ else if (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
+ RTPrintf("%s:\n", pszDir);
+
+ char szPathAbs[RTPATH_MAX + 1];
+ int rc = RTPathAbs(pszDir, szPathAbs, sizeof(szPathAbs));
+ if (RT_FAILURE(rc))
+ {
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ RTMsgError("Failed to retrieve absolute path of '%s', rc=%Rrc\n", pszDir, rc);
+ return rc;
+ }
+
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, szPathAbs);
+ if (RT_FAILURE(rc))
+ {
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ RTMsgError("Failed to open directory '%s', rc=%Rrc\n", szPathAbs, rc);
+ return rc;
+ }
+
+ RTLISTANCHOR dirList;
+ RTListInit(&dirList);
+
+ /* To prevent races we need to read in the directory entries once
+ * and process them afterwards: First loop is displaying the current
+ * directory's content and second loop is diving deeper into
+ * sub directories (if wanted). */
+/** @todo r=bird: Which races are these exactly??? Please, do considering that directory with half a
+ * million files in it, because this isn't going to fly well there (especially not in the recursive case)...
+ * So, this needs to be rewritten unless there is an actual race you're avoiding by doing this! */
+ do
+ {
+ RTDIRENTRYEX DirEntry;
+ rc = RTDirReadEx(hDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
+ if (pNode)
+ {
+ memcpy(&pNode->dirEntry, &DirEntry, sizeof(RTDIRENTRYEX));
+ RTListAppend(&dirList, &pNode->Node);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ /** @todo r=bird: missing DirEntry overflow handling. */
+ } while (RT_SUCCESS(rc));
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ int rc2 = RTDirClose(hDir);
+ if (RT_FAILURE(rc2))
+ {
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ RTMsgError("Failed to close dir '%s', rc=%Rrc\n", pszDir, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICETOOLBOXDIRENTRY pNodeIt;
+ RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ rc = vgsvcToolboxPrintFsInfo(pNodeIt->dirEntry.szName, pNodeIt->dirEntry.cbName, fOutputFlags,
+ szPathAbs, pIdCache, &pNodeIt->dirEntry.Info);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /* If everything went fine we do the second run (if needed) ... */
+ if ( RT_SUCCESS(rc)
+ && (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
+ {
+ /* Process all sub-directories. */
+ RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ RTFMODE fMode = pNodeIt->dirEntry.Info.Attr.fMode;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_SYMLINK:
+ if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
+ break;
+ RT_FALL_THRU();
+ case RTFS_TYPE_DIRECTORY:
+ {
+ const char *pszName = pNodeIt->dirEntry.szName;
+ if ( !RTStrICmp(pszName, ".") /** @todo r=bird: Please do explain what the upper/lower casing of '.' is! I'm really curious. */
+ || !RTStrICmp(pszName, "..")) /** @todo r=bird: There is a RTDir API for checking these. Use it! */
+ {
+ /* Skip dot directories. */
+ continue;
+ }
+
+ char szPath[RTPATH_MAX]; /** @todo r=bird: This is going to kill your stack pretty quickly if deep
+ * directory nesting. There is another buffer further up the function too.
+ * You need to share the path buffer between recursions! There should be
+ * several examples of how to efficiently traverse a tree. */
+ rc = RTPathJoin(szPath, sizeof(szPath), pszDir, pNodeIt->dirEntry.szName);
+ if (RT_SUCCESS(rc))
+ rc = vgsvcToolboxLsHandleDir(szPath, fFlags, fOutputFlags, pIdCache);
+ break;
+ }
+
+ default: /* Ignore the rest. */
+ break;
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ }
+
+ /* Clean up the mess. */
+ PVBOXSERVICETOOLBOXDIRENTRY pNode, pSafe;
+ RTListForEachSafe(&dirList, pNode, pSafe, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ RTListNodeRemove(&pNode->Node);
+ RTMemFree(pNode);
+ }
+ return rc;
+}
+
+
+/** @todo Document options! */
+static char g_paszLsHelp[] =
+ " VBoxService [--use-toolbox] vbox_ls [<general options>] [option]...\n"
+ " [<file>...]\n\n"
+ "List information about files (the current directory by default).\n\n"
+ "Options:\n\n"
+ " [--dereference|-L]\n"
+ " [-l][-R]\n"
+ " [--verbose|-v]\n"
+ " [<file>...]\n"
+ "\n";
+
+
+/**
+ * Main function for tool "vbox_ls".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE vgsvcToolboxLs(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
+ { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
+ { NULL, 'l', RTGETOPT_REQ_NOTHING },
+ { NULL, 'R', RTGETOPT_REQ_NOTHING },
+ { "--verbose", VBOXSERVICETOOLBOXOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ bool fVerbose = false;
+ uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
+ uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc) /** @todo r=bird: WTF is this doing here? rc isn't set in the loop!! And there is an AssertRCReturn after the previous place it was set. */)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'h':
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf("%s", g_paszLsHelp);
+ return RTEXITCODE_SUCCESS;
+
+ case 'L': /* Dereference symlinks. */
+ fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
+ break;
+
+ case 'l': /* Print long format. */
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
+ break;
+
+ case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
+ break;
+
+ case 'R': /* Recursive processing. */
+ fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
+ break;
+
+ case VBOXSERVICETOOLBOXOPT_VERBOSE:
+ fVerbose = true;
+ break;
+
+ case 'V':
+ vgsvcToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ Assert(GetState.iNext);
+ GetState.iNext--;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+
+ /* All flags / options processed? Bail out here.
+ * Processing the file / directory list comes down below. */
+ if (ch == VINF_GETOPT_NOT_OPTION)
+ break;
+ }
+
+ if (RT_SUCCESS(rc)) /** @todo r=bird: WTF?!? The state handling here is certifiably insane. Crap like this drives me CRAZY!! */
+ {
+ /* Print magic/version. */
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ rc = vgsvcToolboxStrmInit();
+ if (RT_FAILURE(rc))
+ RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
+ vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
+ }
+
+ VGSVCTOOLBOXIDCACHE IdCache;
+ RT_ZERO(IdCache);
+
+ ch = RTGetOpt(&GetState, &ValueUnion);
+ do
+ {
+ char *pszEntry = NULL; /** @todo r=bird: Bad name choice. pszEntry sounds like RTDIRENTRY::szName, i.e. no path. */
+
+ if (ch == 0) /* Use current directory if no element specified. */
+ {
+ char szDirCur[RTPATH_MAX + 1]; /** @todo r=bird: Just put this outside the if(ch==0) and make pszEntry point to it. There is no need to duplicate any strings here! */
+ rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
+ if (RT_FAILURE(rc))
+ RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
+
+ pszEntry = RTStrDup(szDirCur);
+ if (!pszEntry)
+ RTMsgError("Allocating current directory failed\n");
+ }
+ else
+ {
+ pszEntry = RTStrDup(ValueUnion.psz);
+ if (!pszEntry)
+ RTMsgError("Allocating directory '%s' failed\n", ValueUnion.psz);
+ }
+
+ /** @todo r=bird: RTFileExists == RTPathQueryInfo, so just do
+ * RTPathQueryInfoEx here! Also, you _need_ to figure out whether or
+ * not to follow "commandline" links! */
+ if (RTFileExists(pszEntry))
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfoEx(pszEntry, &objInfo,
+ RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /** @todo Follow link? */);
+ if (RT_FAILURE(rc2))
+ {
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ RTMsgError("Cannot access '%s': No such file or directory\n", pszEntry);
+ rc = VERR_FILE_NOT_FOUND;
+ /* Do not break here -- process every element in the list
+ * and keep failing rc. */
+ }
+ else
+ {
+ rc2 = vgsvcToolboxPrintFsInfo(pszEntry, strlen(pszEntry), fOutputFlags, NULL, &IdCache, &objInfo);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ int rc2 = vgsvcToolboxLsHandleDir(pszEntry, fFlags, fOutputFlags, &IdCache);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+
+ RTStrFree(pszEntry);
+ } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
+
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ vgsvcToolboxPrintStrmTermination();
+ }
+ else if (fVerbose)
+ RTMsgError("Failed with rc=%Rrc\n", rc);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/* Try using RTPathRmCmd. */
+static RTEXITCODE vgsvcToolboxRm(int argc, char **argv)
+{
+ return RTPathRmCmd(argc, argv);
+}
+
+
+static char g_paszMkTempHelp[] =
+ " VBoxService [--use-toolbox] vbox_mktemp [<general options>] [<options>]\n"
+ " <template>\n\n"
+ "Create a temporary directory based on the template supplied. The first string\n"
+ "of consecutive 'X' characters in the template will be replaced to form a unique\n"
+ "name for the directory. The template may not contain a path. The default\n"
+ "creation mode is 0600 for files and 0700 for directories. If no path is\n"
+ "specified the default temporary directory will be used.\n"
+ "Options:\n\n"
+ " [--directory|-d] Create a directory instead of a file.\n"
+ " [--mode|-m <mode>] Create the object with mode <mode>.\n"
+ " [--secure|-s] Fail if the object cannot be created securely.\n"
+ " [--tmpdir|-t <path>] Create the object with the absolute path <path>.\n"
+ "\n";
+
+
+/**
+ * Report the result of a vbox_mktemp operation.
+ *
+ * Either errors to stderr (not machine-readable) or everything to stdout as
+ * {name}\0{rc}\0 (machine- readable format). The message may optionally
+ * contain a '%s' for the file name and an %Rrc for the result code in that
+ * order. In future a "verbose" flag may be added, without which nothing will
+ * be output in non-machine- readable mode. Sets prc if rc is a non-success
+ * code.
+ */
+static void toolboxMkTempReport(const char *pcszMessage, const char *pcszFile,
+ bool fActive, int rc, uint32_t fOutputFlags, int *prc)
+{
+ if (!fActive)
+ return;
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ if (RT_SUCCESS(rc))
+ RTPrintf(pcszMessage, pcszFile, rc);
+ else
+ RTMsgError(pcszMessage, pcszFile, rc);
+ else
+ RTPrintf("name=%s%crc=%d%c", pcszFile, 0, rc, 0);
+ if (prc && RT_FAILURE(rc))
+ *prc = rc;
+}
+
+
+/**
+ * Main function for tool "vbox_mktemp".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE,
+ RTGETOPT_REQ_NOTHING },
+ { "--directory", 'd', RTGETOPT_REQ_NOTHING },
+ { "--mode", 'm', RTGETOPT_REQ_STRING },
+ { "--secure", 's', RTGETOPT_REQ_NOTHING },
+ { "--tmpdir", 't', RTGETOPT_REQ_STRING },
+ };
+
+ enum
+ {
+ /* Isn't that a bit long? s/VBOXSERVICETOOLBOX/VSTB/ ? */
+ /** Create a temporary directory instead of a temporary file. */
+ VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY = RT_BIT_32(0),
+ /** Only create the temporary object if the operation is expected
+ * to be secure. Not guaranteed to be supported on a particular
+ * set-up. */
+ VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE = RT_BIT_32(1)
+ };
+
+ int ch, rc;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ uint32_t fFlags = 0;
+ uint32_t fOutputFlags = 0;
+ int cNonOptions = 0;
+ RTFMODE fMode = 0700;
+ bool fModeSet = false;
+ const char *pcszPath = NULL;
+ const char *pcszTemplate;
+ char szTemplateWithPath[RTPATH_MAX] = "";
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'h':
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf("%s", g_paszMkTempHelp);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ vgsvcToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
+ break;
+
+ case 'd':
+ fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY;
+ break;
+
+ case 'm':
+ rc = vgsvcToolboxParseMode(ValueUnion.psz, &fMode);
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_SYNTAX;
+ fModeSet = true;
+#ifndef RT_OS_WINDOWS
+ umask(0); /* RTDirCreate workaround */
+#endif
+ break;
+ case 's':
+ fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE;
+ break;
+
+ case 't':
+ pcszPath = ValueUnion.psz;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* RTGetOpt will sort these to the end of the argv vector so
+ * that we will deal with them afterwards. */
+ ++cNonOptions;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /* Print magic/version. */
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ rc = vgsvcToolboxStrmInit();
+ if (RT_FAILURE(rc))
+ RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
+ vgsvcToolboxPrintStrmHeader("vbt_mktemp", 1 /* Stream version */);
+ }
+
+ if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE && fModeSet)
+ {
+ toolboxMkTempReport("'-s' and '-m' parameters cannot be used together.\n", "",
+ true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_SYNTAX;
+ }
+
+ /* We need exactly one template, containing at least one 'X'. */
+ if (cNonOptions != 1)
+ {
+ toolboxMkTempReport("Please specify exactly one template.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_SYNTAX;
+ }
+ pcszTemplate = argv[argc - 1];
+
+ /* Validate that the template is as IPRT requires (asserted by IPRT). */
+ if ( RTPathHasPath(pcszTemplate)
+ || ( !strstr(pcszTemplate, "XXX")
+ && pcszTemplate[strlen(pcszTemplate) - 1] != 'X'))
+ {
+ toolboxMkTempReport("Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'.\n",
+ pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_FAILURE;
+ }
+ if (pcszPath && !RTPathStartsWithRoot(pcszPath))
+ {
+ toolboxMkTempReport("Path '%s' should be absolute.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_FAILURE;
+ }
+ if (pcszPath)
+ {
+ rc = RTStrCopy(szTemplateWithPath, sizeof(szTemplateWithPath), pcszPath);
+ if (RT_FAILURE(rc))
+ {
+ toolboxMkTempReport("Path '%s' too long.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ {
+ rc = RTPathTemp(szTemplateWithPath, sizeof(szTemplateWithPath));
+ if (RT_FAILURE(rc))
+ {
+ toolboxMkTempReport("Failed to get the temporary directory.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ rc = RTPathAppend(szTemplateWithPath, sizeof(szTemplateWithPath), pcszTemplate);
+ if (RT_FAILURE(rc))
+ {
+ toolboxMkTempReport("Template '%s' too long for path.\n", pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
+ return RTEXITCODE_FAILURE;
+ }
+
+ if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY)
+ {
+ rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
+ ? RTDirCreateTempSecure(szTemplateWithPath)
+ : RTDirCreateTemp(szTemplateWithPath, fMode);
+ toolboxMkTempReport("Created temporary directory '%s'.\n",
+ szTemplateWithPath, RT_SUCCESS(rc), rc,
+ fOutputFlags, NULL);
+ /* RTDirCreateTemp[Secure] sets the template to "" on failure. */
+ toolboxMkTempReport("The following error occurred while creating a temporary directory from template '%s': %Rrc.\n",
+ pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
+ }
+ else
+ {
+ rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
+ ? RTFileCreateTempSecure(szTemplateWithPath)
+ : RTFileCreateTemp(szTemplateWithPath, fMode);
+ toolboxMkTempReport("Created temporary file '%s'.\n",
+ szTemplateWithPath, RT_SUCCESS(rc), rc,
+ fOutputFlags, NULL);
+ /* RTFileCreateTemp[Secure] sets the template to "" on failure. */
+ toolboxMkTempReport("The following error occurred while creating a temporary file from template '%s': %Rrc.\n",
+ pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
+ }
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ vgsvcToolboxPrintStrmTermination();
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/** @todo Document options! */
+static char g_paszMkDirHelp[] =
+ " VBoxService [--use-toolbox] vbox_mkdir [<general options>] [<options>]\n"
+ " <directory>...\n\n"
+ "Options:\n\n"
+ " [--mode|-m <mode>] The file mode to set (chmod) on the created\n"
+ " directories. Default: a=rwx & umask.\n"
+ " [--parents|-p] Create parent directories as needed, no\n"
+ " error if the directory already exists.\n"
+ " [--verbose|-v] Display a message for each created directory.\n"
+ "\n";
+
+
+/**
+ * Main function for tool "vbox_mkdir".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--mode", 'm', RTGETOPT_REQ_STRING },
+ { "--parents", 'p', RTGETOPT_REQ_NOTHING},
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ bool fMakeParentDirs = false;
+ bool fVerbose = false;
+ RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
+ int cDirsCreated = 0;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'p':
+ fMakeParentDirs = true;
+ break;
+
+ case 'm':
+ rc = vgsvcToolboxParseMode(ValueUnion.psz, &fDirMode);
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_SYNTAX;
+#ifndef RT_OS_WINDOWS
+ umask(0); /* RTDirCreate workaround */
+#endif
+ break;
+
+ case 'v':
+ fVerbose = true;
+ break;
+
+ case 'h':
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf("%s", g_paszMkDirHelp);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ vgsvcToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (fMakeParentDirs)
+ /** @todo r=bird: If fVerbose is set, we should also show
+ * which directories that get created, parents as well as
+ * omitting existing final dirs. Annoying, but check any
+ * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
+ * twice). */
+ rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
+ else
+ rc = RTDirCreate(ValueUnion.psz, fDirMode, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
+ ValueUnion.psz, rc);
+ if (fVerbose)
+ RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
+ cDirsCreated++;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ AssertRC(rc);
+
+ if (cDirsCreated == 0)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/** @todo Document options! */
+static char g_paszStatHelp[] =
+ " VBoxService [--use-toolbox] vbox_stat [<general options>] [<options>]\n"
+ " <file>...\n\n"
+ "Display file or file system status.\n\n"
+ "Options:\n\n"
+ " [--file-system|-f]\n"
+ " [--dereference|-L]\n"
+ " [--terse|-t]\n"
+ " [--verbose|-v]\n"
+ "\n";
+
+
+/**
+ * Main function for tool "vbox_stat".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE vgsvcToolboxStat(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
+ { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
+ { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
+ { "--terse", 't', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ int rc = VINF_SUCCESS;
+ uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
+ uint32_t fQueryInfoFlags = RTPATH_F_ON_LINK;
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'f':
+ RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
+ rc = VERR_INVALID_PARAMETER;
+ break;
+
+ case 'L':
+ fQueryInfoFlags &= ~RTPATH_F_ON_LINK;
+ fQueryInfoFlags |= RTPATH_F_FOLLOW_LINK;
+ break;
+
+ case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
+ break;
+
+ case 'h':
+ vgsvcToolboxShowUsageHeader();
+ RTPrintf("%s", g_paszStatHelp);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ vgsvcToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ Assert(GetState.iNext);
+ GetState.iNext--;
+ break;
+ }
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+
+ /* All flags / options processed? Bail out here.
+ * Processing the file / directory list comes down below. */
+ if (ch == VINF_GETOPT_NOT_OPTION)
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ {
+ rc = vgsvcToolboxStrmInit();
+ if (RT_FAILURE(rc))
+ RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
+ vgsvcToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
+ }
+
+ VGSVCTOOLBOXIDCACHE IdCache;
+ RT_ZERO(IdCache);
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfoEx(ValueUnion.psz, &objInfo, RTFSOBJATTRADD_UNIX, fQueryInfoFlags);
+ if (RT_FAILURE(rc2))
+ {
+ if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
+ RTMsgError("Cannot stat for '%s': %Rrc\n", ValueUnion.psz, rc2);
+ }
+ else
+ rc2 = vgsvcToolboxPrintFsInfo(ValueUnion.psz, strlen(ValueUnion.psz), fOutputFlags, NULL, &IdCache, &objInfo);
+ /** @todo r=bird: You're checking rc not rc2 here... */
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ /* Do not break here -- process every element in the list
+ * and keep (initial) failing rc. */
+ }
+
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ vgsvcToolboxPrintStrmTermination();
+
+ /* At this point the overall result (success/failure) should be in rc. */
+ }
+ else
+ RTMsgError("Failed with rc=%Rrc\n", rc);
+
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_ACCESS_DENIED:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED;
+
+ case VERR_FILE_NOT_FOUND:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND;
+
+ case VERR_PATH_NOT_FOUND:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND;
+
+ case VERR_NET_PATH_NOT_FOUND:
+ return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND;
+
+ default:
+#ifdef DEBUG_andy
+ AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
+#endif
+ break;
+ }
+
+ return RTEXITCODE_FAILURE;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Looks up the tool definition entry for the tool give by @a pszTool.
+ *
+ * @returns Pointer to the tool definition. NULL if not found.
+ * @param pszTool The name of the tool.
+ */
+static PCVBOXSERVICETOOLBOXTOOL vgsvcToolboxLookUp(const char *pszTool)
+{
+ AssertPtrReturn(pszTool, NULL);
+
+ /* Do a linear search, since we don't have that much stuff in the table. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aTools); i++)
+ if (!strcmp(g_aTools[i].pszName, pszTool))
+ return &g_aTools[i];
+
+ return NULL;
+}
+
+
+/**
+ * Converts a tool's exit code back to an IPRT error code.
+ *
+ * @return Converted IPRT status code.
+ * @param pszTool Name of the toolbox tool to convert exit code for.
+ * @param rcExit The tool's exit code to convert.
+ */
+int VGSvcToolboxExitCodeConvertToRc(const char *pszTool, RTEXITCODE rcExit)
+{
+ AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
+
+ PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
+ if (pTool)
+ return pTool->pfnExitCodeConvertToRc(rcExit);
+
+ AssertMsgFailed(("Tool '%s' not found\n", pszTool));
+ return VERR_GENERAL_FAILURE; /* Lookup failed, should not happen. */
+}
+
+
+/**
+ * Entry point for internal toolbox.
+ *
+ * @return True if an internal tool was handled, false if not.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ * @param prcExit Where to store the exit code when an
+ * internal toolbox command was handled.
+ */
+bool VGSvcToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
+{
+
+ /*
+ * Check if the file named in argv[0] is one of the toolbox programs.
+ */
+ AssertReturn(argc > 0, false);
+ const char *pszTool = RTPathFilename(argv[0]);
+ PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
+ if (!pTool)
+ {
+ /*
+ * For debugging and testing purposes we also allow toolbox program access
+ * when the first VBoxService argument is --use-toolbox.
+ */
+ if (argc < 2 || strcmp(argv[1], "--use-toolbox"))
+ return false;
+
+ /* No tool specified? Show toolbox help. */
+ if (argc < 3)
+ {
+ vgsvcToolboxShowUsage();
+ *prcExit = RTEXITCODE_SYNTAX;
+ return true;
+ }
+
+ argc -= 2;
+ argv += 2;
+ pszTool = argv[0];
+ pTool = vgsvcToolboxLookUp(pszTool);
+ if (!pTool)
+ {
+ *prcExit = RTEXITCODE_SUCCESS;
+ if (!strcmp(pszTool, "-V"))
+ {
+ vgsvcToolboxShowVersion();
+ return true;
+ }
+ if ( strcmp(pszTool, "help")
+ && strcmp(pszTool, "--help")
+ && strcmp(pszTool, "-h"))
+ *prcExit = RTEXITCODE_SYNTAX;
+ vgsvcToolboxShowUsage();
+ return true;
+ }
+ }
+
+ /*
+ * Invoke the handler.
+ */
+ RTMsgSetProgName("VBoxService/%s", pszTool);
+ AssertPtr(pTool);
+ *prcExit = pTool->pfnHandler(argc, argv);
+
+ return true;
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.h b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.h
new file mode 100644
index 00000000..f6c87aa7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.h
@@ -0,0 +1,32 @@
+/* $Id: VBoxServiceToolBox.h $ */
+/** @file
+ * VBoxService - Toolbox header for sharing defines between toolbox binary and VBoxService.
+ */
+
+/*
+ * Copyright (C) 2016-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceToolBox_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceToolBox_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/GuestHost/GuestControl.h>
+
+RT_C_DECLS_BEGIN
+extern bool VGSvcToolboxMain(int argc, char **argv, RTEXITCODE *prcExit);
+extern int VGSvcToolboxExitCodeConvertToRc(const char *pszTool, RTEXITCODE rcExit);
+RT_C_DECLS_END
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceToolBox_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp
new file mode 100644
index 00000000..81cb41ca
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp
@@ -0,0 +1,401 @@
+/* $Id: VBoxServiceUtils.cpp $ */
+/** @file
+ * VBoxServiceUtils - Some utility functions.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <iprt/param.h>
+# include <iprt/path.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+
+
+#ifdef VBOX_WITH_GUEST_PROPS
+
+/**
+ * Reads a guest property.
+ *
+ * @returns VBox status code, fully bitched.
+ *
+ * @param u32ClientId 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. Free it using RTStrFree(). Optional.
+ * @param ppszFlags Where to return the value flags. Free it
+ * using RTStrFree(). Optional.
+ * @param puTimestamp Where to return the timestamp. This is only set
+ * on success. Optional.
+ */
+int VGSvcReadProp(uint32_t u32ClientId, 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)
+ {
+ VGSvcError("Guest Property: Failed to allocate %zu bytes\n", cbBuf);
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ char *pszValue;
+ char *pszFlags;
+ uint64_t uTimestamp;
+ rc = VbglR3GuestPropRead(u32ClientId, 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;
+ }
+ if (rc == VERR_NOT_FOUND)
+ VGSvcVerbose(2, "Guest Property: %s not found\n", pszPropName);
+ else
+ VGSvcError("Guest Property: Failed to query '%s': %Rrc\n", pszPropName, rc);
+ break;
+ }
+
+ VGSvcVerbose(2, "Guest Property: Read '%s' = '%s', timestamp %RU64n\n", pszPropName, pszValue, uTimestamp);
+ if (ppszValue)
+ {
+ *ppszValue = RTStrDup(pszValue);
+ if (!*ppszValue)
+ {
+ VGSvcError("Guest Property: RTStrDup failed for '%s'\n", pszValue);
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+
+ if (puTimestamp)
+ *puTimestamp = uTimestamp;
+ if (ppszFlags)
+ *ppszFlags = RTStrDup(pszFlags);
+ break; /* done */
+ }
+
+ if (pvBuf)
+ RTMemFree(pvBuf);
+ return rc;
+}
+
+
+/**
+ * Reads a guest property as a 32-bit value.
+ *
+ * @returns VBox status code, fully bitched.
+ *
+ * @param u32ClientId The HGCM client ID for the guest property session.
+ * @param pszPropName The property name.
+ * @param pu32 Where to store the 32-bit value.
+ *
+ */
+int VGSvcReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
+{
+ char *pszValue;
+ int rc = VGSvcReadProp(u32ClientId, pszPropName, &pszValue, NULL /* ppszFlags */, NULL /* puTimestamp */);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszNext;
+ rc = RTStrToUInt32Ex(pszValue, &pszNext, 0, pu32);
+ if ( RT_SUCCESS(rc)
+ && (*pu32 < u32Min || *pu32 > u32Max))
+ rc = VGSvcError("The guest property value %s = %RU32 is out of range [%RU32..%RU32].\n",
+ pszPropName, *pu32, u32Min, u32Max);
+ RTStrFree(pszValue);
+ }
+ return rc;
+}
+
+/**
+ * Checks if @a pszPropName exists.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if it exists.
+ * @retval VERR_NOT_FOUND if not found.
+ *
+ * @param u32ClientId The HGCM client ID for the guest property session.
+ * @param pszPropName The property name.
+ */
+int VGSvcCheckPropExist(uint32_t u32ClientId, const char *pszPropName)
+{
+ return VGSvcReadProp(u32ClientId, pszPropName, NULL /*ppszValue*/, NULL /* ppszFlags */, NULL /* puTimestamp */);
+}
+
+
+/**
+ * Reads a guest property from the host side.
+ *
+ * @returns IPRT status code, fully bitched.
+ * @param u32ClientId The HGCM client ID for the guest property session.
+ * @param pszPropName The property name.
+ * @param fReadOnly Whether or not this property needs to be read only
+ * by the guest side. Otherwise VERR_ACCESS_DENIED will
+ * be returned.
+ * @param ppszValue Where to return the value. This is always set
+ * to NULL. Free it using RTStrFree().
+ * @param ppszFlags Where to return the value flags. Free it
+ * using RTStrFree(). Optional.
+ * @param puTimestamp Where to return the timestamp. This is only set
+ * on success. Optional.
+ */
+int VGSvcReadHostProp(uint32_t u32ClientId, const char *pszPropName, bool fReadOnly,
+ char **ppszValue, char **ppszFlags, uint64_t *puTimestamp)
+{
+ AssertPtrReturn(ppszValue, VERR_INVALID_PARAMETER);
+
+ char *pszValue = NULL;
+ char *pszFlags = NULL;
+ int rc = VGSvcReadProp(u32ClientId, pszPropName, &pszValue, &pszFlags, puTimestamp);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check security bits. */
+ if ( fReadOnly /* Do we except a guest read-only property */
+ && !RTStrStr(pszFlags, "RDONLYGUEST"))
+ {
+ /* If we want a property which is read-only on the guest
+ * and it is *not* marked as such, deny access! */
+ rc = VERR_ACCESS_DENIED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppszValue = pszValue;
+
+ if (ppszFlags)
+ *ppszFlags = pszFlags;
+ else if (pszFlags)
+ RTStrFree(pszFlags);
+ }
+ else
+ {
+ if (pszValue)
+ RTStrFree(pszValue);
+ if (pszFlags)
+ RTStrFree(pszFlags);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Wrapper around VbglR3GuestPropWriteValue that does value formatting and
+ * logging.
+ *
+ * @returns VBox status code. Errors will be logged.
+ *
+ * @param u32ClientId The HGCM client ID for the guest property session.
+ * @param pszName The property name.
+ * @param pszValueFormat The property format string. If this is NULL then
+ * the property will be deleted (if possible).
+ * @param ... Format arguments.
+ */
+int VGSvcWritePropF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...)
+{
+ AssertPtr(pszName);
+ int rc;
+ if (pszValueFormat != NULL)
+ {
+ va_list va;
+ va_start(va, pszValueFormat);
+ VGSvcVerbose(3, "Writing guest property '%s' = '%N'\n", pszName, pszValueFormat, &va);
+ va_end(va);
+
+ va_start(va, pszValueFormat);
+ rc = VbglR3GuestPropWriteValueV(u32ClientId, pszName, pszValueFormat, va);
+ va_end(va);
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Error writing guest property '%s' (rc=%Rrc)\n", pszName, rc);
+ }
+ else
+ {
+ VGSvcVerbose(3, "Deleting guest property '%s'\n", pszName);
+ rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
+ if (RT_FAILURE(rc))
+ VGSvcError("Error deleting guest property '%s' (rc=%Rrc)\n", pszName, rc);
+ }
+ return rc;
+}
+
+#endif /* VBOX_WITH_GUEST_PROPS */
+#ifdef RT_OS_WINDOWS
+
+/**
+ * Helper for vgsvcUtilGetFileVersion and attempts to read and parse
+ * FileVersion.
+ *
+ * @returns Success indicator.
+ */
+static bool vgsvcUtilGetFileVersionOwn(LPSTR pVerData, PDWORD pdwMajor, PDWORD pdwMinor, PDWORD pdwBuildNumber,
+ PDWORD pdwRevisionNumber)
+{
+ UINT cchStrValue = 0;
+ LPTSTR pStrValue = NULL;
+ if (!VerQueryValueA(pVerData, "\\StringFileInfo\\040904b0\\FileVersion", (LPVOID *)&pStrValue, &cchStrValue))
+ return false;
+
+ /** @todo r=bird: get rid of this. Avoid sscanf like the plague! */
+ if (sscanf(pStrValue, "%ld.%ld.%ld.%ld", pdwMajor, pdwMinor, pdwBuildNumber, pdwRevisionNumber) != 4)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Worker for VGSvcUtilWinGetFileVersionString.
+ *
+ * @returns VBox status code.
+ * @param pszFilename ASCII & ANSI & UTF-8 compliant name.
+ * @param pdwMajor Where to return the major version number.
+ * @param pdwMinor Where to return the minor version number.
+ * @param pdwBuildNumber Where to return the build number.
+ * @param pdwRevisionNumber Where to return the revision number.
+ */
+static int vgsvcUtilGetFileVersion(const char *pszFilename, PDWORD pdwMajor, PDWORD pdwMinor, PDWORD pdwBuildNumber,
+ PDWORD pdwRevisionNumber)
+{
+ int rc;
+
+ *pdwMajor = *pdwMinor = *pdwBuildNumber = *pdwRevisionNumber = 0;
+
+ /*
+ * Get the file version info.
+ */
+ DWORD dwHandleIgnored;
+ DWORD cbVerData = GetFileVersionInfoSizeA(pszFilename, &dwHandleIgnored);
+ if (cbVerData)
+ {
+ LPTSTR pVerData = (LPTSTR)RTMemTmpAllocZ(cbVerData);
+ if (pVerData)
+ {
+ if (GetFileVersionInfoA(pszFilename, dwHandleIgnored, cbVerData, pVerData))
+ {
+ /*
+ * Try query and parse the FileVersion string our selves first
+ * since this will give us the correct revision number when
+ * it goes beyond the range of an uint16_t / WORD.
+ */
+ if (vgsvcUtilGetFileVersionOwn(pVerData, pdwMajor, pdwMinor, pdwBuildNumber, pdwRevisionNumber))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /* Fall back on VS_FIXEDFILEINFO */
+ UINT cbFileInfoIgnored = 0;
+ VS_FIXEDFILEINFO *pFileInfo = NULL;
+ if (VerQueryValue(pVerData, "\\", (LPVOID *)&pFileInfo, &cbFileInfoIgnored))
+ {
+ *pdwMajor = HIWORD(pFileInfo->dwFileVersionMS);
+ *pdwMinor = LOWORD(pFileInfo->dwFileVersionMS);
+ *pdwBuildNumber = HIWORD(pFileInfo->dwFileVersionLS);
+ *pdwRevisionNumber = LOWORD(pFileInfo->dwFileVersionLS);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ VGSvcVerbose(3, "No file version value for file '%s' available! (%d / rc=%Rrc)\n",
+ pszFilename, GetLastError(), rc);
+ }
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ VGSvcVerbose(0, "GetFileVersionInfo(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc);
+ }
+
+ RTMemTmpFree(pVerData);
+ }
+ else
+ {
+ VGSvcVerbose(0, "Failed to allocate %u byte for file version info for '%s'\n", cbVerData, pszFilename);
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ VGSvcVerbose(3, "GetFileVersionInfoSize(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets a re-formatted version string from the VS_FIXEDFILEINFO table.
+ *
+ * @returns VBox status code. The output buffer is always valid and the status
+ * code can safely be ignored.
+ *
+ * @param pszPath The base path.
+ * @param pszFilename The filename.
+ * @param pszVersion Where to return the version string.
+ * @param cbVersion The size of the version string buffer. This MUST be
+ * at least 2 bytes!
+ */
+int VGSvcUtilWinGetFileVersionString(const char *pszPath, const char *pszFilename, char *pszVersion, size_t cbVersion)
+{
+ /*
+ * We will ALWAYS return with a valid output buffer.
+ */
+ AssertReturn(cbVersion >= 2, VERR_BUFFER_OVERFLOW);
+ pszVersion[0] = '-';
+ pszVersion[1] = '\0';
+
+ /*
+ * Create the path and query the bits.
+ */
+ char szFullPath[RTPATH_MAX];
+ int rc = RTPathJoin(szFullPath, sizeof(szFullPath), pszPath, pszFilename);
+ if (RT_SUCCESS(rc))
+ {
+ DWORD dwMajor, dwMinor, dwBuild, dwRev;
+ rc = vgsvcUtilGetFileVersion(szFullPath, &dwMajor, &dwMinor, &dwBuild, &dwRev);
+ if (RT_SUCCESS(rc))
+ RTStrPrintf(pszVersion, cbVersion, "%u.%u.%ur%u", dwMajor, dwMinor, dwBuild, dwRev);
+ }
+ return rc;
+}
+
+#endif /* RT_OS_WINDOWS */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h
new file mode 100644
index 00000000..21038b0b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h
@@ -0,0 +1,40 @@
+/* $Id: VBoxServiceUtils.h $ */
+/** @file
+ * VBoxServiceUtils - Guest Additions Services (Utilities).
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceUtils_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceUtils_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxServiceInternal.h"
+
+#ifdef VBOX_WITH_GUEST_PROPS
+int VGSvcReadProp(uint32_t u32ClientId, const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp);
+int VGSvcReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max);
+int VGSvcCheckPropExist(uint32_t u32ClientId, const char *pszPropName);
+int VGSvcReadHostProp(uint32_t u32ClientId, const char *pszPropName, bool fReadOnly, char **ppszValue, char **ppszFlags,
+ uint64_t *puTimestamp);
+int VGSvcWritePropF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...);
+#endif
+
+#ifdef RT_OS_WINDOWS
+int VGSvcUtilWinGetFileVersionString(const char *pszPath, const char *pszFileName, char *pszVersion, size_t cbVersion);
+#endif
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceUtils_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp
new file mode 100644
index 00000000..f46cb861
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp
@@ -0,0 +1,1388 @@
+/* $Id: VBoxServiceVMInfo-win.cpp $ */
+/** @file
+ * VBoxService - Virtual Machine Information for the Host, Windows specifics.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
+# undef _WIN32_WINNT
+# define _WIN32_WINNT 0x0600 /* QueryFullProcessImageNameW in recent SDKs. */
+#endif
+#include <iprt/win/windows.h>
+#include <wtsapi32.h> /* For WTS* calls. */
+#include <psapi.h> /* EnumProcesses. */
+#include <Ntsecapi.h> /* Needed for process security information. */
+
+#include <iprt/assert.h>
+#include <iprt/ldr.h>
+#include <iprt/localipc.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/system.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+#include "VBoxServiceVMInfo.h"
+#include "../../WINNT/VBoxTray/VBoxTrayMsg.h" /* For IPC. */
+
+static uint32_t s_uDebugGuestPropClientID = 0;
+static uint32_t s_uDebugIter = 0;
+/** Whether to skip the logged-in user detection over RDP or not.
+ * See notes in this section why we might want to skip this. */
+static bool s_fSkipRDPDetection = false;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Structure for storing the looked up user information. */
+typedef struct VBOXSERVICEVMINFOUSER
+{
+ WCHAR wszUser[_MAX_PATH];
+ WCHAR wszAuthenticationPackage[_MAX_PATH];
+ WCHAR wszLogonDomain[_MAX_PATH];
+ /** Number of assigned user processes. */
+ ULONG ulNumProcs;
+ /** Last (highest) session ID. This
+ * is needed for distinguishing old session
+ * process counts from new (current) session
+ * ones. */
+ ULONG ulLastSession;
+} VBOXSERVICEVMINFOUSER, *PVBOXSERVICEVMINFOUSER;
+
+/** Structure for the file information lookup. */
+typedef struct VBOXSERVICEVMINFOFILE
+{
+ char *pszFilePath;
+ char *pszFileName;
+} VBOXSERVICEVMINFOFILE, *PVBOXSERVICEVMINFOFILE;
+
+/** Structure for process information lookup. */
+typedef struct VBOXSERVICEVMINFOPROC
+{
+ /** The PID. */
+ DWORD id;
+ /** The SID. */
+ PSID pSid;
+ /** The LUID. */
+ LUID luid;
+ /** Interactive process. */
+ bool fInteractive;
+} VBOXSERVICEVMINFOPROC, *PVBOXSERVICEVMINFOPROC;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static uint32_t vgsvcVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs);
+static bool vgsvcVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession);
+static int vgsvcVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppProc, DWORD *pdwCount);
+static void vgsvcVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs);
+static int vgsvcVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTONCE g_vgsvcWinVmInitOnce = RTONCE_INITIALIZER;
+
+/** @name Secur32.dll imports are dynamically resolved because of NT4.
+ * @{ */
+static decltype(LsaGetLogonSessionData) *g_pfnLsaGetLogonSessionData = NULL;
+static decltype(LsaEnumerateLogonSessions) *g_pfnLsaEnumerateLogonSessions = NULL;
+static decltype(LsaFreeReturnBuffer) *g_pfnLsaFreeReturnBuffer = NULL;
+/** @} */
+
+/** @name WtsApi32.dll imports are dynamically resolved because of NT4.
+ * @{ */
+static decltype(WTSFreeMemory) *g_pfnWTSFreeMemory = NULL;
+static decltype(WTSQuerySessionInformationA) *g_pfnWTSQuerySessionInformationA = NULL;
+/** @} */
+
+/** @name PsApi.dll imports are dynamically resolved because of NT4.
+ * @{ */
+static decltype(EnumProcesses) *g_pfnEnumProcesses = NULL;
+static decltype(GetModuleFileNameExW) *g_pfnGetModuleFileNameExW = NULL;
+/** @} */
+
+/** @name New Kernel32.dll APIs we may use when present.
+ * @{ */
+static decltype(QueryFullProcessImageNameW) *g_pfnQueryFullProcessImageNameW = NULL;
+
+/** @} */
+
+/** Windows version. */
+static OSVERSIONINFOEXA g_WinVersion;
+
+
+/**
+ * An RTOnce callback function.
+ */
+static DECLCALLBACK(int) vgsvcWinVmInfoInitOnce(void *pvIgnored)
+{
+ RT_NOREF1(pvIgnored);
+
+ /* SECUR32 */
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrLoadSystem("secur32.dll", true, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLdrMod, "LsaGetLogonSessionData", (void **)&g_pfnLsaGetLogonSessionData);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hLdrMod, "LsaEnumerateLogonSessions", (void **)&g_pfnLsaEnumerateLogonSessions);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hLdrMod, "LsaFreeReturnBuffer", (void **)&g_pfnLsaFreeReturnBuffer);
+ AssertRC(rc);
+ RTLdrClose(hLdrMod);
+ }
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(1, "Secur32.dll APIs are not available (%Rrc)\n", rc);
+ g_pfnLsaGetLogonSessionData = NULL;
+ g_pfnLsaEnumerateLogonSessions = NULL;
+ g_pfnLsaFreeReturnBuffer = NULL;
+ Assert(g_WinVersion.dwMajorVersion < 5);
+ }
+
+ /* WTSAPI32 */
+ rc = RTLdrLoadSystem("wtsapi32.dll", true, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLdrMod, "WTSFreeMemory", (void **)&g_pfnWTSFreeMemory);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hLdrMod, "WTSQuerySessionInformationA", (void **)&g_pfnWTSQuerySessionInformationA);
+ AssertRC(rc);
+ RTLdrClose(hLdrMod);
+ }
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(1, "WtsApi32.dll APIs are not available (%Rrc)\n", rc);
+ g_pfnWTSFreeMemory = NULL;
+ g_pfnWTSQuerySessionInformationA = NULL;
+ Assert(g_WinVersion.dwMajorVersion < 5);
+ }
+
+ /* PSAPI */
+ rc = RTLdrLoadSystem("psapi.dll", true, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLdrMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hLdrMod, "GetModuleFileNameExW", (void **)&g_pfnGetModuleFileNameExW);
+ AssertRC(rc);
+ RTLdrClose(hLdrMod);
+ }
+ if (RT_FAILURE(rc))
+ {
+ VGSvcVerbose(1, "psapi.dll APIs are not available (%Rrc)\n", rc);
+ g_pfnEnumProcesses = NULL;
+ g_pfnGetModuleFileNameExW = NULL;
+ Assert(g_WinVersion.dwMajorVersion < 5);
+ }
+
+ /* Kernel32: */
+ rc = RTLdrLoadSystem("kernel32.dll", true, &hLdrMod);
+ AssertRCReturn(rc, rc);
+ rc = RTLdrGetSymbol(hLdrMod, "QueryFullProcessImageNameW", (void **)&g_pfnQueryFullProcessImageNameW);
+ if (RT_FAILURE(rc))
+ {
+ Assert(g_WinVersion.dwMajorVersion < 6);
+ g_pfnQueryFullProcessImageNameW = NULL;
+ }
+ RTLdrClose(hLdrMod);
+
+ /*
+ * Get the extended windows version once and for all.
+ */
+ g_WinVersion.dwOSVersionInfoSize = sizeof(g_WinVersion);
+ if (!GetVersionExA((OSVERSIONINFO *)&g_WinVersion))
+ {
+ RT_ZERO(g_WinVersion);
+ g_WinVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionExA((OSVERSIONINFO *)&g_WinVersion))
+ {
+ AssertFailed();
+ RT_ZERO(g_WinVersion);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static bool vgsvcVMInfoSession0Separation(void)
+{
+ return g_WinVersion.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && g_WinVersion.dwMajorVersion >= 6; /* Vista = 6.0 */
+}
+
+
+/**
+ * Retrieves the module name of a given process.
+ *
+ * @return IPRT status code.
+ */
+static int vgsvcVMInfoWinProcessesGetModuleNameA(PVBOXSERVICEVMINFOPROC const pProc, PRTUTF16 *ppszName)
+{
+ AssertPtrReturn(pProc, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppszName, VERR_INVALID_POINTER);
+
+ /** @todo Only do this once. Later. */
+ /* Platform other than NT (e.g. Win9x) not supported. */
+ if (g_WinVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ return VERR_NOT_SUPPORTED;
+
+ int rc = VINF_SUCCESS;
+
+ DWORD dwFlags = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+ if (g_WinVersion.dwMajorVersion >= 6 /* Vista or later */)
+ dwFlags = PROCESS_QUERY_LIMITED_INFORMATION; /* possible to do on more processes */
+
+ HANDLE h = OpenProcess(dwFlags, FALSE, pProc->id);
+ if (h == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ if (g_cVerbosity)
+ VGSvcError("Unable to open process with PID=%u, error=%u\n", pProc->id, dwErr);
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ else
+ {
+ /* Since GetModuleFileNameEx has trouble with cross-bitness stuff (32-bit apps cannot query 64-bit
+ apps and vice verse) we have to use a different code path for Vista and up. */
+ WCHAR wszName[_1K];
+ DWORD dwLen = sizeof(wszName); /** @todo r=bird: wrong? */
+
+ /* Use QueryFullProcessImageNameW if available (Vista+). */
+ if (g_pfnQueryFullProcessImageNameW)
+ {
+ if (!g_pfnQueryFullProcessImageNameW(h, 0 /*PROCESS_NAME_NATIVE*/, wszName, &dwLen))
+ rc = VERR_ACCESS_DENIED;
+ }
+ else if (!g_pfnGetModuleFileNameExW(h, NULL /* Get main executable */, wszName, dwLen))
+ rc = VERR_ACCESS_DENIED;
+
+ if ( RT_FAILURE(rc)
+ && g_cVerbosity > 3)
+ VGSvcError("Unable to retrieve process name for PID=%u, error=%u\n", pProc->id, GetLastError());
+ else
+ {
+ PRTUTF16 pszName = RTUtf16Dup(wszName);
+ if (pszName)
+ *ppszName = pszName;
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ CloseHandle(h);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Fills in more data for a process.
+ *
+ * @returns VBox status code.
+ * @param pProc The process structure to fill data into.
+ * @param tkClass The kind of token information to get.
+ */
+static int vgsvcVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc, TOKEN_INFORMATION_CLASS tkClass)
+{
+ AssertPtrReturn(pProc, VERR_INVALID_POINTER);
+
+ DWORD dwErr = ERROR_SUCCESS;
+ HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
+ if (h == NULL)
+ {
+ dwErr = GetLastError();
+ if (g_cVerbosity > 4)
+ VGSvcError("Unable to open process with PID=%u, error=%u\n", pProc->id, dwErr);
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ int rc = VINF_SUCCESS;
+ HANDLE hToken;
+ if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
+ {
+ void *pvTokenInfo = NULL;
+ DWORD dwTokenInfoSize;
+ switch (tkClass)
+ {
+ case TokenStatistics:
+ /** @todo r=bird: Someone has been reading too many MSDN examples. You shall
+ * use RTMemAlloc here! There is absolutely not reason for
+ * complicating things uncessarily by using HeapAlloc! */
+ dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
+ pvTokenInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTokenInfoSize);
+ AssertPtr(pvTokenInfo);
+ break;
+
+ case TokenGroups:
+ dwTokenInfoSize = 0;
+ /* Allocation will follow in a second step. */
+ break;
+
+ case TokenUser:
+ dwTokenInfoSize = 0;
+ /* Allocation will follow in a second step. */
+ break;
+
+ default:
+ VGSvcError("Token class not implemented: %d\n", tkClass);
+ rc = VERR_NOT_IMPLEMENTED;
+ dwTokenInfoSize = 0; /* Shut up MSC. */
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ DWORD dwRetLength;
+ if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
+ {
+ dwErr = GetLastError();
+ if (dwErr == ERROR_INSUFFICIENT_BUFFER)
+ {
+ dwErr = ERROR_SUCCESS;
+
+ switch (tkClass)
+ {
+ case TokenGroups:
+ pvTokenInfo = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRetLength);
+ if (!pvTokenInfo)
+ dwErr = GetLastError();
+ dwTokenInfoSize = dwRetLength;
+ break;
+
+ case TokenUser:
+ pvTokenInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRetLength);
+ if (!pvTokenInfo)
+ dwErr = GetLastError();
+ dwTokenInfoSize = dwRetLength;
+ break;
+
+ default:
+ AssertMsgFailed(("Re-allocating of token information for token class not implemented\n"));
+ break;
+ }
+
+ if (dwErr == ERROR_SUCCESS)
+ {
+ if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
+ dwErr = GetLastError();
+ }
+ }
+ }
+
+ if (dwErr == ERROR_SUCCESS)
+ {
+ rc = VINF_SUCCESS;
+
+ switch (tkClass)
+ {
+ case TokenStatistics:
+ {
+ PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)pvTokenInfo;
+ AssertPtr(pStats);
+ memcpy(&pProc->luid, &pStats->AuthenticationId, sizeof(LUID));
+ /** @todo Add more information of TOKEN_STATISTICS as needed. */
+ break;
+ }
+
+ case TokenGroups:
+ {
+ pProc->fInteractive = false;
+
+ SID_IDENTIFIER_AUTHORITY sidAuthNT = SECURITY_NT_AUTHORITY;
+ PSID pSidInteractive = NULL; /* S-1-5-4 */
+ if (!AllocateAndInitializeSid(&sidAuthNT, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pSidInteractive))
+ dwErr = GetLastError();
+
+ PSID pSidLocal = NULL; /* S-1-2-0 */
+ if (dwErr == ERROR_SUCCESS)
+ {
+ SID_IDENTIFIER_AUTHORITY sidAuthLocal = SECURITY_LOCAL_SID_AUTHORITY;
+ if (!AllocateAndInitializeSid(&sidAuthLocal, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pSidLocal))
+ dwErr = GetLastError();
+ }
+
+ if (dwErr == ERROR_SUCCESS)
+ {
+ PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)pvTokenInfo;
+ AssertPtr(pGroups);
+ for (DWORD i = 0; i < pGroups->GroupCount; i++)
+ {
+ if ( EqualSid(pGroups->Groups[i].Sid, pSidInteractive)
+ || EqualSid(pGroups->Groups[i].Sid, pSidLocal)
+ || pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID)
+ {
+ pProc->fInteractive = true;
+ break;
+ }
+ }
+ }
+
+ if (pSidInteractive)
+ FreeSid(pSidInteractive);
+ if (pSidLocal)
+ FreeSid(pSidLocal);
+ break;
+ }
+
+ case TokenUser:
+ {
+ PTOKEN_USER pUser = (PTOKEN_USER)pvTokenInfo;
+ AssertPtr(pUser);
+
+ DWORD dwLength = GetLengthSid(pUser->User.Sid);
+ Assert(dwLength);
+ if (dwLength)
+ {
+ pProc->pSid = (PSID)HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, dwLength);
+ AssertPtr(pProc->pSid);
+ if (CopySid(dwLength, pProc->pSid, pUser->User.Sid))
+ {
+ if (!IsValidSid(pProc->pSid))
+ dwErr = ERROR_INVALID_NAME;
+ }
+ else
+ dwErr = GetLastError();
+ }
+ else
+ dwErr = ERROR_NO_DATA;
+
+ if (dwErr != ERROR_SUCCESS)
+ {
+ VGSvcError("Error retrieving SID of process PID=%u: %u\n", pProc->id, dwErr);
+ if (pProc->pSid)
+ {
+ HeapFree(GetProcessHeap(), 0 /* Flags */, pProc->pSid);
+ pProc->pSid = NULL;
+ }
+ }
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("Unhandled token information class\n"));
+ break;
+ }
+ }
+
+ if (pvTokenInfo)
+ HeapFree(GetProcessHeap(), 0 /* Flags */, pvTokenInfo);
+ }
+ CloseHandle(hToken);
+ }
+ else
+ dwErr = GetLastError();
+
+ if (dwErr != ERROR_SUCCESS)
+ {
+ if (g_cVerbosity)
+ VGSvcError("Unable to query token information for PID=%u, error=%u\n", pProc->id, dwErr);
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ CloseHandle(h);
+ return rc;
+}
+
+
+/**
+ * Enumerate all the processes in the system and get the logon user IDs for
+ * them.
+ *
+ * @returns VBox status code.
+ * @param ppaProcs Where to return the process snapshot. This must be
+ * freed by calling vgsvcVMInfoWinProcessesFree.
+ *
+ * @param pcProcs Where to store the returned process count.
+ */
+static int vgsvcVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
+{
+ AssertPtr(ppaProcs);
+ AssertPtr(pcProcs);
+
+ if (!g_pfnEnumProcesses)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Call EnumProcesses with an increasingly larger buffer until it all fits
+ * or we think something is screwed up.
+ */
+ DWORD cProcesses = 64;
+ PDWORD paPID = NULL;
+ int rc = VINF_SUCCESS;
+ do
+ {
+ /* Allocate / grow the buffer first. */
+ cProcesses *= 2;
+ void *pvNew = RTMemRealloc(paPID, cProcesses * sizeof(DWORD));
+ if (!pvNew)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ paPID = (PDWORD)pvNew;
+
+ /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
+ DWORD cbRet;
+ if (!g_pfnEnumProcesses(paPID, cProcesses * sizeof(DWORD), &cbRet))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+ if (cbRet < cProcesses * sizeof(DWORD))
+ {
+ cProcesses = cbRet / sizeof(DWORD);
+ break;
+ }
+ } while (cProcesses <= _32K); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate out process structures and fill data into them.
+ * We currently only try lookup their LUID's.
+ */
+ PVBOXSERVICEVMINFOPROC paProcs;
+ paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
+ if (paProcs)
+ {
+ for (DWORD i = 0; i < cProcesses; i++)
+ {
+ paProcs[i].id = paPID[i];
+ paProcs[i].pSid = NULL;
+
+ int rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenUser);
+ if (RT_FAILURE(rc2) && g_cVerbosity)
+ VGSvcError("Get token class 'user' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
+
+ rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenGroups);
+ if (RT_FAILURE(rc2) && g_cVerbosity)
+ VGSvcError("Get token class 'groups' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
+
+ rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
+ if (RT_FAILURE(rc2) && g_cVerbosity)
+ VGSvcError("Get token class 'statistics' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
+ }
+
+ /* Save number of processes */
+ if (RT_SUCCESS(rc))
+ {
+ *pcProcs = cProcesses;
+ *ppaProcs = paProcs;
+ }
+ else
+ vgsvcVMInfoWinProcessesFree(cProcesses, paProcs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTMemFree(paPID);
+ return rc;
+}
+
+/**
+ * Frees the process structures returned by
+ * vgsvcVMInfoWinProcessesEnumerate() before.
+ *
+ * @param cProcs Number of processes in paProcs.
+ * @param paProcs The process array.
+ */
+static void vgsvcVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs)
+{
+ for (DWORD i = 0; i < cProcs; i++)
+ if (paProcs[i].pSid)
+ {
+ HeapFree(GetProcessHeap(), 0 /* Flags */, paProcs[i].pSid);
+ paProcs[i].pSid = NULL;
+ }
+ RTMemFree(paProcs);
+}
+
+/**
+ * Determines whether the specified session has processes on the system.
+ *
+ * @returns Number of processes found for a specified session.
+ * @param pSession The current user's SID.
+ * @param paProcs The process snapshot.
+ * @param cProcs The number of processes in the snaphot.
+ * @param puTerminalSession Where to return terminal session number.
+ * Optional.
+ */
+/** @todo r=bird: The 'Has' indicates a predicate function, which this is
+ * not. Predicate functions always returns bool. */
+static uint32_t vgsvcVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs,
+ PULONG puTerminalSession)
+{
+ if (!pSession)
+ {
+ VGSvcVerbose(1, "Session became invalid while enumerating!\n");
+ return 0;
+ }
+ if (!g_pfnLsaGetLogonSessionData)
+ return 0;
+
+ PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
+ NTSTATUS rcNt = g_pfnLsaGetLogonSessionData(pSession, &pSessionData);
+ if (rcNt != STATUS_SUCCESS)
+ {
+ VGSvcError("Could not get logon session data! rcNt=%#x\n", rcNt);
+ return 0;
+ }
+
+ if (!IsValidSid(pSessionData->Sid))
+ {
+ VGSvcError("User SID=%p is not valid\n", pSessionData->Sid);
+ if (pSessionData)
+ g_pfnLsaFreeReturnBuffer(pSessionData);
+ return 0;
+ }
+
+
+ /*
+ * Even if a user seems to be logged in, it could be a stale/orphaned logon
+ * session. So check if we have some processes bound to it by comparing the
+ * session <-> process LUIDs.
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t cProcessesFound = 0;
+ for (DWORD i = 0; i < cProcs; i++)
+ {
+ PSID pProcSID = paProcs[i].pSid;
+ if ( RT_SUCCESS(rc)
+ && pProcSID
+ && IsValidSid(pProcSID))
+ {
+ if (EqualSid(pSessionData->Sid, paProcs[i].pSid))
+ {
+ if (g_cVerbosity)
+ {
+ PRTUTF16 pszName;
+ int rc2 = vgsvcVMInfoWinProcessesGetModuleNameA(&paProcs[i], &pszName);
+ VGSvcVerbose(4, "Session %RU32: PID=%u (fInt=%RTbool): %ls\n",
+ pSessionData->Session, paProcs[i].id, paProcs[i].fInteractive,
+ RT_SUCCESS(rc2) ? pszName : L"<Unknown>");
+ if (RT_SUCCESS(rc2))
+ RTUtf16Free(pszName);
+ }
+
+ if (paProcs[i].fInteractive)
+ {
+ cProcessesFound++;
+ if (!g_cVerbosity) /* We want a bit more info on higher verbosity. */
+ break;
+ }
+ }
+ }
+ }
+
+ if (puTerminalSession)
+ *puTerminalSession = pSessionData->Session;
+
+ g_pfnLsaFreeReturnBuffer(pSessionData);
+
+ return cProcessesFound;
+}
+
+
+/**
+ * Save and noisy string copy.
+ *
+ * @param pwszDst Destination buffer.
+ * @param cbDst Size in bytes - not WCHAR count!
+ * @param pSrc Source string.
+ * @param pszWhat What this is. For the log.
+ */
+static void vgsvcVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
+{
+ Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
+
+ size_t cbCopy = pSrc->Length;
+ if (cbCopy + sizeof(WCHAR) > cbDst)
+ {
+ VGSvcVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n", pszWhat, cbCopy, cbDst);
+ cbCopy = cbDst - sizeof(WCHAR);
+ }
+ if (cbCopy)
+ memcpy(pwszDst, pSrc->Buffer, cbCopy);
+ pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
+}
+
+
+/**
+ * Detects whether a user is logged on.
+ *
+ * @returns true if logged in, false if not (or error).
+ * @param pUserInfo Where to return the user information.
+ * @param pSession The session to check.
+ */
+static bool vgsvcVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSession)
+{
+ AssertPtrReturn(pUserInfo, false);
+ if (!pSession)
+ return false;
+ if ( !g_pfnLsaGetLogonSessionData
+ || !g_pfnLsaNtStatusToWinError)
+ return false;
+
+ PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
+ NTSTATUS rcNt = g_pfnLsaGetLogonSessionData(pSession, &pSessionData);
+ if (rcNt != STATUS_SUCCESS)
+ {
+ ULONG ulError = g_pfnLsaNtStatusToWinError(rcNt);
+ switch (ulError)
+ {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ /* If we don't have enough memory it's hard to judge whether the specified user
+ * is logged in or not, so just assume he/she's not. */
+ VGSvcVerbose(3, "Not enough memory to retrieve logon session data!\n");
+ break;
+
+ case ERROR_NO_SUCH_LOGON_SESSION:
+ /* Skip session data which is not valid anymore because it may have been
+ * already terminated. */
+ break;
+
+ default:
+ VGSvcError("LsaGetLogonSessionData failed with error %u\n", ulError);
+ break;
+ }
+ if (pSessionData)
+ g_pfnLsaFreeReturnBuffer(pSessionData);
+ return false;
+ }
+ if (!pSessionData)
+ {
+ VGSvcError("Invalid logon session data!\n");
+ return false;
+ }
+
+ VGSvcVerbose(3, "Session data: Name=%ls, SessionID=%RU32, LogonID=%d,%u, LogonType=%u\n",
+ pSessionData->UserName.Buffer, pSessionData->Session,
+ pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart, pSessionData->LogonType);
+
+ if (vgsvcVMInfoSession0Separation())
+ {
+ /* Starting at Windows Vista user sessions begin with session 1, so
+ * ignore (stale) session 0 users. */
+ if ( pSessionData->Session == 0
+ /* Also check the logon time. */
+ || pSessionData->LogonTime.QuadPart == 0)
+ {
+ g_pfnLsaFreeReturnBuffer(pSessionData);
+ return false;
+ }
+ }
+
+ /*
+ * Only handle users which can login interactively or logged in
+ * remotely over native RDP.
+ */
+ bool fFoundUser = false;
+ if ( IsValidSid(pSessionData->Sid)
+ && ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
+ || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive
+ /* Note: We also need CachedInteractive in case Windows cached the credentials
+ * or just wants to reuse them! */
+ || (SECURITY_LOGON_TYPE)pSessionData->LogonType == CachedInteractive))
+ {
+ VGSvcVerbose(3, "Session LogonType=%u is supported -- looking up SID + type ...\n", pSessionData->LogonType);
+
+ /*
+ * Copy out relevant data.
+ */
+ vgsvcVMInfoWinSafeCopy(pUserInfo->wszUser, sizeof(pUserInfo->wszUser), &pSessionData->UserName, "User name");
+ vgsvcVMInfoWinSafeCopy(pUserInfo->wszAuthenticationPackage, sizeof(pUserInfo->wszAuthenticationPackage),
+ &pSessionData->AuthenticationPackage, "Authentication pkg name");
+ vgsvcVMInfoWinSafeCopy(pUserInfo->wszLogonDomain, sizeof(pUserInfo->wszLogonDomain),
+ &pSessionData->LogonDomain, "Logon domain name");
+
+ TCHAR szOwnerName[_MAX_PATH] = { 0 };
+ DWORD dwOwnerNameSize = sizeof(szOwnerName);
+ TCHAR szDomainName[_MAX_PATH] = { 0 };
+ DWORD dwDomainNameSize = sizeof(szDomainName);
+ SID_NAME_USE enmOwnerType = SidTypeInvalid;
+ if (!LookupAccountSid(NULL,
+ pSessionData->Sid,
+ szOwnerName,
+ &dwOwnerNameSize,
+ szDomainName,
+ &dwDomainNameSize,
+ &enmOwnerType))
+ {
+ DWORD dwErr = GetLastError();
+ /*
+ * If a network time-out prevents the function from finding the name or
+ * if a SID that does not have a corresponding account name (such as a
+ * logon SID that identifies a logon session), we get ERROR_NONE_MAPPED
+ * here that we just skip.
+ */
+ if (dwErr != ERROR_NONE_MAPPED)
+ VGSvcError("Failed looking up account info for user=%ls, error=$ld!\n", pUserInfo->wszUser, dwErr);
+ }
+ else
+ {
+ if (enmOwnerType == SidTypeUser) /* Only recognize users; we don't care about the rest! */
+ {
+ VGSvcVerbose(3, "Account User=%ls, Session=%u, LogonID=%d,%u, AuthPkg=%ls, Domain=%ls\n",
+ pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
+ pSessionData->LogonId.LowPart, pUserInfo->wszAuthenticationPackage, pUserInfo->wszLogonDomain);
+
+ /**
+ * Note: On certain Windows OSes WTSQuerySessionInformation leaks memory when used
+ * under a heavy stress situation. There are hotfixes available from Microsoft.
+ *
+ * See: http://support.microsoft.com/kb/970910
+ */
+ if (!s_fSkipRDPDetection)
+ {
+ /* Skip RDP detection on non-NT systems. */
+ if (g_WinVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ s_fSkipRDPDetection = true;
+
+ /* Skip RDP detection on Windows 2000.
+ * For Windows 2000 however we don't have any hotfixes, so just skip the
+ * RDP detection in any case. */
+ if ( g_WinVersion.dwMajorVersion == 5
+ && g_WinVersion.dwMinorVersion == 0)
+ s_fSkipRDPDetection = true;
+
+ /* Skip if we don't have the WTS API. */
+ if (!g_pfnWTSQuerySessionInformationA)
+ s_fSkipRDPDetection = true;
+
+ if (s_fSkipRDPDetection)
+ VGSvcVerbose(0, "Detection of logged-in users via RDP is disabled\n");
+ }
+
+ if (!s_fSkipRDPDetection)
+ {
+ Assert(g_pfnWTSQuerySessionInformationA);
+ Assert(g_pfnWTSFreeMemory);
+
+ /* Detect RDP sessions as well. */
+ LPTSTR pBuffer = NULL;
+ DWORD cbRet = 0;
+ int iState = -1;
+ if (g_pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE,
+ pSessionData->Session,
+ WTSConnectState,
+ &pBuffer,
+ &cbRet))
+ {
+ if (cbRet)
+ iState = *pBuffer;
+ VGSvcVerbose(3, "Account User=%ls, WTSConnectState=%d (%u)\n", pUserInfo->wszUser, iState, cbRet);
+ if ( iState == WTSActive /* User logged on to WinStation. */
+ || iState == WTSShadow /* Shadowing another WinStation. */
+ || iState == WTSDisconnected) /* WinStation logged on without client. */
+ {
+ /** @todo On Vista and W2K, always "old" user name are still
+ * there. Filter out the old one! */
+ VGSvcVerbose(3, "Account User=%ls using TCS/RDP, state=%d \n", pUserInfo->wszUser, iState);
+ fFoundUser = true;
+ }
+ if (pBuffer)
+ g_pfnWTSFreeMemory(pBuffer);
+ }
+ else
+ {
+ DWORD dwLastErr = GetLastError();
+ switch (dwLastErr)
+ {
+ /*
+ * Terminal services don't run (for example in W2K,
+ * nothing to worry about ...). ... or is on the Vista
+ * fast user switching page!
+ */
+ case ERROR_CTX_WINSTATION_NOT_FOUND:
+ VGSvcVerbose(3, "No WinStation found for user=%ls\n", pUserInfo->wszUser);
+ break;
+
+ default:
+ VGSvcVerbose(3, "Cannot query WTS connection state for user=%ls, error=%u\n",
+ pUserInfo->wszUser, dwLastErr);
+ break;
+ }
+
+ fFoundUser = true;
+ }
+ }
+ }
+ else
+ VGSvcVerbose(3, "SID owner type=%d not handled, skipping\n", enmOwnerType);
+ }
+
+ VGSvcVerbose(3, "Account User=%ls %s logged in\n", pUserInfo->wszUser, fFoundUser ? "is" : "is not");
+ }
+
+ if (fFoundUser)
+ pUserInfo->ulLastSession = pSessionData->Session;
+
+ g_pfnLsaFreeReturnBuffer(pSessionData);
+ return fFoundUser;
+}
+
+
+static int vgsvcVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
+ /* pszDomain is optional. */
+
+ char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
+ memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
+ int rc = RTStrCat(szPipeName, sizeof(szPipeName), pszUser);
+ if (RT_SUCCESS(rc))
+ {
+ bool fReportToHost = false;
+ VBoxGuestUserState userState = VBoxGuestUserState_Unknown;
+
+ RTLOCALIPCSESSION hSession;
+ rc = RTLocalIpcSessionConnect(&hSession, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
+ if (RT_SUCCESS(rc))
+ {
+ VBOXTRAYIPCHEADER ipcHdr =
+ {
+ /* .uMagic = */ VBOXTRAY_IPC_HDR_MAGIC,
+ /* .uHdrVersion = */ 0,
+ /* .uMsgType = */ VBOXTRAYIPCMSGTYPE_USERLASTINPUT,
+ /* .cbMsgData = */ 0 /* No msg */
+ };
+
+ rc = RTLocalIpcSessionWrite(hSession, &ipcHdr, sizeof(ipcHdr));
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXTRAYIPCRES_USERLASTINPUT ipcRes;
+ rc = RTLocalIpcSessionRead(hSession, &ipcRes, sizeof(ipcRes), NULL /* Exact read */);
+ if ( RT_SUCCESS(rc)
+ /* If uLastInput is set to UINT32_MAX VBoxTray was not able to retrieve the
+ * user's last input time. This might happen when running on Windows NT4 or older. */
+ && ipcRes.uLastInput != UINT32_MAX)
+ {
+ userState = (ipcRes.uLastInput * 1000) < g_uVMInfoUserIdleThresholdMS
+ ? VBoxGuestUserState_InUse
+ : VBoxGuestUserState_Idle;
+
+ rc = VGSvcUserUpdateF(pCache, pszUser, pszDomain, "UsageState",
+ userState == VBoxGuestUserState_InUse ? "InUse" : "Idle");
+
+ /*
+ * Note: vboxServiceUserUpdateF can return VINF_NO_CHANGE in case there wasn't anything
+ * to update. So only report the user's status to host when we really got something
+ * new.
+ */
+ fReportToHost = rc == VINF_SUCCESS;
+ VGSvcVerbose(4, "User '%s' (domain '%s') is idle for %RU32, fReportToHost=%RTbool\n",
+ pszUser, pszDomain ? pszDomain : "<None>", ipcRes.uLastInput, fReportToHost);
+
+#if 0 /* Do we want to write the idle time as well? */
+ /* Also write the user's current idle time, if there is any. */
+ if (userState == VBoxGuestUserState_Idle)
+ rc = vgsvcUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", "%RU32", ipcRes.uLastInputMs);
+ else
+ rc = vgsvcUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", NULL /* Delete property */);
+
+ if (RT_SUCCESS(rc))
+#endif
+ }
+#ifdef DEBUG
+ else if (RT_SUCCESS(rc) && ipcRes.uLastInput == UINT32_MAX)
+ VGSvcVerbose(4, "Last input for user '%s' is not supported, skipping\n", pszUser, rc);
+#endif
+ }
+#ifdef DEBUG
+ VGSvcVerbose(4, "Getting last input for user '%s' ended with rc=%Rrc\n", pszUser, rc);
+#endif
+ int rc2 = RTLocalIpcSessionClose(hSession);
+ if (RT_SUCCESS(rc) && RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ else
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND:
+ {
+ /* No VBoxTray (or too old version which does not support IPC) running
+ for the given user. Not much we can do then. */
+ VGSvcVerbose(4, "VBoxTray for user '%s' not running (anymore), no last input available\n", pszUser);
+
+ /* Overwrite rc from above. */
+ rc = VGSvcUserUpdateF(pCache, pszUser, pszDomain, "UsageState", "Idle");
+
+ fReportToHost = rc == VINF_SUCCESS;
+ if (fReportToHost)
+ userState = VBoxGuestUserState_Idle;
+ break;
+ }
+
+ default:
+ VGSvcError("Error querying last input for user '%s', rc=%Rrc\n", pszUser, rc);
+ break;
+ }
+ }
+
+ if (fReportToHost)
+ {
+ Assert(userState != VBoxGuestUserState_Unknown);
+ int rc2 = VbglR3GuestUserReportState(pszUser, pszDomain, userState, NULL /* No details */, 0);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Error reporting usage state %d for user '%s' to host, rc=%Rrc\n", userState, pszUser, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Retrieves the currently logged in users and stores their names along with the
+ * user count.
+ *
+ * @returns VBox status code.
+ * @param pCache Property cache to use for storing some of the lookup
+ * data in between calls.
+ * @param ppszUserList Where to store the user list (separated by commas).
+ * Must be freed with RTStrFree().
+ * @param pcUsersInList Where to store the number of users in the list.
+ */
+int VGSvcVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache, char **ppszUserList, uint32_t *pcUsersInList)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppszUserList, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcUsersInList, VERR_INVALID_POINTER);
+
+ int rc = RTOnce(&g_vgsvcWinVmInitOnce, vgsvcWinVmInfoInitOnce, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!g_pfnLsaEnumerateLogonSessions || !g_pfnEnumProcesses || !g_pfnLsaNtStatusToWinError)
+ return VERR_NOT_SUPPORTED;
+
+ rc = VbglR3GuestPropConnect(&s_uDebugGuestPropClientID);
+ AssertRC(rc);
+
+ char *pszUserList = NULL;
+ uint32_t cUsersInList = 0;
+
+ /* This function can report stale or orphaned interactive logon sessions
+ of already logged off users (especially in Windows 2000). */
+ PLUID paSessions = NULL;
+ ULONG cSessions = 0;
+ NTSTATUS rcNt = g_pfnLsaEnumerateLogonSessions(&cSessions, &paSessions);
+ if (rcNt != STATUS_SUCCESS)
+ {
+ ULONG uError = g_pfnLsaNtStatusToWinError(rcNt);
+ switch (uError)
+ {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ VGSvcError("Not enough memory to enumerate logon sessions!\n");
+ break;
+
+ case ERROR_SHUTDOWN_IN_PROGRESS:
+ /* If we're about to shutdown when we were in the middle of enumerating the logon
+ * sessions, skip the error to not confuse the user with an unnecessary log message. */
+ VGSvcVerbose(3, "Shutdown in progress ...\n");
+ uError = ERROR_SUCCESS;
+ break;
+
+ default:
+ VGSvcError("LsaEnumerate failed with error %RU32\n", uError);
+ break;
+ }
+
+ if (paSessions)
+ g_pfnLsaFreeReturnBuffer(paSessions);
+
+ return RTErrConvertFromWin32(uError);
+ }
+ VGSvcVerbose(3, "Found %u sessions\n", cSessions);
+
+ PVBOXSERVICEVMINFOPROC paProcs;
+ DWORD cProcs;
+ rc = vgsvcVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MEMORY)
+ VGSvcError("Not enough memory to enumerate processes\n");
+ else
+ VGSvcError("Failed to enumerate processes, rc=%Rrc\n", rc);
+ }
+ else
+ {
+ PVBOXSERVICEVMINFOUSER pUserInfo;
+ pUserInfo = (PVBOXSERVICEVMINFOUSER)RTMemAllocZ(cSessions * sizeof(VBOXSERVICEVMINFOUSER) + 1);
+ if (!pUserInfo)
+ VGSvcError("Not enough memory to store enumerated users!\n");
+ else
+ {
+ ULONG cUniqueUsers = 0;
+
+ /*
+ * Note: The cSessions loop variable does *not* correlate with
+ * the Windows session ID!
+ */
+ for (ULONG i = 0; i < cSessions; i++)
+ {
+ VGSvcVerbose(3, "Handling session %RU32 (of %RU32)\n", i + 1, cSessions);
+
+ VBOXSERVICEVMINFOUSER userSession;
+ if (vgsvcVMInfoWinIsLoggedIn(&userSession, &paSessions[i]))
+ {
+ VGSvcVerbose(4, "Handling user=%ls, domain=%ls, package=%ls, session=%RU32\n",
+ userSession.wszUser, userSession.wszLogonDomain, userSession.wszAuthenticationPackage,
+ userSession.ulLastSession);
+
+ /* Retrieve assigned processes of current session. */
+ uint32_t cCurSessionProcs = vgsvcVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs,
+ NULL /* Terminal session ID */);
+ /* Don't return here when current session does not have assigned processes
+ * anymore -- in that case we have to search through the unique users list below
+ * and see if got a stale user/session entry. */
+
+ if (g_cVerbosity > 3)
+ {
+ char szDebugSessionPath[255];
+ RTStrPrintf(szDebugSessionPath, sizeof(szDebugSessionPath),
+ "/VirtualBox/GuestInfo/Debug/LSA/Session/%RU32", userSession.ulLastSession);
+ VGSvcWritePropF(s_uDebugGuestPropClientID, szDebugSessionPath,
+ "#%RU32: cSessionProcs=%RU32 (of %RU32 procs total)",
+ s_uDebugIter, cCurSessionProcs, cProcs);
+ }
+
+ bool fFoundUser = false;
+ for (ULONG a = 0; a < cUniqueUsers; a++)
+ {
+ PVBOXSERVICEVMINFOUSER pCurUser = &pUserInfo[a];
+ AssertPtr(pCurUser);
+
+ if ( !wcscmp(userSession.wszUser, pCurUser->wszUser)
+ && !wcscmp(userSession.wszLogonDomain, pCurUser->wszLogonDomain)
+ && !wcscmp(userSession.wszAuthenticationPackage, pCurUser->wszAuthenticationPackage))
+ {
+ /*
+ * Only respect the highest session for the current user.
+ */
+ if (userSession.ulLastSession > pCurUser->ulLastSession)
+ {
+ VGSvcVerbose(4, "Updating user=%ls to %u processes (last used session: %RU32)\n",
+ pCurUser->wszUser, cCurSessionProcs, userSession.ulLastSession);
+
+ if (!cCurSessionProcs)
+ VGSvcVerbose(3, "Stale session for user=%ls detected! Processes: %RU32 -> %RU32, Session: %RU32 -> %RU32\n",
+ pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs,
+ pCurUser->ulLastSession, userSession.ulLastSession);
+
+ pCurUser->ulNumProcs = cCurSessionProcs;
+ pCurUser->ulLastSession = userSession.ulLastSession;
+ }
+ /* There can be multiple session objects using the same session ID for the
+ * current user -- so when we got the same session again just add the found
+ * processes to it. */
+ else if (pCurUser->ulLastSession == userSession.ulLastSession)
+ {
+ VGSvcVerbose(4, "Updating processes for user=%ls (old procs=%RU32, new procs=%RU32, session=%RU32)\n",
+ pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs, pCurUser->ulLastSession);
+
+ pCurUser->ulNumProcs = cCurSessionProcs;
+ }
+
+ fFoundUser = true;
+ break;
+ }
+ }
+
+ if (!fFoundUser)
+ {
+ VGSvcVerbose(4, "Adding new user=%ls (session=%RU32) with %RU32 processes\n",
+ userSession.wszUser, userSession.ulLastSession, cCurSessionProcs);
+
+ memcpy(&pUserInfo[cUniqueUsers], &userSession, sizeof(VBOXSERVICEVMINFOUSER));
+ pUserInfo[cUniqueUsers].ulNumProcs = cCurSessionProcs;
+ cUniqueUsers++;
+ Assert(cUniqueUsers <= cSessions);
+ }
+ }
+ }
+
+ if (g_cVerbosity > 3)
+ VGSvcWritePropF(s_uDebugGuestPropClientID, "/VirtualBox/GuestInfo/Debug/LSA",
+ "#%RU32: cSessions=%RU32, cProcs=%RU32, cUniqueUsers=%RU32",
+ s_uDebugIter, cSessions, cProcs, cUniqueUsers);
+
+ VGSvcVerbose(3, "Found %u unique logged-in user(s)\n", cUniqueUsers);
+
+ for (ULONG i = 0; i < cUniqueUsers; i++)
+ {
+ if (g_cVerbosity > 3)
+ {
+ char szDebugUserPath[255]; RTStrPrintf(szDebugUserPath, sizeof(szDebugUserPath), "/VirtualBox/GuestInfo/Debug/LSA/User/%RU32", i);
+ VGSvcWritePropF(s_uDebugGuestPropClientID, szDebugUserPath,
+ "#%RU32: szName=%ls, sessionID=%RU32, cProcs=%RU32",
+ s_uDebugIter, pUserInfo[i].wszUser, pUserInfo[i].ulLastSession, pUserInfo[i].ulNumProcs);
+ }
+
+ bool fAddUser = false;
+ if (pUserInfo[i].ulNumProcs)
+ fAddUser = true;
+
+ if (fAddUser)
+ {
+ VGSvcVerbose(3, "User '%ls' has %RU32 interactive processes (session=%RU32)\n",
+ pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulLastSession);
+
+ if (cUsersInList > 0)
+ {
+ rc = RTStrAAppend(&pszUserList, ",");
+ AssertRCBreakStmt(rc, RTStrFree(pszUserList));
+ }
+
+ cUsersInList += 1;
+
+ char *pszUser = NULL;
+ char *pszDomain = NULL;
+ rc = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszUser);
+ if ( RT_SUCCESS(rc)
+ && pUserInfo[i].wszLogonDomain)
+ rc = RTUtf16ToUtf8(pUserInfo[i].wszLogonDomain, &pszDomain);
+ if (RT_SUCCESS(rc))
+ {
+ /* Append user to users list. */
+ rc = RTStrAAppend(&pszUserList, pszUser);
+
+ /* Do idle detection. */
+ if (RT_SUCCESS(rc))
+ rc = vgsvcVMInfoWinWriteLastInput(pCache, pszUser, pszDomain);
+ }
+ else
+ rc = RTStrAAppend(&pszUserList, "<string-conversion-error>");
+
+ RTStrFree(pszUser);
+ RTStrFree(pszDomain);
+
+ AssertRCBreakStmt(rc, RTStrFree(pszUserList));
+ }
+ }
+
+ RTMemFree(pUserInfo);
+ }
+ vgsvcVMInfoWinProcessesFree(cProcs, paProcs);
+ }
+ if (paSessions)
+ g_pfnLsaFreeReturnBuffer(paSessions);
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppszUserList = pszUserList;
+ *pcUsersInList = cUsersInList;
+ }
+
+ s_uDebugIter++;
+ VbglR3GuestPropDisconnect(s_uDebugGuestPropClientID);
+
+ return rc;
+}
+
+
+int VGSvcVMInfoWinGetComponentVersions(uint32_t uClientID)
+{
+ int rc;
+ char szSysDir[_MAX_PATH] = {0};
+ char szWinDir[_MAX_PATH] = {0};
+ char szDriversDir[_MAX_PATH + 32] = {0};
+
+ /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
+ GetSystemDirectory(szSysDir, _MAX_PATH);
+ GetWindowsDirectory(szWinDir, _MAX_PATH);
+ RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
+#ifdef RT_ARCH_AMD64
+ char szSysWowDir[_MAX_PATH + 32] = {0};
+ RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
+#endif
+
+ /* The file information table. */
+ const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
+ {
+ { szSysDir, "VBoxControl.exe" },
+ { szSysDir, "VBoxHook.dll" },
+ { szSysDir, "VBoxDisp.dll" },
+ { szSysDir, "VBoxTray.exe" },
+ { szSysDir, "VBoxService.exe" },
+ { szSysDir, "VBoxMRXNP.dll" },
+ { szSysDir, "VBoxGINA.dll" },
+ { szSysDir, "VBoxCredProv.dll" },
+
+ /* On 64-bit we don't yet have the OpenGL DLLs in native format.
+ So just enumerate the 32-bit files in the SYSWOW directory. */
+#ifdef RT_ARCH_AMD64
+ { szSysWowDir, "VBoxOGLarrayspu.dll" },
+ { szSysWowDir, "VBoxOGLcrutil.dll" },
+ { szSysWowDir, "VBoxOGLerrorspu.dll" },
+ { szSysWowDir, "VBoxOGLpackspu.dll" },
+ { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
+ { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
+ { szSysWowDir, "VBoxOGL.dll" },
+#else /* !RT_ARCH_AMD64 */
+ { szSysDir, "VBoxOGLarrayspu.dll" },
+ { szSysDir, "VBoxOGLcrutil.dll" },
+ { szSysDir, "VBoxOGLerrorspu.dll" },
+ { szSysDir, "VBoxOGLpackspu.dll" },
+ { szSysDir, "VBoxOGLpassthroughspu.dll" },
+ { szSysDir, "VBoxOGLfeedbackspu.dll" },
+ { szSysDir, "VBoxOGL.dll" },
+#endif /* !RT_ARCH_AMD64 */
+
+ { szDriversDir, "VBoxGuest.sys" },
+ { szDriversDir, "VBoxMouseNT.sys" },
+ { szDriversDir, "VBoxMouse.sys" },
+ { szDriversDir, "VBoxSF.sys" },
+ { szDriversDir, "VBoxVideo.sys" },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
+ {
+ char szVer[128];
+ rc = VGSvcUtilWinGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
+ char szPropPath[256];
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
+ if ( rc != VERR_FILE_NOT_FOUND
+ && rc != VERR_PATH_NOT_FOUND)
+ VGSvcWritePropF(uClientID, szPropPath, "%s", szVer);
+ else
+ VGSvcWritePropF(uClientID, szPropPath, NULL);
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp
new file mode 100644
index 00000000..308bd90e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp
@@ -0,0 +1,1689 @@
+/* $Id: VBoxServiceVMInfo.cpp $ */
+/** @file
+ * VBoxService - Virtual Machine Information for the Host.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vgsvc_vminfo VBoxService - VM Information
+ *
+ * The VM Information subservice provides heaps of useful information about the
+ * VM via guest properties.
+ *
+ * Guest properties is a limited database maintained by the HGCM GuestProperties
+ * service in cooperation with the Main API (VBoxSVC). Properties have a name
+ * (ours are path like), a string value, and a nanosecond timestamp (unix
+ * epoch). The timestamp lets the user see how recent the information is. As
+ * an laternative to polling on changes, it is also possible to wait on changes
+ * via the Main API or VBoxManage on the host side and VBoxControl in the guest.
+ *
+ * The namespace "/VirtualBox/" is reserved for value provided by VirtualBox.
+ * This service provides all the information under "/VirtualBox/GuestInfo/".
+ *
+ *
+ * @section sec_vgsvc_vminfo_beacons Beacons
+ *
+ * The subservice does not write properties unless there are changes. So, in
+ * order for the host side to know that information is up to date despite an
+ * oldish timestamp we define a couple of values that are always updated and can
+ * reliably used to figure how old the information actually is.
+ *
+ * For the networking part "/VirtualBox/GuestInfo/Net/Count" is the value to
+ * watch out for.
+ *
+ * For the login part, it's possible that we intended to use
+ * "/VirtualBox/GuestInfo/OS/LoggedInUsers" for this, however it is not defined
+ * correctly and current does NOT work as a beacon.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+# include <iprt/win/iphlpapi.h>
+# include <iprt/win/ws2tcpip.h>
+# include <iprt/win/windows.h>
+# include <Ntsecapi.h>
+#else
+# define __STDC_LIMIT_MACROS
+# include <arpa/inet.h>
+# include <errno.h>
+# include <netinet/in.h>
+# include <sys/ioctl.h>
+# include <sys/socket.h>
+# include <net/if.h>
+# include <pwd.h> /* getpwuid */
+# include <unistd.h>
+# if !defined(RT_OS_OS2) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_HAIKU)
+# include <utmpx.h> /** @todo FreeBSD 9 should have this. */
+# endif
+# ifdef RT_OS_OS2
+# include <net/if_dl.h>
+# endif
+# ifdef RT_OS_SOLARIS
+# include <sys/sockio.h>
+# include <net/if_arp.h>
+# endif
+# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD)
+# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
+# include <net/if_dl.h> /* LLADDR */
+# include <netdb.h> /* getnameinfo */
+# endif
+# ifdef VBOX_WITH_DBUS
+# include <VBox/dbus.h>
+# endif
+#endif
+
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/system.h>
+#include <iprt/time.h>
+#include <iprt/assert.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <VBox/VBoxGuestLib.h>
+#include "VBoxServiceInternal.h"
+#include "VBoxServiceUtils.h"
+#include "VBoxServicePropCache.h"
+
+
+/** Structure containing information about a location awarness
+ * client provided by the host. */
+/** @todo Move this (and functions) into VbglR3. */
+typedef struct VBOXSERVICELACLIENTINFO
+{
+ uint32_t uID;
+ char *pszName;
+ char *pszLocation;
+ char *pszDomain;
+ bool fAttached;
+ uint64_t uAttachedTS;
+} VBOXSERVICELACLIENTINFO, *PVBOXSERVICELACLIENTINFO;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The vminfo interval (milliseconds). */
+static uint32_t g_cMsVMInfoInterval = 0;
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
+/** The guest property service client ID. */
+static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
+/** Number of currently logged in users in OS. */
+static uint32_t g_cVMInfoLoggedInUsers = 0;
+/** The guest property cache. */
+static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
+static const char *g_pszPropCacheValLoggedInUsersList = "/VirtualBox/GuestInfo/OS/LoggedInUsersList";
+static const char *g_pszPropCacheValLoggedInUsers = "/VirtualBox/GuestInfo/OS/LoggedInUsers";
+static const char *g_pszPropCacheValNoLoggedInUsers = "/VirtualBox/GuestInfo/OS/NoLoggedInUsers";
+static const char *g_pszPropCacheValNetCount = "/VirtualBox/GuestInfo/Net/Count";
+/** A guest user's guest property root key. */
+static const char *g_pszPropCacheValUser = "/VirtualBox/GuestInfo/User/";
+/** The VM session ID. Changes whenever the VM is restored or reset. */
+static uint64_t g_idVMInfoSession;
+/** The last attached locartion awareness (LA) client timestamp. */
+static uint64_t g_LAClientAttachedTS = 0;
+/** The current LA client info. */
+static VBOXSERVICELACLIENTINFO g_LAClientInfo;
+/** User idle threshold (in ms). This specifies the minimum time a user is considered
+ * as being idle and then will be reported to the host. Default is 5s. */
+uint32_t g_uVMInfoUserIdleThresholdMS = 5 * 1000;
+
+
+/*********************************************************************************************************************************
+* Defines *
+*********************************************************************************************************************************/
+static const char *g_pszLAActiveClient = "/VirtualBox/HostInfo/VRDP/ActiveClient";
+
+#ifdef VBOX_WITH_DBUS
+/** @name ConsoleKit defines (taken from 0.4.5).
+ * @{ */
+# define CK_NAME "org.freedesktop.ConsoleKit"
+# define CK_PATH "/org/freedesktop/ConsoleKit"
+# define CK_INTERFACE "org.freedesktop.ConsoleKit"
+# define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+# define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+# define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat"
+# define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+/** @} */
+#endif
+
+
+
+/**
+ * Signals the event so that a re-enumeration of VM-specific
+ * information (like logged in users) can happen.
+ *
+ * @return IPRT status code.
+ */
+int VGSvcVMInfoSignal(void)
+{
+ /* Trigger a re-enumeration of all logged-in users by unblocking
+ * the multi event semaphore of the VMInfo thread. */
+ if (g_hVMInfoEvent)
+ return RTSemEventMultiSignal(g_hVMInfoEvent);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnPreInit}
+ */
+static DECLCALLBACK(int) vbsvcVMInfoPreInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnOption}
+ */
+static DECLCALLBACK(int) vbsvcVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
+{
+ /** @todo Use RTGetOpt here. */
+
+ int rc = -1;
+ if (ppszShort)
+ /* no short options */;
+ else if (!strcmp(argv[*pi], "--vminfo-interval"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
+ else if (!strcmp(argv[*pi], "--vminfo-user-idle-threshold"))
+ rc = VGSvcArgUInt32(argc, argv, "", pi, &g_uVMInfoUserIdleThresholdMS, 1, UINT32_MAX - 1);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vbsvcVMInfoInit(void)
+{
+ /*
+ * If not specified, find the right interval default.
+ * Then create the event sem to block on.
+ */
+ if (!g_cMsVMInfoInterval)
+ g_cMsVMInfoInterval = g_DefaultInterval * 1000;
+ if (!g_cMsVMInfoInterval)
+ {
+ /* Set it to 5s by default for location awareness checks. */
+ g_cMsVMInfoInterval = 5 * 1000;
+ }
+
+ int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
+ AssertRCReturn(rc, rc);
+
+ VbglR3GetSessionId(&g_idVMInfoSession);
+ /* The status code is ignored as this information is not available with VBox < 3.2.10. */
+
+ /* Initialize the LA client object. */
+ RT_ZERO(g_LAClientInfo);
+
+ rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
+ if (RT_SUCCESS(rc))
+ VGSvcVerbose(3, "Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
+ else
+ {
+ /* If the service was not found, we disable this service without
+ causing VBoxService to fail. */
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
+ {
+ VGSvcVerbose(0, "Guest property service is not available, disabling the service\n");
+ rc = VERR_SERVICE_DISABLED;
+ }
+ else
+ VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
+ RTSemEventMultiDestroy(g_hVMInfoEvent);
+ g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ VGSvcPropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
+
+ /*
+ * Declare some guest properties with flags and reset values.
+ */
+ int rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList,
+ VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT,
+ NULL /* Delete on exit */);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValLoggedInUsersList, rc2);
+
+ rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers,
+ VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT, "0");
+ if (RT_FAILURE(rc2))
+ VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValLoggedInUsers, rc2);
+
+ rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers,
+ VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT, "true");
+ if (RT_FAILURE(rc2))
+ VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValNoLoggedInUsers, rc2);
+
+ rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNetCount,
+ VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE,
+ NULL /* Delete on exit */);
+ if (RT_FAILURE(rc2))
+ VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValNetCount, rc2);
+
+ /*
+ * Get configuration guest properties from the host.
+ * Note: All properties should have sensible defaults in case the lookup here fails.
+ */
+ char *pszValue;
+ rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--vminfo-user-idle-threshold",
+ true /* Read only */, &pszValue, NULL /* Flags */, NULL /* Timestamp */);
+ if (RT_SUCCESS(rc2))
+ {
+ AssertPtr(pszValue);
+ g_uVMInfoUserIdleThresholdMS = RT_CLAMP(RTStrToUInt32(pszValue), 1000, UINT32_MAX - 1);
+ RTStrFree(pszValue);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Retrieves a specifiy client LA property.
+ *
+ * @return IPRT status code.
+ * @param uClientID LA client ID to retrieve property for.
+ * @param pszProperty Property (without path) to retrieve.
+ * @param ppszValue Where to store value of property.
+ * @param puTimestamp Timestamp of property to retrieve. Optional.
+ */
+static int vgsvcGetLAClientValue(uint32_t uClientID, const char *pszProperty, char **ppszValue, uint64_t *puTimestamp)
+{
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszProperty, VERR_INVALID_POINTER);
+
+ int rc;
+
+ char pszClientPath[255];
+/** @todo r=bird: Another pointless RTStrPrintf test with wrong status code to boot. */
+ if (RTStrPrintf(pszClientPath, sizeof(pszClientPath),
+ "/VirtualBox/HostInfo/VRDP/Client/%RU32/%s", uClientID, pszProperty))
+ {
+ rc = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, pszClientPath, true /* Read only */,
+ ppszValue, NULL /* Flags */, puTimestamp);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Retrieves LA client information. On success the returned structure will have allocated
+ * objects which need to be free'd with vboxServiceFreeLAClientInfo.
+ *
+ * @return IPRT status code.
+ * @param uClientID Client ID to retrieve information for.
+ * @param pClient Pointer where to store the client information.
+ */
+static int vgsvcGetLAClientInfo(uint32_t uClientID, PVBOXSERVICELACLIENTINFO pClient)
+{
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+
+ int rc = vgsvcGetLAClientValue(uClientID, "Name", &pClient->pszName,
+ NULL /* Timestamp */);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszAttach;
+ rc = vgsvcGetLAClientValue(uClientID, "Attach", &pszAttach, &pClient->uAttachedTS);
+ if (RT_SUCCESS(rc))
+ {
+ AssertPtr(pszAttach);
+ pClient->fAttached = RTStrICmp(pszAttach, "1") == 0;
+
+ RTStrFree(pszAttach);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ rc = vgsvcGetLAClientValue(uClientID, "Location", &pClient->pszLocation, NULL /* Timestamp */);
+ if (RT_SUCCESS(rc))
+ rc = vgsvcGetLAClientValue(uClientID, "Domain", &pClient->pszDomain, NULL /* Timestamp */);
+ if (RT_SUCCESS(rc))
+ pClient->uID = uClientID;
+
+ return rc;
+}
+
+
+/**
+ * Frees all allocated LA client information of a structure.
+ *
+ * @param pClient Pointer to client information structure to free.
+ */
+static void vgsvcFreeLAClientInfo(PVBOXSERVICELACLIENTINFO pClient)
+{
+ if (pClient)
+ {
+ if (pClient->pszName)
+ {
+ RTStrFree(pClient->pszName);
+ pClient->pszName = NULL;
+ }
+ if (pClient->pszLocation)
+ {
+ RTStrFree(pClient->pszLocation);
+ pClient->pszLocation = NULL;
+ }
+ if (pClient->pszDomain)
+ {
+ RTStrFree(pClient->pszDomain);
+ pClient->pszDomain = NULL;
+ }
+ }
+}
+
+
+/**
+ * Updates a per-guest user guest property inside the given property cache.
+ *
+ * @return IPRT status code.
+ * @param pCache Pointer to guest property cache to update user in.
+ * @param pszUser Name of guest user to update.
+ * @param pszDomain Domain of guest user to update. Optional.
+ * @param pszKey Key name of guest property to update.
+ * @param pszValueFormat Guest property value to set. Pass NULL for deleting
+ * the property.
+ */
+int VGSvcUserUpdateF(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain,
+ const char *pszKey, const char *pszValueFormat, ...)
+{
+ AssertPtrReturn(pCache, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
+ /* pszDomain is optional. */
+ AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+ /* pszValueFormat is optional. */
+
+ int rc = VINF_SUCCESS;
+
+ char *pszName;
+ if (pszDomain)
+ {
+/** @todo r=bird: RTStrAPrintf returns -1, not zero on failure! */
+ if (!RTStrAPrintf(&pszName, "%s%s@%s/%s", g_pszPropCacheValUser, pszUser, pszDomain, pszKey))
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+/** @todo r=bird: RTStrAPrintf returns -1, not zero on failure! You got it
+ * right 5 lines further down... */
+ if (!RTStrAPrintf(&pszName, "%s%s/%s", g_pszPropCacheValUser, pszUser, pszKey))
+ rc = VERR_NO_MEMORY;
+ }
+
+ char *pszValue = NULL;
+ if ( RT_SUCCESS(rc)
+ && pszValueFormat)
+ {
+ va_list va;
+ va_start(va, pszValueFormat);
+ if (RTStrAPrintfV(&pszValue, pszValueFormat, va) < 0)
+ rc = VERR_NO_MEMORY;
+ va_end(va);
+ if ( RT_SUCCESS(rc)
+ && !pszValue)
+ rc = VERR_NO_STR_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = VGSvcPropCacheUpdate(pCache, pszName, pszValue);
+ if (rc == VINF_SUCCESS) /* VGSvcPropCacheUpdate will also return VINF_NO_CHANGE. */
+ {
+ /** @todo Combine updating flags w/ updating the actual value. */
+ rc = VGSvcPropCacheUpdateEntry(pCache, pszName,
+ VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT,
+ NULL /* Delete on exit */);
+ }
+
+ RTStrFree(pszValue);
+ RTStrFree(pszName);
+ return rc;
+}
+
+
+/**
+ * Writes the properties that won't change while the service is running.
+ *
+ * Errors are ignored.
+ */
+static void vgsvcVMInfoWriteFixedProperties(void)
+{
+ /*
+ * First get OS information that won't change.
+ */
+ char szInfo[256];
+ int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
+ "%s", RT_FAILURE(rc) ? "" : szInfo);
+
+ rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
+ "%s", RT_FAILURE(rc) ? "" : szInfo);
+
+ rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
+ "%s", RT_FAILURE(rc) ? "" : szInfo);
+
+ rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
+ "%s", RT_FAILURE(rc) ? "" : szInfo);
+
+ /*
+ * Retrieve version information about Guest Additions and installed files (components).
+ */
+ char *pszAddVer;
+ char *pszAddVerExt;
+ char *pszAddRev;
+ rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddVerExt, &pszAddRev);
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
+ "%s", RT_FAILURE(rc) ? "" : pszAddVer);
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VersionExt",
+ "%s", RT_FAILURE(rc) ? "" : pszAddVerExt);
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
+ "%s", RT_FAILURE(rc) ? "" : pszAddRev);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszAddVer);
+ RTStrFree(pszAddVerExt);
+ RTStrFree(pszAddRev);
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Do windows specific properties.
+ */
+ char *pszInstDir;
+ rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
+ VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
+ "%s", RT_FAILURE(rc) ? "" : pszInstDir);
+ if (RT_SUCCESS(rc))
+ RTStrFree(pszInstDir);
+
+ VGSvcVMInfoWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
+#endif
+}
+
+
+#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
+/*
+ * Simple wrapper to work around compiler-specific va_list madness.
+ */
+static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message, DBusError *error, int first_arg_type, ...)
+{
+ va_list va;
+ va_start(va, first_arg_type);
+ dbus_bool_t ret = dbus_message_get_args_valist(message, error, first_arg_type, va);
+ va_end(va);
+ return ret;
+}
+#endif
+
+
+/**
+ * Provide information about active users.
+ */
+static int vgsvcVMInfoWriteUsers(void)
+{
+ int rc;
+ char *pszUserList = NULL;
+ uint32_t cUsersInList = 0;
+
+#ifdef RT_OS_WINDOWS
+ rc = VGSvcVMInfoWinWriteUsers(&g_VMInfoPropCache, &pszUserList, &cUsersInList);
+
+#elif defined(RT_OS_FREEBSD)
+ /** @todo FreeBSD: Port logged on user info retrieval.
+ * However, FreeBSD 9 supports utmpx, so we could use the code
+ * block below (?). */
+ rc = VERR_NOT_IMPLEMENTED;
+
+#elif defined(RT_OS_HAIKU)
+ /** @todo Haiku: Port logged on user info retrieval. */
+ rc = VERR_NOT_IMPLEMENTED;
+
+#elif defined(RT_OS_OS2)
+ /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */
+ rc = VERR_NOT_IMPLEMENTED;
+
+#else
+ setutxent();
+ utmpx *ut_user;
+ uint32_t cListSize = 32;
+
+ /* Allocate a first array to hold 32 users max. */
+ char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
+ if (papszUsers)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_NO_MEMORY;
+
+ /* Process all entries in the utmp file.
+ * Note: This only handles */
+ while ( (ut_user = getutxent())
+ && RT_SUCCESS(rc))
+ {
+# ifdef RT_OS_DARWIN /* No ut_user->ut_session on Darwin */
+ VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32)\n", ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid);
+# else
+ VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32, session: %RU32)\n",
+ ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session);
+# endif
+ if (cUsersInList > cListSize)
+ {
+ cListSize += 32;
+ void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
+ AssertBreakStmt(pvNew, cListSize -= 32);
+ papszUsers = (char **)pvNew;
+ }
+
+ /* Make sure we don't add user names which are not
+ * part of type USER_PROCES. */
+ if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */
+ {
+ bool fFound = false;
+ for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
+ fFound = strcmp(papszUsers[i], ut_user->ut_user) == 0;
+
+ if (!fFound)
+ {
+ VGSvcVerbose(4, "Adding user '%s' (type: %d) to list\n", ut_user->ut_user, ut_user->ut_type);
+
+ rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
+ if (RT_FAILURE(rc))
+ break;
+ cUsersInList++;
+ }
+ }
+ }
+
+# ifdef VBOX_WITH_DBUS
+# if defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
+ DBusError dbErr;
+ DBusConnection *pConnection = NULL;
+ int rc2 = RTDBusLoadLib();
+ bool fHaveLibDbus = false;
+ if (RT_SUCCESS(rc2))
+ {
+ /* Handle desktop sessions using ConsoleKit. */
+ VGSvcVerbose(4, "Checking ConsoleKit sessions ...\n");
+ fHaveLibDbus = true;
+ dbus_error_init(&dbErr);
+ pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
+ }
+
+ if ( pConnection
+ && !dbus_error_is_set(&dbErr))
+ {
+ /* Get all available sessions. */
+/** @todo r=bird: What's the point of hardcoding things here when we've taken the pain of defining CK_XXX constants at the top of the file (or vice versa)? */
+ DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
+ "/org/freedesktop/ConsoleKit/Manager",
+ "org.freedesktop.ConsoleKit.Manager",
+ "GetSessions");
+ if ( pMsgSessions
+ && dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection,
+ pMsgSessions, 30 * 1000 /* 30s timeout */,
+ &dbErr);
+ if ( pReplySessions
+ && !dbus_error_is_set(&dbErr))
+ {
+ char **ppszSessions;
+ int cSessions;
+ if ( dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL
+ && vboxService_dbus_message_get_args(pReplySessions, &dbErr, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH, &ppszSessions, &cSessions,
+ DBUS_TYPE_INVALID /* Termination */))
+ {
+ VGSvcVerbose(4, "ConsoleKit: retrieved %RU16 session(s)\n", cSessions);
+
+ char **ppszCurSession = ppszSessions;
+ for (ppszCurSession; ppszCurSession && *ppszCurSession; ppszCurSession++)
+ {
+ VGSvcVerbose(4, "ConsoleKit: processing session '%s' ...\n", *ppszCurSession);
+
+ /* Only respect active sessions .*/
+ bool fActive = false;
+ DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
+ *ppszCurSession,
+ "org.freedesktop.ConsoleKit.Session",
+ "IsActive");
+ if ( pMsgSessionActive
+ && dbus_message_get_type(pMsgSessionActive) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ DBusMessage *pReplySessionActive = dbus_connection_send_with_reply_and_block(pConnection,
+ pMsgSessionActive,
+ 30 * 1000 /*sec*/,
+ &dbErr);
+ if ( pReplySessionActive
+ && !dbus_error_is_set(&dbErr))
+ {
+ DBusMessageIter itMsg;
+ if ( dbus_message_iter_init(pReplySessionActive, &itMsg)
+ && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_BOOLEAN)
+ {
+ /* Get uid from message. */
+ int val;
+ dbus_message_iter_get_basic(&itMsg, &val);
+ fActive = val >= 1;
+ }
+
+ if (pReplySessionActive)
+ dbus_message_unref(pReplySessionActive);
+ }
+
+ if (pMsgSessionActive)
+ dbus_message_unref(pMsgSessionActive);
+ }
+
+ VGSvcVerbose(4, "ConsoleKit: session '%s' is %s\n",
+ *ppszCurSession, fActive ? "active" : "not active");
+
+ /* *ppszCurSession now contains the object path
+ * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */
+ DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
+ *ppszCurSession,
+ "org.freedesktop.ConsoleKit.Session",
+ "GetUnixUser");
+ if ( fActive
+ && pMsgUnixUser
+ && dbus_message_get_type(pMsgUnixUser) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ DBusMessage *pReplyUnixUser = dbus_connection_send_with_reply_and_block(pConnection,
+ pMsgUnixUser,
+ 30 * 1000 /* 30s timeout */,
+ &dbErr);
+ if ( pReplyUnixUser
+ && !dbus_error_is_set(&dbErr))
+ {
+ DBusMessageIter itMsg;
+ if ( dbus_message_iter_init(pReplyUnixUser, &itMsg)
+ && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_UINT32)
+ {
+ /* Get uid from message. */
+ uint32_t uid;
+ dbus_message_iter_get_basic(&itMsg, &uid);
+
+ /** @todo Add support for getting UID_MIN (/etc/login.defs on
+ * Debian). */
+ uint32_t uid_min = 1000;
+
+ /* Look up user name (realname) from uid. */
+ setpwent();
+ struct passwd *ppwEntry = getpwuid(uid);
+ if ( ppwEntry
+ && ppwEntry->pw_name)
+ {
+ if (ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */)
+ {
+ VGSvcVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n",
+ *ppszCurSession, ppwEntry->pw_name, uid);
+
+ bool fFound = false;
+ for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
+ fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0;
+
+ if (!fFound)
+ {
+ VGSvcVerbose(4, "ConsoleKit: adding user '%s' to list\n", ppwEntry->pw_name);
+
+ rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name);
+ if (RT_FAILURE(rc))
+ break;
+ cUsersInList++;
+ }
+ }
+ /* else silently ignore the user */
+ }
+ else
+ VGSvcError("ConsoleKit: unable to lookup user name for uid=%RU32\n", uid);
+ }
+ else
+ AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n"));
+ }
+
+ if (pReplyUnixUser)
+ dbus_message_unref(pReplyUnixUser);
+ }
+ else if (fActive) /* don't bitch about inactive users */
+ {
+ static int s_iBitchedAboutConsoleKit = 0;
+ if (s_iBitchedAboutConsoleKit < 1)
+ {
+ s_iBitchedAboutConsoleKit++;
+ VGSvcError("ConsoleKit: unable to retrieve user for session '%s' (msg type=%d): %s\n",
+ *ppszCurSession, dbus_message_get_type(pMsgUnixUser),
+ dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
+ }
+ }
+
+ if (pMsgUnixUser)
+ dbus_message_unref(pMsgUnixUser);
+ }
+
+ dbus_free_string_array(ppszSessions);
+ }
+ else
+ VGSvcError("ConsoleKit: unable to retrieve session parameters (msg type=%d): %s\n",
+ dbus_message_get_type(pMsgSessions),
+ dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
+ dbus_message_unref(pReplySessions);
+ }
+
+ if (pMsgSessions)
+ {
+ dbus_message_unref(pMsgSessions);
+ pMsgSessions = NULL;
+ }
+ }
+ else
+ {
+ static int s_iBitchedAboutConsoleKit = 0;
+ if (s_iBitchedAboutConsoleKit < 3)
+ {
+ s_iBitchedAboutConsoleKit++;
+ VGSvcError("Unable to invoke ConsoleKit (%d/3) -- maybe not installed / used? Error: %s\n",
+ s_iBitchedAboutConsoleKit,
+ dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
+ }
+ }
+
+ if (pMsgSessions)
+ dbus_message_unref(pMsgSessions);
+ }
+ else
+ {
+ static int s_iBitchedAboutDBus = 0;
+ if (s_iBitchedAboutDBus < 3)
+ {
+ s_iBitchedAboutDBus++;
+ VGSvcError("Unable to connect to system D-Bus (%d/3): %s\n", s_iBitchedAboutDBus,
+ fHaveLibDbus && dbus_error_is_set(&dbErr) ? dbErr.message : "D-Bus not installed");
+ }
+ }
+
+ if ( fHaveLibDbus
+ && dbus_error_is_set(&dbErr))
+ dbus_error_free(&dbErr);
+# endif /* RT_OS_LINUX */
+# endif /* VBOX_WITH_DBUS */
+
+ /** @todo Fedora/others: Handle systemd-loginctl. */
+
+ /* Calc the string length. */
+ size_t cchUserList = 0;
+ if (RT_SUCCESS(rc))
+ for (uint32_t i = 0; i < cUsersInList; i++)
+ cchUserList += (i != 0) + strlen(papszUsers[i]);
+
+ /* Build the user list. */
+ if (cchUserList > 0)
+ {
+ if (RT_SUCCESS(rc))
+ rc = RTStrAllocEx(&pszUserList, cchUserList + 1);
+ if (RT_SUCCESS(rc))
+ {
+ char *psz = pszUserList;
+ for (uint32_t i = 0; i < cUsersInList; i++)
+ {
+ if (i != 0)
+ *psz++ = ',';
+ size_t cch = strlen(papszUsers[i]);
+ memcpy(psz, papszUsers[i], cch);
+ psz += cch;
+ }
+ *psz = '\0';
+ }
+ }
+
+ /* Cleanup. */
+ for (uint32_t i = 0; i < cUsersInList; i++)
+ RTStrFree(papszUsers[i]);
+ RTMemFree(papszUsers);
+
+ endutxent(); /* Close utmpx file. */
+#endif /* !RT_OS_WINDOWS && !RT_OS_FREEBSD && !RT_OS_HAIKU && !RT_OS_OS2 */
+
+ Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
+
+ /*
+ * If the user enumeration above failed, reset the user count to 0 except
+ * we didn't have enough memory anymore. In that case we want to preserve
+ * the previous user count in order to not confuse third party tools which
+ * rely on that count.
+ */
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MEMORY)
+ {
+ static int s_iVMInfoBitchedOOM = 0;
+ if (s_iVMInfoBitchedOOM++ < 3)
+ VGSvcVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%RU32)\n",
+ g_cVMInfoLoggedInUsers);
+ cUsersInList = g_cVMInfoLoggedInUsers;
+ }
+ else
+ cUsersInList = 0;
+ }
+ else /* Preserve logged in users count. */
+ g_cVMInfoLoggedInUsers = cUsersInList;
+
+ VGSvcVerbose(4, "cUsersInList=%RU32, pszUserList=%s, rc=%Rrc\n", cUsersInList, pszUserList ? pszUserList : "<NULL>", rc);
+
+ if (pszUserList)
+ {
+ AssertMsg(cUsersInList, ("pszUserList contains users whereas cUsersInList is 0\n"));
+ rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, "%s", pszUserList);
+ }
+ else
+ rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, NULL);
+ if (RT_FAILURE(rc))
+ VGSvcError("Error writing logged in users list, rc=%Rrc\n", rc);
+
+ rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, "%RU32", cUsersInList);
+ if (RT_FAILURE(rc))
+ VGSvcError("Error writing logged in users count, rc=%Rrc\n", rc);
+
+/** @todo r=bird: What's this 'beacon' nonsense here? It's _not_ defined with
+ * the VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE flag set!! */
+ rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers, cUsersInList == 0 ? "true" : "false");
+ if (RT_FAILURE(rc))
+ VGSvcError("Error writing no logged in users beacon, rc=%Rrc\n", rc);
+
+ if (pszUserList)
+ RTStrFree(pszUserList);
+
+ VGSvcVerbose(4, "Writing users returned with rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Provide information about the guest network.
+ */
+static int vgsvcVMInfoWriteNetwork(void)
+{
+ uint32_t cIfsReported = 0;
+ char szPropPath[256];
+
+#ifdef RT_OS_WINDOWS
+ /* */
+ if ( !g_pfnGetAdaptersInfo
+ && ( !g_pfnWSAIoctl
+ || !g_pfnWSASocketA
+ || !g_pfnWSAGetLastError
+ || !g_pfninet_ntoa
+ || !g_pfnclosesocket) )
+ return VINF_SUCCESS;
+
+ ULONG cbAdpInfo = sizeof(IP_ADAPTER_INFO);
+ IP_ADAPTER_INFO *pAdpInfo = (IP_ADAPTER_INFO *)RTMemAlloc(cbAdpInfo);
+ if (!pAdpInfo)
+ {
+ VGSvcError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
+ return VERR_NO_MEMORY;
+ }
+ DWORD dwRet = g_pfnGetAdaptersInfo ? g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo) : ERROR_NO_DATA;
+ if (dwRet == ERROR_BUFFER_OVERFLOW)
+ {
+ IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
+ if (pAdpInfoNew)
+ {
+ pAdpInfo = pAdpInfoNew;
+ dwRet = g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo);
+ }
+ }
+ else if (dwRet == ERROR_NO_DATA)
+ {
+ VGSvcVerbose(3, "VMInfo/Network: No network adapters available\n");
+
+ /* If no network adapters available / present in the
+ * system we pretend success to not bail out too early. */
+ dwRet = ERROR_SUCCESS;
+ }
+ if (dwRet != ERROR_SUCCESS)
+ {
+ RTMemFree(pAdpInfo);
+ VGSvcError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
+ return RTErrConvertFromWin32(dwRet);
+ }
+
+ SOCKET sd = g_pfnWSASocketA(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+ if (sd == SOCKET_ERROR) /* Socket invalid. */
+ {
+ int wsaErr = g_pfnWSAGetLastError();
+ /* Don't complain/bail out with an error if network stack is not up; can happen
+ * on NT4 due to start up when not connected shares dialogs pop up. */
+ if (WSAENETDOWN == wsaErr)
+ {
+ VGSvcVerbose(0, "VMInfo/Network: Network is not up yet.\n");
+ wsaErr = VINF_SUCCESS;
+ }
+ else
+ VGSvcError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
+ if (pAdpInfo)
+ RTMemFree(pAdpInfo);
+ return RTErrConvertFromWin32(wsaErr);
+ }
+
+ INTERFACE_INFO aInterfaces[20] = {0};
+ DWORD cbReturned = 0;
+# ifdef RT_ARCH_x86
+ /* Workaround for uninitialized variable used in memcpy in GetTcpipInterfaceList
+ (NT4SP1 at least). It seems to be happy enough with garbages, no failure
+ returns so far, so we just need to prevent it from crashing by filling the
+ stack with valid pointer values prior to the API call. */
+ _asm
+ {
+ mov edx, edi
+ lea eax, aInterfaces
+ mov [esp - 0x1000], eax
+ mov [esp - 0x2000], eax
+ mov ecx, 0x2000/4 - 1
+ cld
+ lea edi, [esp - 0x2000]
+ rep stosd
+ mov edi, edx
+ }
+# endif
+ int rc = g_pfnWSAIoctl(sd,
+ SIO_GET_INTERFACE_LIST,
+ NULL, /* pvInBuffer */
+ 0, /* cbInBuffer */
+ &aInterfaces[0], /* pvOutBuffer */
+ sizeof(aInterfaces), /* cbOutBuffer */
+ &cbReturned,
+ NULL, /* pOverlapped */
+ NULL); /* pCompletionRoutine */
+ if (rc == SOCKET_ERROR)
+ {
+ VGSvcError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", g_pfnWSAGetLastError());
+ if (pAdpInfo)
+ RTMemFree(pAdpInfo);
+ g_pfnclosesocket(sd);
+ return RTErrConvertFromWin32(g_pfnWSAGetLastError());
+ }
+ g_pfnclosesocket(sd);
+ int cIfacesSystem = cbReturned / sizeof(INTERFACE_INFO);
+
+ /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
+ for (int i = 0; i < cIfacesSystem; ++i)
+ {
+ sockaddr_in *pAddress;
+ u_long nFlags = 0;
+ if (aInterfaces[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
+ continue;
+ nFlags = aInterfaces[i].iiFlags;
+ pAddress = (sockaddr_in *)&(aInterfaces[i].iiAddress);
+ Assert(pAddress);
+ char szIp[32];
+ RTStrPrintf(szIp, sizeof(szIp), "%s", g_pfninet_ntoa(pAddress->sin_addr));
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
+
+ pAddress = (sockaddr_in *) & (aInterfaces[i].iiBroadcastAddress);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
+
+ pAddress = (sockaddr_in *)&(aInterfaces[i].iiNetmask);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
+
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
+
+ if (pAdpInfo)
+ {
+ IP_ADAPTER_INFO *pAdp;
+ for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
+ if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
+ break;
+
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
+ if (pAdp)
+ {
+ char szMac[32];
+ RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
+ pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
+ pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
+ }
+ else
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
+ }
+
+ cIfsReported++;
+ }
+ if (pAdpInfo)
+ RTMemFree(pAdpInfo);
+
+#elif defined(RT_OS_HAIKU)
+ /** @todo Haiku: implement network info. retreival */
+ return VERR_NOT_IMPLEMENTED;
+
+#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD)
+ struct ifaddrs *pIfHead = NULL;
+
+ /* Get all available interfaces */
+ int rc = getifaddrs(&pIfHead);
+ if (rc < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
+ return rc;
+ }
+
+ /* Loop through all interfaces and set the data. */
+ for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
+ {
+ /*
+ * Only AF_INET and no loopback interfaces
+ */
+ /** @todo IPv6 interfaces */
+ if ( pIfCurr->ifa_addr->sa_family == AF_INET
+ && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
+ {
+ char szInetAddr[NI_MAXHOST];
+
+ memset(szInetAddr, 0, NI_MAXHOST);
+ getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
+ szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
+
+ memset(szInetAddr, 0, NI_MAXHOST);
+ getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
+ szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
+
+ memset(szInetAddr, 0, NI_MAXHOST);
+ getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
+ szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
+
+ /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
+ for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
+ {
+ if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
+ && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
+ {
+ char szMac[32];
+ uint8_t *pu8Mac = NULL;
+ struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
+
+ AssertPtr(pLinkAddress);
+ pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
+ RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
+ pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
+ break;
+ }
+ }
+
+ RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
+
+ cIfsReported++;
+ }
+ }
+
+ /* Free allocated resources. */
+ freeifaddrs(pIfHead);
+
+#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
+ /*
+ * Use SIOCGIFCONF to get a list of interface/protocol configurations.
+ *
+ * See "UNIX Network Programming Volume 1" by W. R. Stevens, section 17.6
+ * for details on this ioctl.
+ */
+ int sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
+ return rc;
+ }
+
+ /* Call SIOCGIFCONF with the right sized buffer (remember the size). */
+ static int s_cbBuf = 256; // 1024
+ int cbBuf = s_cbBuf;
+ char *pchBuf;
+ struct ifconf IfConf;
+ int rc = VINF_SUCCESS;
+ for (;;)
+ {
+ pchBuf = (char *)RTMemTmpAllocZ(cbBuf);
+ if (!pchBuf)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ break;
+ }
+
+ IfConf.ifc_len = cbBuf;
+ IfConf.ifc_buf = pchBuf;
+ if (ioctl(sd, SIOCGIFCONF, &IfConf) >= 0)
+ {
+ /* Hard to anticipate how space an address might possibly take, so
+ making some generous assumptions here to avoid performing the
+ query twice with different buffer sizes. */
+ if (IfConf.ifc_len + 128 < cbBuf)
+ break;
+ }
+ else if (errno != EOVERFLOW)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /* grow the buffer */
+ s_cbBuf = cbBuf *= 2;
+ RTMemFree(pchBuf);
+ }
+ if (RT_FAILURE(rc))
+ {
+ close(sd);
+ RTMemTmpFree(pchBuf);
+ VGSvcError("VMInfo/Network: Error doing SIOCGIFCONF (cbBuf=%d): %Rrc\n", cbBuf, rc);
+ return rc;
+ }
+
+ /*
+ * Iterate the interface/protocol configurations.
+ *
+ * Note! The current code naively assumes one IPv4 address per interface.
+ * This means that guest assigning more than one address to an
+ * interface will get multiple entries for one physical interface.
+ */
+# ifdef RT_OS_OS2
+ struct ifreq *pPrevLinkAddr = NULL;
+# endif
+ struct ifreq *pCur = IfConf.ifc_req;
+ size_t cbLeft = IfConf.ifc_len;
+ while (cbLeft >= sizeof(*pCur))
+ {
+# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
+ /* These two do not provide the sa_len member but only support address
+ * families which do not need extra bytes on the end. */
+# define SA_LEN(pAddr) sizeof(struct sockaddr)
+# elif !defined(SA_LEN)
+# define SA_LEN(pAddr) (pAddr)->sa_len
+# endif
+ /* Figure the size of the current request. */
+ size_t cbCur = RT_UOFFSETOF(struct ifreq, ifr_addr)
+ + SA_LEN(&pCur->ifr_addr);
+ cbCur = RT_MAX(cbCur, sizeof(struct ifreq));
+# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
+ Assert(pCur->ifr_addr.sa_family == AF_INET);
+# endif
+ AssertBreak(cbCur <= cbLeft);
+
+# ifdef RT_OS_OS2
+ /* On OS/2 we get the MAC address in the AF_LINK that the BSD 4.4 stack
+ emits. We boldly ASSUME these always comes first. */
+ if ( pCur->ifr_addr.sa_family == AF_LINK
+ && ((struct sockaddr_dl *)&pCur->ifr_addr)->sdl_alen == 6)
+ pPrevLinkAddr = pCur;
+# endif
+
+ /* Skip it if it's not the kind of address we're looking for. */
+ struct ifreq IfReqTmp;
+ bool fIfUp = false;
+ bool fSkip = false;
+ if (pCur->ifr_addr.sa_family != AF_INET)
+ fSkip = true;
+ else
+ {
+ /* Get the interface flags so we can detect loopback and check if it's up. */
+ IfReqTmp = *pCur;
+ if (ioctl(sd, SIOCGIFFLAGS, &IfReqTmp) < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS,%s) on socket: Error %Rrc\n", pCur->ifr_name, rc);
+ break;
+ }
+ fIfUp = !!(IfReqTmp.ifr_flags & IFF_UP);
+ if (IfReqTmp.ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
+ fSkip = true;
+ }
+ if (!fSkip)
+ {
+ size_t offSubProp = RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32", cIfsReported);
+
+ sockaddr_in *pAddress = (sockaddr_in *)&pCur->ifr_addr;
+ strcpy(&szPropPath[offSubProp], "/V4/IP");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
+
+ /* Get the broadcast address. */
+ IfReqTmp = *pCur;
+ if (ioctl(sd, SIOCGIFBRDADDR, &IfReqTmp) < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
+ break;
+ }
+ pAddress = (sockaddr_in *)&IfReqTmp.ifr_broadaddr;
+ strcpy(&szPropPath[offSubProp], "/V4/Broadcast");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
+
+ /* Get the net mask. */
+ IfReqTmp = *pCur;
+ if (ioctl(sd, SIOCGIFNETMASK, &IfReqTmp) < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
+ break;
+ }
+# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
+ pAddress = (sockaddr_in *)&IfReqTmp.ifr_addr;
+# else
+ pAddress = (sockaddr_in *)&IfReqTmp.ifr_netmask;
+# endif
+ strcpy(&szPropPath[offSubProp], "/V4/Netmask");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
+
+# if defined(RT_OS_SOLARIS)
+ /*
+ * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
+ * We might fail if the interface has not been assigned an IP address.
+ * That doesn't matter; as long as it's plumbed we can pick it up.
+ * But, if it has not acquired an IP address we cannot obtain it's MAC
+ * address this way, so we just use all zeros there.
+ */
+ RTMAC IfMac;
+ struct lifreq IfReq;
+ RT_ZERO(IfReq);
+ AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(pCur->ifr_name));
+ strncpy(IfReq.lifr_name, pCur->ifr_name, sizeof(pCur->ifr_name));
+ if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
+ {
+ struct arpreq ArpReq;
+ RT_ZERO(ArpReq);
+ memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
+
+ if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
+ memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
+ break;
+ }
+ }
+ else
+ {
+ VGSvcVerbose(2, "VMInfo/Network: Interface '%s' has no assigned IP address, skipping ...\n", pCur->ifr_name);
+ continue;
+ }
+# elif defined(RT_OS_OS2)
+ RTMAC IfMac;
+ if ( pPrevLinkAddr
+ && strncmp(pCur->ifr_name, pPrevLinkAddr->ifr_name, sizeof(pCur->ifr_name)) == 0)
+ {
+ struct sockaddr_dl *pDlAddr = (struct sockaddr_dl *)&pPrevLinkAddr->ifr_addr;
+ IfMac = *(PRTMAC)&pDlAddr->sdl_data[pDlAddr->sdl_nlen];
+ }
+ else
+ RT_ZERO(IfMac);
+#else
+ if (ioctl(sd, SIOCGIFHWADDR, &IfReqTmp) < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
+ break;
+ }
+ RTMAC IfMac = *(PRTMAC)&IfReqTmp.ifr_hwaddr.sa_data[0];
+# endif
+ strcpy(&szPropPath[offSubProp], "/MAC");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%02X%02X%02X%02X%02X%02X",
+ IfMac.au8[0], IfMac.au8[1], IfMac.au8[2], IfMac.au8[3], IfMac.au8[4], IfMac.au8[5]);
+
+ strcpy(&szPropPath[offSubProp], "/Status");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
+
+ /* The name. */
+ int rc2 = RTStrValidateEncodingEx(pCur->ifr_name, sizeof(pCur->ifr_name), 0);
+ if (RT_SUCCESS(rc2))
+ {
+ strcpy(&szPropPath[offSubProp], "/Name");
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%.*s", sizeof(pCur->ifr_name), pCur->ifr_name);
+ }
+
+ cIfsReported++;
+ }
+
+ /*
+ * Next interface/protocol configuration.
+ */
+ pCur = (struct ifreq *)((uintptr_t)pCur + cbCur);
+ cbLeft -= cbCur;
+ }
+
+ RTMemTmpFree(pchBuf);
+ close(sd);
+ if (RT_FAILURE(rc))
+ VGSvcError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfsReported, rc);
+
+#endif /* !RT_OS_WINDOWS */
+
+#if 0 /* Zapping not enabled yet, needs more testing first. */
+ /*
+ * Zap all stale network interface data if the former (saved) network ifaces count
+ * is bigger than the current one.
+ */
+
+ /* Get former count. */
+ uint32_t cIfsReportedOld;
+ rc = VGSvcReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfsReportedOld,
+ 0 /* Min */, UINT32_MAX /* Max */);
+ if ( RT_SUCCESS(rc)
+ && cIfsReportedOld > cIfsReported) /* Are some ifaces not around anymore? */
+ {
+ VGSvcVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n",
+ cIfsReportedOld, cIfsReported);
+
+ uint32_t uIfaceDeleteIdx = cIfsReported;
+ do
+ {
+ VGSvcVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
+ rc = VGSvcPropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++);
+ } while (RT_SUCCESS(rc));
+ }
+ else if ( RT_FAILURE(rc)
+ && rc != VERR_NOT_FOUND)
+ {
+ VGSvcError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
+ }
+#endif
+
+ /*
+ * This property is a beacon which is _always_ written, even if the network configuration
+ * does not change. If this property is missing, the host assumes that all other GuestInfo
+ * properties are no longer valid.
+ */
+ VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32", cIfsReported);
+
+ /* Don't fail here; just report everything we got. */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vbsvcVMInfoWorker(bool volatile *pfShutdown)
+{
+ int rc;
+
+ /*
+ * Tell the control thread that it can continue
+ * spawning services.
+ */
+ RTThreadUserSignal(RTThreadSelf());
+
+#ifdef RT_OS_WINDOWS
+ /* Required for network information (must be called per thread). */
+ if (g_pfnWSAStartup)
+ {
+ WSADATA wsaData;
+ RT_ZERO(wsaData);
+ if (g_pfnWSAStartup(MAKEWORD(2, 2), &wsaData))
+ VGSvcError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(g_pfnWSAGetLastError()));
+ }
+#endif
+
+ /*
+ * Write the fixed properties first.
+ */
+ vgsvcVMInfoWriteFixedProperties();
+
+ /*
+ * Now enter the loop retrieving runtime data continuously.
+ */
+ for (;;)
+ {
+ rc = vgsvcVMInfoWriteUsers();
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = vgsvcVMInfoWriteNetwork();
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Whether to wait for event semaphore or not. */
+ bool fWait = true;
+
+ /* Check for location awareness. This most likely only
+ * works with VBox (latest) 4.1 and up. */
+
+ /* Check for new connection. */
+ char *pszLAClientID = NULL;
+ int rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */,
+ &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */);
+ if (RT_SUCCESS(rc2))
+ {
+ AssertPtr(pszLAClientID);
+ if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */
+ {
+ uint32_t uLAClientID = RTStrToInt32(pszLAClientID);
+ uint64_t uLAClientAttachedTS;
+
+ /* Peek at "Attach" value to figure out if hotdesking happened. */
+ char *pszAttach = NULL;
+ rc2 = vgsvcGetLAClientValue(uLAClientID, "Attach", &pszAttach,
+ &uLAClientAttachedTS);
+
+ if ( RT_SUCCESS(rc2)
+ && ( !g_LAClientAttachedTS
+ || (g_LAClientAttachedTS != uLAClientAttachedTS)))
+ {
+ vgsvcFreeLAClientInfo(&g_LAClientInfo);
+
+ /* Note: There is a race between setting the guest properties by the host and getting them by
+ * the guest. */
+ rc2 = vgsvcGetLAClientInfo(uLAClientID, &g_LAClientInfo);
+ if (RT_SUCCESS(rc2))
+ {
+ VGSvcVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n",
+ /* If g_LAClientAttachedTS is 0 this means there already was an active
+ * hotdesk session when VBoxService started. */
+ !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected",
+ uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain);
+
+ g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS;
+
+ /* Don't wait for event semaphore below anymore because we now know that the client
+ * changed. This means we need to iterate all VM information again immediately. */
+ fWait = false;
+ }
+ else
+ {
+ static int s_iBitchedAboutLAClientInfo = 0;
+ if (s_iBitchedAboutLAClientInfo < 10)
+ {
+ s_iBitchedAboutLAClientInfo++;
+ VGSvcError("Error getting active location awareness client info, rc=%Rrc\n", rc2);
+ }
+ }
+ }
+ else if (RT_FAILURE(rc2))
+ VGSvcError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n", uLAClientID, rc2);
+ if (pszAttach)
+ RTStrFree(pszAttach);
+ }
+ else
+ {
+ VGSvcVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n");
+ vgsvcFreeLAClientInfo(&g_LAClientInfo);
+ }
+
+ RTStrFree(pszLAClientID);
+ }
+ else
+ {
+ static int s_iBitchedAboutLAClient = 0;
+ if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */
+ && s_iBitchedAboutLAClient < 3)
+ {
+ s_iBitchedAboutLAClient++;
+ VGSvcError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2);
+ }
+ }
+
+ VGSvcVerbose(3, "VRDP: Handling location awareness done\n");
+
+ /*
+ * Flush all properties if we were restored.
+ */
+ uint64_t idNewSession = g_idVMInfoSession;
+ VbglR3GetSessionId(&idNewSession);
+ if (idNewSession != g_idVMInfoSession)
+ {
+ VGSvcVerbose(3, "The VM session ID changed, flushing all properties\n");
+ vgsvcVMInfoWriteFixedProperties();
+ VGSvcPropCacheFlush(&g_VMInfoPropCache);
+ g_idVMInfoSession = idNewSession;
+ }
+
+ /*
+ * Block for a while.
+ *
+ * The event semaphore takes care of ignoring interruptions and it
+ * allows us to implement service wakeup later.
+ */
+ if (*pfShutdown)
+ break;
+ if (fWait)
+ rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
+ if (*pfShutdown)
+ break;
+ if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
+ {
+ VGSvcError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
+ rc = rc2;
+ break;
+ }
+ else if (RT_LIKELY(RT_SUCCESS(rc2)))
+ {
+ /* Reset event semaphore if it got triggered. */
+ rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
+ if (RT_FAILURE(rc2))
+ rc2 = VGSvcError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
+ }
+ }
+
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSACleanup)
+ g_pfnWSACleanup();
+#endif
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vbsvcVMInfoStop(void)
+{
+ RTSemEventMultiSignal(g_hVMInfoEvent);
+}
+
+
+/**
+ * @interface_method_impl{VBOXSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(void) vbsvcVMInfoTerm(void)
+{
+ if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
+ {
+ /** @todo temporary solution: Zap all values which are not valid
+ * anymore when VM goes down (reboot/shutdown ). Needs to
+ * be replaced with "temporary properties" later.
+ *
+ * One idea is to introduce a (HGCM-)session guest property
+ * flag meaning that a guest property is only valid as long
+ * as the HGCM session isn't closed (e.g. guest application
+ * terminates). [don't remove till implemented]
+ */
+ /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
+ * since it remembers what we've written. */
+ /* Delete the "../Net" branch. */
+ const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
+ int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
+
+ /* Destroy LA client info. */
+ vgsvcFreeLAClientInfo(&g_LAClientInfo);
+
+ /* Destroy property cache. */
+ VGSvcPropCacheDestroy(&g_VMInfoPropCache);
+
+ /* Disconnect from guest properties service. */
+ rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
+ if (RT_FAILURE(rc))
+ VGSvcError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
+ g_uVMInfoGuestPropSvcClientID = 0;
+
+ RTSemEventMultiDestroy(g_hVMInfoEvent);
+ g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
+ }
+}
+
+
+/**
+ * The 'vminfo' service description.
+ */
+VBOXSERVICE g_VMInfo =
+{
+ /* pszName. */
+ "vminfo",
+ /* pszDescription. */
+ "Virtual Machine Information",
+ /* pszUsage. */
+ " [--vminfo-interval <ms>] [--vminfo-user-idle-threshold <ms>]"
+ ,
+ /* pszOptions. */
+ " --vminfo-interval Specifies the interval at which to retrieve the\n"
+ " VM information. The default is 10000 ms.\n"
+ " --vminfo-user-idle-threshold <ms>\n"
+ " Specifies the user idle threshold (in ms) for\n"
+ " considering a guest user as being idle. The default\n"
+ " is 5000 (5 seconds).\n"
+ ,
+ /* methods */
+ vbsvcVMInfoPreInit,
+ vbsvcVMInfoOption,
+ vbsvcVMInfoInit,
+ vbsvcVMInfoWorker,
+ vbsvcVMInfoStop,
+ vbsvcVMInfoTerm
+};
+
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h
new file mode 100644
index 00000000..45922c78
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h
@@ -0,0 +1,32 @@
+/* $Id: VBoxServiceVMInfo.h $ */
+/** @file
+ * VBoxServiceVMInfo.h - Internal VM info definitions.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxService_VBoxServiceVMInfo_h
+#define GA_INCLUDED_SRC_common_VBoxService_VBoxServiceVMInfo_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+extern int VGSvcUserUpdateF(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain,
+ const char *pszKey, const char *pszValueFormat, ...);
+
+
+extern uint32_t g_uVMInfoUserIdleThresholdMS;
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxService_VBoxServiceVMInfo_h */
+
diff --git a/src/VBox/Additions/common/VBoxService/testcase/Makefile.kmk b/src/VBox/Additions/common/VBoxService/testcase/Makefile.kmk
new file mode 100644
index 00000000..1161802b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/testcase/Makefile.kmk
@@ -0,0 +1,34 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxServicec test cases.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Target lists.
+#
+PROGRAMS.win += tstUserInfo
+
+#
+# tstUserInfo
+#
+tstUserInfo_TEMPLATE = VBoxGuestR3Exe
+tstUserInfo_SOURCES = \
+ tstUserInfo.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp b/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp
new file mode 100644
index 00000000..452cf898
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp
@@ -0,0 +1,76 @@
+/* $Id: tstUserInfo.cpp $ */
+/** @file
+ * Test case for correct user environment.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <iprt/win/shlobj.h>
+#endif
+
+#include <iprt/initterm.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <VBox/VBoxGuestLib.h>
+
+
+int main()
+{
+ /*
+ * Init globals and such.
+ */
+ RTR3InitExeNoArguments(0);
+
+ int rc = VbglR3Init();
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("VbglR3Init failed with rc=%Rrc.\n", rc);
+ return -1;
+ }
+#ifdef RT_OS_WINDOWS
+ WCHAR wszPath[MAX_PATH];
+ HRESULT hRes = SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, wszPath);
+
+ if (SUCCEEDED(hRes))
+ {
+ RTPrintf("SHGetFolderPathW (CSIDL_APPDATA) = %ls\n", wszPath);
+ hRes = SHGetFolderPathW(0, CSIDL_PERSONAL, 0, 0, wszPath);
+ if (SUCCEEDED(hRes))
+ {
+ RTPrintf("SHGetFolderPathW (CSIDL_PERSONAL) = %ls\n", wszPath);
+ }
+ else
+ RTPrintf("SHGetFolderPathW (CSIDL_PERSONAL) returned error: 0x%x\n", hRes);
+ }
+ else
+ RTPrintf("SHGetFolderPathW (CSIDL_APPDATA) returned error: 0x%x\n", hRes);
+
+ if (FAILED(hRes))
+ rc = RTErrConvertFromWin32(hRes);
+
+ /* Dump env bits. */
+ RTPrintf("Environment:\n\n");
+ RTPrintf("APPDATA = %s\n", getenv("APPDATA"));
+#endif
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
diff --git a/src/VBox/Additions/common/VBoxVideo/.scm-settings b/src/VBox/Additions/common/VBoxVideo/.scm-settings
new file mode 100644
index 00000000..765754d4
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/.scm-settings
@@ -0,0 +1,33 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for common/VBoxVideo
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# 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 graphics stuff is using MIT to encourage kernel upstreaming.
+--license-mit
+
diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp
new file mode 100644
index 00000000..5cc7d6a0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp
@@ -0,0 +1,300 @@
+/* $Id: HGSMIBase.cpp $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI guest-to-host communication.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+#include <HGSMIBase.h>
+#include <VBoxVideoIPRT.h>
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+#include <HGSMIChannels.h>
+#include <HGSMIChSetup.h>
+
+/** Detect whether HGSMI is supported by the host. */
+DECLHIDDEN(bool) VBoxHGSMIIsSupported(void)
+{
+ uint16_t DispiId;
+
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI);
+
+ DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+
+ return (DispiId == VBE_DISPI_ID_HGSMI);
+}
+
+
+/**
+ * Inform the host of the location of the host flags in VRAM via an HGSMI command.
+ * @returns IPRT status value.
+ * @returns VERR_NOT_IMPLEMENTED if the host does not support the command.
+ * @returns VERR_NO_MEMORY if a heap allocation fails.
+ * @param pCtx the context of the guest heap to use.
+ * @param offLocation the offset chosen for the flags withing guest VRAM.
+ */
+DECLHIDDEN(int) VBoxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx, HGSMIOFFSET offLocation)
+{
+
+ /* Allocate the IO buffer. */
+ HGSMIBUFFERLOCATION RT_UNTRUSTED_VOLATILE_HOST *p =
+ (HGSMIBUFFERLOCATION RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_HGSMI,
+ HGSMI_CC_HOST_FLAGS_LOCATION);
+ if (!p)
+ return VERR_NO_MEMORY;
+
+ /* Prepare data to be sent to the host. */
+ p->offLocation = offLocation;
+ p->cbLocation = sizeof(HGSMIHOSTFLAGS);
+ /* No need to check that the buffer is valid as we have just allocated it. */
+ VBoxHGSMIBufferSubmit(pCtx, p);
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Notify the host of HGSMI-related guest capabilities via an HGSMI command.
+ * @returns IPRT status value.
+ * @returns VERR_NOT_IMPLEMENTED if the host does not support the command.
+ * @returns VERR_NO_MEMORY if a heap allocation fails.
+ * @param pCtx the context of the guest heap to use.
+ * @param fCaps the capabilities to report, see VBVACAPS.
+ */
+DECLHIDDEN(int) VBoxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fCaps)
+{
+
+ /* Allocate the IO buffer. */
+ VBVACAPS RT_UNTRUSTED_VOLATILE_HOST *p =
+ (VBVACAPS RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, VBVA_INFO_CAPS);
+
+ if (!p)
+ return VERR_NO_MEMORY;
+
+ /* Prepare data to be sent to the host. */
+ p->rc = VERR_NOT_IMPLEMENTED;
+ p->fCaps = fCaps;
+ /* No need to check that the buffer is valid as we have just allocated it. */
+ VBoxHGSMIBufferSubmit(pCtx, p);
+
+ AssertRC(p->rc);
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ return p->rc;
+}
+
+
+/**
+ * Get the information needed to map the basic communication structures in
+ * device memory into our address space. All pointer parameters are optional.
+ *
+ * @param cbVRAM how much video RAM is allocated to the device
+ * @param poffVRAMBaseMapping where to save the offset from the start of the
+ * device VRAM of the whole area to map
+ * @param pcbMapping where to save the mapping size
+ * @param poffGuestHeapMemory where to save the offset into the mapped area
+ * of the guest heap backing memory
+ * @param pcbGuestHeapMemory where to save the size of the guest heap
+ * backing memory
+ * @param poffHostFlags where to save the offset into the mapped area
+ * of the host flags
+ */
+DECLHIDDEN(void) VBoxHGSMIGetBaseMappingInfo(uint32_t cbVRAM,
+ uint32_t *poffVRAMBaseMapping,
+ uint32_t *pcbMapping,
+ uint32_t *poffGuestHeapMemory,
+ uint32_t *pcbGuestHeapMemory,
+ uint32_t *poffHostFlags)
+{
+ AssertPtrNullReturnVoid(poffVRAMBaseMapping);
+ AssertPtrNullReturnVoid(pcbMapping);
+ AssertPtrNullReturnVoid(poffGuestHeapMemory);
+ AssertPtrNullReturnVoid(pcbGuestHeapMemory);
+ AssertPtrNullReturnVoid(poffHostFlags);
+ if (poffVRAMBaseMapping)
+ *poffVRAMBaseMapping = cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE;
+ if (pcbMapping)
+ *pcbMapping = VBVA_ADAPTER_INFORMATION_SIZE;
+ if (poffGuestHeapMemory)
+ *poffGuestHeapMemory = 0;
+ if (pcbGuestHeapMemory)
+ *pcbGuestHeapMemory = VBVA_ADAPTER_INFORMATION_SIZE
+ - sizeof(HGSMIHOSTFLAGS);
+ if (poffHostFlags)
+ *poffHostFlags = VBVA_ADAPTER_INFORMATION_SIZE
+ - sizeof(HGSMIHOSTFLAGS);
+}
+
+/**
+ * Query the host for an HGSMI configuration parameter via an HGSMI command.
+ * @returns iprt status value
+ * @param pCtx the context containing the heap used
+ * @param u32Index the index of the parameter to query,
+ * @see VBVACONF32::u32Index
+ * @param pulValue where to store the value of the parameter on success
+ */
+DECLHIDDEN(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t u32Index, uint32_t *pulValue)
+{
+ VBVACONF32 *p;
+
+ /* Allocate the IO buffer. */
+ p = (VBVACONF32 *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, VBVA_QUERY_CONF32);
+ if (!p)
+ return VERR_NO_MEMORY;
+
+ /* Prepare data to be sent to the host. */
+ p->u32Index = u32Index;
+ p->u32Value = UINT32_MAX;
+ /* No need to check that the buffer is valid as we have just allocated it. */
+ VBoxHGSMIBufferSubmit(pCtx, p);
+ *pulValue = p->u32Value;
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Pass the host a new mouse pointer shape via an HGSMI command.
+ *
+ * @returns success or failure
+ * @param pCtx the context containing the heap to be used
+ * @param fFlags cursor flags, @see VMMDevReqMousePointer::fFlags
+ * @param cHotX horizontal position of the hot spot
+ * @param cHotY vertical position of the hot spot
+ * @param cWidth width in pixels of the cursor
+ * @param cHeight height in pixels of the cursor
+ * @param pPixels pixel data, @see VMMDevReqMousePointer for the format
+ * @param cbLength size in bytes of the pixel data
+ */
+DECLHIDDEN(int) VBoxHGSMIUpdatePointerShape(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fFlags,
+ uint32_t cHotX, uint32_t cHotY, uint32_t cWidth, uint32_t cHeight,
+ uint8_t *pPixels, uint32_t cbLength)
+{
+ VBVAMOUSEPOINTERSHAPE *p;
+ uint32_t cbPixels = 0;
+ int rc;
+
+ if (fFlags & VBOX_MOUSE_POINTER_SHAPE)
+ {
+ /*
+ * Size of the pointer data:
+ * sizeof (AND mask) + sizeof (XOR_MASK)
+ */
+ cbPixels = ((((cWidth + 7) / 8) * cHeight + 3) & ~3)
+ + cWidth * 4 * cHeight;
+ if (cbPixels > cbLength)
+ return VERR_INVALID_PARAMETER;
+ /*
+ * If shape is supplied, then always create the pointer visible.
+ * See comments in 'vboxUpdatePointerShape'
+ */
+ fFlags |= VBOX_MOUSE_POINTER_VISIBLE;
+ }
+ /* Allocate the IO buffer. */
+ p = (VBVAMOUSEPOINTERSHAPE *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p) + cbPixels, HGSMI_CH_VBVA,
+ VBVA_MOUSE_POINTER_SHAPE);
+ if (!p)
+ return VERR_NO_MEMORY;
+ /* Prepare data to be sent to the host. */
+ /* Will be updated by the host. */
+ p->i32Result = VINF_SUCCESS;
+ /* We have our custom flags in the field */
+ p->fu32Flags = fFlags;
+ p->u32HotX = cHotX;
+ p->u32HotY = cHotY;
+ p->u32Width = cWidth;
+ p->u32Height = cHeight;
+ if (cbPixels)
+ /* Copy the actual pointer data. */
+ memcpy (p->au8Data, pPixels, cbPixels);
+ /* No need to check that the buffer is valid as we have just allocated it. */
+ VBoxHGSMIBufferSubmit(pCtx, p);
+ rc = p->i32Result;
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ return rc;
+}
+
+
+/**
+ * Report the guest cursor position. The host may wish to use this information
+ * to re-position its own cursor (though this is currently unlikely). The
+ * current host cursor position is returned.
+ * @param pCtx The context containing the heap used.
+ * @param fReportPosition Are we reporting a position?
+ * @param x Guest cursor X position.
+ * @param y Guest cursor Y position.
+ * @param pxHost Host cursor X position is stored here. Optional.
+ * @param pyHost Host cursor Y position is stored here. Optional.
+ * @returns iprt status code.
+ * @returns VERR_NO_MEMORY HGSMI heap allocation failed.
+ */
+DECLHIDDEN(int) VBoxHGSMICursorPosition(PHGSMIGUESTCOMMANDCONTEXT pCtx, bool fReportPosition,
+ uint32_t x, uint32_t y, uint32_t *pxHost, uint32_t *pyHost)
+{
+ VBVACURSORPOSITION *p;
+
+ /* Allocate the IO buffer. */
+ p = (VBVACURSORPOSITION *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA,
+ VBVA_CURSOR_POSITION);
+ if (!p)
+ return VERR_NO_MEMORY;
+ /* Prepare data to be sent to the host. */
+ p->fReportPosition = fReportPosition;
+ p->x = x;
+ p->y = y;
+ /* No need to check that the buffer is valid as we have just allocated it. */
+ VBoxHGSMIBufferSubmit(pCtx, p);
+ if (pxHost)
+ *pxHost = p->x;
+ if (pyHost)
+ *pyHost = p->y;
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @todo Mouse pointer position to be read from VMMDev memory, address of the
+ * memory region can be queried from VMMDev via an IOCTL. This VMMDev memory
+ * region will contain host information which is needed by the guest.
+ *
+ * Reading will not cause a switch to the host.
+ *
+ * Have to take into account:
+ * * synchronization: host must write to the memory only from EMT,
+ * large structures must be read under flag, which tells the host
+ * that the guest is currently reading the memory (OWNER flag?).
+ * * guest writes: may be allocate a page for the host info and make
+ * the page readonly for the guest.
+ * * the information should be available only for additions drivers.
+ * * VMMDev additions driver will inform the host which version of the info
+ * it expects, host must support all versions.
+ */
diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIBuffers.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIBuffers.cpp
new file mode 100644
index 00000000..248e9d0b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/HGSMIBuffers.cpp
@@ -0,0 +1,124 @@
+/* $Id: HGSMIBuffers.cpp $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI buffer management.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+#include <VBoxVideoIPRT.h>
+
+/**
+ * Set up the HGSMI guest-to-host command context.
+ * @returns iprt status value
+ * @param pCtx the context to set up
+ * @param pvGuestHeapMemory a pointer to the mapped backing memory for
+ * the guest heap
+ * @param cbGuestHeapMemory the size of the backing memory area
+ * @param offVRAMGuestHeapMemory the offset of the memory pointed to by
+ * @a pvGuestHeapMemory within the video RAM
+ * @param pEnv HGSMI environment.
+ */
+DECLHIDDEN(int) VBoxHGSMISetupGuestContext(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ void *pvGuestHeapMemory,
+ uint32_t cbGuestHeapMemory,
+ uint32_t offVRAMGuestHeapMemory,
+ const HGSMIENV *pEnv)
+{
+ /** @todo should we be using a fixed ISA port value here? */
+ pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_GUEST;
+#ifdef VBOX_WDDM_MINIPORT
+ return VBoxSHGSMIInit(&pCtx->heapCtx, pvGuestHeapMemory,
+ cbGuestHeapMemory, offVRAMGuestHeapMemory, pEnv);
+#else
+ return HGSMIHeapSetup(&pCtx->heapCtx, pvGuestHeapMemory,
+ cbGuestHeapMemory, offVRAMGuestHeapMemory, pEnv);
+#endif
+}
+
+
+/**
+ * Allocate and initialise a command descriptor in the guest heap for a
+ * guest-to-host command.
+ *
+ * @returns pointer to the descriptor's command data buffer
+ * @param pCtx the context containing the heap to be used
+ * @param cbData the size of the command data to go into the descriptor
+ * @param u8Ch the HGSMI channel to be used, set to the descriptor
+ * @param u16Op the HGSMI command to be sent, set to the descriptor
+ */
+DECLHIDDEN(void RT_UNTRUSTED_VOLATILE_HOST *) VBoxHGSMIBufferAlloc(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ HGSMISIZE cbData,
+ uint8_t u8Ch,
+ uint16_t u16Op)
+{
+#ifdef VBOX_WDDM_MINIPORT
+ return VBoxSHGSMIHeapAlloc(&pCtx->heapCtx, cbData, u8Ch, u16Op);
+#else
+ return HGSMIHeapAlloc(&pCtx->heapCtx, cbData, u8Ch, u16Op);
+#endif
+}
+
+
+/**
+ * Free a descriptor allocated by @a VBoxHGSMIBufferAlloc.
+ *
+ * @param pCtx the context containing the heap used
+ * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc
+ */
+DECLHIDDEN(void) VBoxHGSMIBufferFree(PHGSMIGUESTCOMMANDCONTEXT pCtx, void RT_UNTRUSTED_VOLATILE_HOST *pvBuffer)
+{
+#ifdef VBOX_WDDM_MINIPORT
+ VBoxSHGSMIHeapFree(&pCtx->heapCtx, pvBuffer);
+#else
+ HGSMIHeapFree(&pCtx->heapCtx, pvBuffer);
+#endif
+}
+
+/**
+ * Submit a command descriptor allocated by @a VBoxHGSMIBufferAlloc.
+ *
+ * @param pCtx the context containing the heap used
+ * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc
+ */
+DECLHIDDEN(int) VBoxHGSMIBufferSubmit(PHGSMIGUESTCOMMANDCONTEXT pCtx, void RT_UNTRUSTED_VOLATILE_HOST *pvBuffer)
+{
+ /* Initialize the buffer and get the offset for port IO. */
+ HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset(HGSMIGUESTCMDHEAP_GET(&pCtx->heapCtx), pvBuffer);
+
+ Assert(offBuffer != HGSMIOFFSET_VOID);
+ if (offBuffer != HGSMIOFFSET_VOID)
+ {
+ /* Submit the buffer to the host. */
+ VBVO_PORT_WRITE_U32(pCtx->port, offBuffer);
+ /* Make the compiler aware that the host has changed memory. */
+ ASMCompilerBarrier();
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIHostCmd.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIHostCmd.cpp
new file mode 100644
index 00000000..38321eeb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/HGSMIHostCmd.cpp
@@ -0,0 +1,245 @@
+/* $Id: HGSMIHostCmd.cpp $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI host-to-guest communication.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+#include <VBoxVideoIPRT.h>
+#include <HGSMIHostCmd.h>
+
+/**
+ * Initialise the host context structure.
+ *
+ * @param pCtx the context structure to initialise
+ * @param pvBaseMapping where the basic HGSMI structures are mapped at
+ * @param offHostFlags the offset of the host flags into the basic HGSMI
+ * structures
+ * @param pvHostAreaMapping where the area for the host heap is mapped at
+ * @param offVRAMHostArea offset of the host heap area into VRAM
+ * @param cbHostArea size in bytes of the host heap area
+ */
+DECLHIDDEN(void) VBoxHGSMISetupHostContext(PHGSMIHOSTCOMMANDCONTEXT pCtx,
+ void *pvBaseMapping,
+ uint32_t offHostFlags,
+ void *pvHostAreaMapping,
+ uint32_t offVRAMHostArea,
+ uint32_t cbHostArea)
+{
+ uint8_t *pu8HostFlags = ((uint8_t *)pvBaseMapping) + offHostFlags;
+ pCtx->pfHostFlags = (HGSMIHOSTFLAGS *)pu8HostFlags;
+ /** @todo should we really be using a fixed ISA port value here? */
+ pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_HOST;
+ HGSMIAreaInitialize(&pCtx->areaCtx, pvHostAreaMapping, cbHostArea,
+ offVRAMHostArea);
+}
+
+
+/** Send completion notification to the host for the command located at offset
+ * @a offt into the host command buffer. */
+static void HGSMINotifyHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx, HGSMIOFFSET offt)
+{
+ VBVO_PORT_WRITE_U32(pCtx->port, offt);
+}
+
+
+/**
+ * Inform the host that a command has been handled.
+ *
+ * @param pCtx the context containing the heap to be used
+ * @param pvMem pointer into the heap as mapped in @a pCtx to the command to
+ * be completed
+ */
+DECLHIDDEN(void) VBoxHGSMIHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx, void RT_UNTRUSTED_VOLATILE_HOST *pvMem)
+{
+ HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_GUEST *pHdr = HGSMIBufferHeaderFromData(pvMem);
+ HGSMIOFFSET offMem = HGSMIPointerToOffset(&pCtx->areaCtx, pHdr);
+ Assert(offMem != HGSMIOFFSET_VOID);
+ if (offMem != HGSMIOFFSET_VOID)
+ HGSMINotifyHostCmdComplete(pCtx, offMem);
+}
+
+
+/** Submit an incoming host command to the appropriate handler. */
+static void hgsmiHostCmdProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx,
+ HGSMIOFFSET offBuffer)
+{
+ int rc = HGSMIBufferProcess(&pCtx->areaCtx, &pCtx->channels, offBuffer);
+ Assert(!RT_FAILURE(rc));
+ if(RT_FAILURE(rc))
+ {
+ /* failure means the command was not submitted to the handler for some reason
+ * it's our responsibility to notify its completion in this case */
+ HGSMINotifyHostCmdComplete(pCtx, offBuffer);
+ }
+ /* if the cmd succeeded it's responsibility of the callback to complete it */
+}
+
+/** Get the next command from the host. */
+static HGSMIOFFSET hgsmiGetHostBuffer(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ return VBVO_PORT_READ_U32(pCtx->port);
+}
+
+
+/** Get and handle the next command from the host. */
+static void hgsmiHostCommandQueryProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ HGSMIOFFSET offset = hgsmiGetHostBuffer(pCtx);
+ AssertReturnVoid(offset != HGSMIOFFSET_VOID);
+ hgsmiHostCmdProcess(pCtx, offset);
+}
+
+
+/** Drain the host command queue. */
+DECLHIDDEN(void) VBoxHGSMIProcessHostQueue(PHGSMIHOSTCOMMANDCONTEXT pCtx)
+{
+ while (pCtx->pfHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING)
+ {
+ if (!ASMAtomicCmpXchgBool(&pCtx->fHostCmdProcessing, true, false))
+ return;
+ hgsmiHostCommandQueryProcess(pCtx);
+ ASMAtomicWriteBool(&pCtx->fHostCmdProcessing, false);
+ }
+}
+
+
+/** Tell the host about the location of the area of VRAM set aside for the host
+ * heap. */
+static int vboxHGSMIReportHostArea(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t u32AreaOffset, uint32_t u32AreaSize)
+{
+ VBVAINFOHEAP *p;
+ int rc = VINF_SUCCESS;
+
+ /* Allocate the IO buffer. */
+ p = (VBVAINFOHEAP *)VBoxHGSMIBufferAlloc(pCtx,
+ sizeof (VBVAINFOHEAP), HGSMI_CH_VBVA,
+ VBVA_INFO_HEAP);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ p->u32HeapOffset = u32AreaOffset;
+ p->u32HeapSize = u32AreaSize;
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Get the information needed to map the area used by the host to send back
+ * requests.
+ *
+ * @param pCtx the context containing the heap to use
+ * @param cbVRAM how much video RAM is allocated to the device
+ * @param offVRAMBaseMapping the offset of the basic communication structures
+ * into the guest's VRAM
+ * @param poffVRAMHostArea where to store the offset into VRAM of the host
+ * heap area
+ * @param pcbHostArea where to store the size of the host heap area
+ */
+DECLHIDDEN(void) VBoxHGSMIGetHostAreaMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t cbVRAM,
+ uint32_t offVRAMBaseMapping,
+ uint32_t *poffVRAMHostArea,
+ uint32_t *pcbHostArea)
+{
+ uint32_t offVRAMHostArea = offVRAMBaseMapping, cbHostArea = 0;
+
+ AssertPtrReturnVoid(poffVRAMHostArea);
+ AssertPtrReturnVoid(pcbHostArea);
+ VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbHostArea);
+ if (cbHostArea != 0)
+ {
+ uint32_t cbHostAreaMaxSize = cbVRAM / 4;
+ /** @todo what is the idea of this? */
+ if (cbHostAreaMaxSize >= VBVA_ADAPTER_INFORMATION_SIZE)
+ {
+ cbHostAreaMaxSize -= VBVA_ADAPTER_INFORMATION_SIZE;
+ }
+ if (cbHostArea > cbHostAreaMaxSize)
+ {
+ cbHostArea = cbHostAreaMaxSize;
+ }
+ /* Round up to 4096 bytes. */
+ cbHostArea = (cbHostArea + 0xFFF) & ~0xFFF;
+ offVRAMHostArea = offVRAMBaseMapping - cbHostArea;
+ }
+
+ *pcbHostArea = cbHostArea;
+ *poffVRAMHostArea = offVRAMHostArea;
+ // LogFunc(("offVRAMHostArea = 0x%08X, cbHostArea = 0x%08X\n",
+ // offVRAMHostArea, cbHostArea));
+}
+
+
+/**
+ * Tell the host about the ways it can use to communicate back to us via an
+ * HGSMI command
+ *
+ * @returns iprt status value
+ * @param pCtx the context containing the heap to use
+ * @param offVRAMFlagsLocation where we wish the host to place its flags
+ * relative to the start of the VRAM
+ * @param fCaps additions HGSMI capabilities the guest
+ * supports
+ * @param offVRAMHostArea offset into VRAM of the host heap area
+ * @param cbHostArea size in bytes of the host heap area
+ */
+DECLHIDDEN(int) VBoxHGSMISendHostCtxInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ HGSMIOFFSET offVRAMFlagsLocation,
+ uint32_t fCaps,
+ uint32_t offVRAMHostArea,
+ uint32_t cbHostArea)
+{
+ // Log(("VBoxVideo::vboxSetupAdapterInfo\n"));
+
+ /* setup the flags first to ensure they are initialized by the time the
+ * host heap is ready */
+ int rc = VBoxHGSMIReportFlagsLocation(pCtx, offVRAMFlagsLocation);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc) && fCaps)
+ {
+ /* Inform about caps */
+ rc = VBoxHGSMISendCapsInfo(pCtx, fCaps);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS (rc))
+ {
+ /* Report the host heap location. */
+ rc = vboxHGSMIReportHostArea(pCtx, offVRAMHostArea, cbHostArea);
+ AssertRC(rc);
+ }
+ // Log(("VBoxVideo::vboxSetupAdapterInfo finished rc = %d\n", rc));
+ return rc;
+}
diff --git a/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp
new file mode 100644
index 00000000..0cbd7258
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp
@@ -0,0 +1,419 @@
+/* $Id: Modesetting.cpp $ */
+/** @file
+ * VirtualBox Video driver, common code - HGSMI initialisation and helper
+ * functions.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+#include <HGSMIChannels.h>
+
+#ifndef VBOX_GUESTR3XF86MOD
+# include <VBoxVideoIPRT.h>
+#endif
+
+/**
+ * Gets the count of virtual monitors attached to the guest via an HGSMI
+ * command
+ *
+ * @returns the right count on success or 1 on failure.
+ * @param pCtx the context containing the heap to use
+ */
+DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx)
+{
+ /* Query the configured number of displays. */
+ uint32_t cDisplays = 0;
+ VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
+ // LogFunc(("cDisplays = %d\n", cDisplays));
+ if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
+ /* Host reported some bad value. Continue in the 1 screen mode. */
+ cDisplays = 1;
+ return cDisplays;
+}
+
+
+/**
+ * Query whether the virtual hardware supports VBE_DISPI_ID_CFG
+ * and set the interface.
+ *
+ * @returns Whether the interface is supported.
+ */
+DECLHIDDEN(bool) VBoxVGACfgAvailable(void)
+{
+ uint16_t DispiId;
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_CFG);
+ DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ return (DispiId == VBE_DISPI_ID_CFG);
+}
+
+
+/**
+ * Query a configuration value from the virtual hardware which supports VBE_DISPI_ID_CFG.
+ * I.e. use this function only if VBoxVGACfgAvailable returns true.
+ *
+ * @returns Whether the value is supported.
+ * @param u16Id Identifier of the configuration value (VBE_DISPI_CFG_ID_*).
+ * @param pu32Value Where to store value from the host.
+ * @param u32DefValue What to assign to *pu32Value if the value is not supported.
+ */
+DECLHIDDEN(bool) VBoxVGACfgQuery(uint16_t u16Id, uint32_t *pu32Value, uint32_t u32DefValue)
+{
+ uint32_t u32;
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_CFG);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_CFG_MASK_SUPPORT | u16Id);
+ u32 = VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
+ if (u32)
+ {
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, u16Id);
+ *pu32Value = VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
+ return true;
+ }
+
+ *pu32Value = u32DefValue;
+ return false;
+}
+
+
+/**
+ * Returns the size of the video RAM in bytes.
+ *
+ * @returns the size
+ */
+DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void)
+{
+ /** @note A 32bit read on this port returns the VRAM size if interface is older than VBE_DISPI_ID_CFG. */
+ return VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
+}
+
+
+/**
+ * Check whether this hardware allows the display width to have non-multiple-
+ * of-eight values.
+ *
+ * @returns true if any width is allowed, false otherwise.
+ */
+DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void)
+{
+ unsigned DispiId;
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_ANYX);
+ DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ return (DispiId == VBE_DISPI_ID_ANYX);
+}
+
+
+/**
+ * Tell the host about how VRAM is divided up between each screen via an HGSMI
+ * command. It is acceptable to specifiy identical data for each screen if
+ * they share a single framebuffer.
+ *
+ * @returns iprt status code, either VERR_NO_MEMORY or the status returned by
+ * @a pfnFill
+ * @todo What was I thinking of with that callback function? It
+ * would be much simpler to just pass in a structure in normal
+ * memory and copy it.
+ * @param pCtx the context containing the heap to use
+ * @param u32Count the number of screens we are activating
+ * @param pfnFill a callback which initialises the VBVAINFOVIEW structures
+ * for all screens
+ * @param pvData context data for @a pfnFill
+ */
+DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t u32Count,
+ PFNHGSMIFILLVIEWINFO pfnFill,
+ void *pvData)
+{
+ int rc;
+ /* Issue the screen info command. */
+ VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_HOST *pInfo =
+ (VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count,
+ HGSMI_CH_VBVA, VBVA_INFO_VIEW);
+ if (pInfo)
+ {
+ rc = pfnFill(pvData, (VBVAINFOVIEW *)pInfo /* lazy bird */, u32Count);
+ if (RT_SUCCESS(rc))
+ VBoxHGSMIBufferSubmit(pCtx, pInfo);
+ VBoxHGSMIBufferFree(pCtx, pInfo);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Set a video mode using port registers. This must be done for the first
+ * screen before every HGSMI modeset and also works when HGSM is not enabled.
+ * @param cWidth the mode width
+ * @param cHeight the mode height
+ * @param cVirtWidth the mode pitch
+ * @param cBPP the colour depth of the mode
+ * @param fFlags flags for the mode. These will be or-ed with the
+ * default _ENABLED flag, so unless you are restoring
+ * a saved mode or have special requirements you can pass
+ * zero here.
+ * @param cx the horizontal panning offset
+ * @param cy the vertical panning offset
+ */
+DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight,
+ uint16_t cVirtWidth, uint16_t cBPP,
+ uint16_t fFlags, uint16_t cx,
+ uint16_t cy)
+{
+ /* set the mode characteristics */
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cWidth);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cHeight);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cVirtWidth);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cBPP);
+ /* enable the mode */
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, fFlags | VBE_DISPI_ENABLED);
+ /* Panning registers */
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_X_OFFSET);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cx);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_Y_OFFSET);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cy);
+ /** @todo read from the port to see if the mode switch was successful */
+}
+
+
+/**
+ * Get the video mode for the first screen using the port registers. All
+ * parameters are optional
+ * @returns true if the VBE mode returned is active, false if we are in VGA
+ * mode
+ * @note If anyone else needs additional register values just extend the
+ * function with additional parameters and fix any existing callers.
+ * @param pcWidth where to store the mode width
+ * @param pcHeight where to store the mode height
+ * @param pcVirtWidth where to store the mode pitch
+ * @param pcBPP where to store the colour depth of the mode
+ * @param pfFlags where to store the flags for the mode
+ */
+DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, uint16_t *pcHeight,
+ uint16_t *pcVirtWidth, uint16_t *pcBPP,
+ uint16_t *pfFlags)
+{
+ uint16_t fFlags;
+
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
+ fFlags = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ if (pcWidth)
+ {
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
+ *pcWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ }
+ if (pcHeight)
+ {
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
+ *pcHeight = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ }
+ if (pcVirtWidth)
+ {
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
+ *pcVirtWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ }
+ if (pcBPP)
+ {
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
+ *pcBPP = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
+ }
+ if (pfFlags)
+ *pfFlags = fFlags;
+ return RT_BOOL(fFlags & VBE_DISPI_ENABLED);
+}
+
+
+/**
+ * Disable our extended graphics mode and go back to VGA mode.
+ */
+DECLHIDDEN(void) VBoxVideoDisableVBE(void)
+{
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
+ VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 0);
+}
+
+
+/**
+ * Set a video mode via an HGSMI request. The views must have been
+ * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being
+ * set on the first display then it must be set first using registers.
+ * @param pCtx The context containing the heap to use.
+ * @param cDisplay the screen number
+ * @param cOriginX the horizontal displacement relative to the first screen
+ * @param cOriginY the vertical displacement relative to the first screen
+ * @param offStart the offset of the visible area of the framebuffer
+ * relative to the framebuffer start
+ * @param cbPitch the offset in bytes between the starts of two adjecent
+ * scan lines in video RAM
+ * @param cWidth the mode width
+ * @param cHeight the mode height
+ * @param cBPP the colour depth of the mode
+ * @param fFlags flags
+ */
+DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ uint32_t cDisplay,
+ int32_t cOriginX,
+ int32_t cOriginY,
+ uint32_t offStart,
+ uint32_t cbPitch,
+ uint32_t cWidth,
+ uint32_t cHeight,
+ uint16_t cBPP,
+ uint16_t fFlags)
+{
+ /* Issue the screen info command. */
+ VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_HOST *pScreen =
+ (VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOSCREEN),
+ HGSMI_CH_VBVA, VBVA_INFO_SCREEN);
+ if (pScreen != NULL)
+ {
+ pScreen->u32ViewIndex = cDisplay;
+ pScreen->i32OriginX = cOriginX;
+ pScreen->i32OriginY = cOriginY;
+ pScreen->u32StartOffset = offStart;
+ pScreen->u32LineSize = cbPitch;
+ pScreen->u32Width = cWidth;
+ pScreen->u32Height = cHeight;
+ pScreen->u16BitsPerPixel = cBPP;
+ pScreen->u16Flags = fFlags;
+
+ VBoxHGSMIBufferSubmit(pCtx, pScreen);
+
+ VBoxHGSMIBufferFree(pCtx, pScreen);
+ }
+ else
+ {
+ // LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+}
+
+
+/** Report the rectangle relative to which absolute pointer events should be
+ * expressed. This information remains valid until the next VBVA resize event
+ * for any screen, at which time it is reset to the bounding rectangle of all
+ * virtual screens.
+ * @param pCtx The context containing the heap to use.
+ * @param cOriginX Upper left X co-ordinate relative to the first screen.
+ * @param cOriginY Upper left Y co-ordinate relative to the first screen.
+ * @param cWidth Rectangle width.
+ * @param cHeight Rectangle height.
+ * @returns iprt status code.
+ * @returns VERR_NO_MEMORY HGSMI heap allocation failed.
+ */
+DECLHIDDEN(int) VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t cOriginX, int32_t cOriginY,
+ uint32_t cWidth, uint32_t cHeight)
+{
+ int rc;
+ VBVAREPORTINPUTMAPPING *p;
+ // Log(("%s: cOriginX=%d, cOriginY=%d, cWidth=%u, cHeight=%u\n", __PRETTY_FUNCTION__, (int)cOriginX, (int)cOriginX,
+ // (unsigned)cWidth, (unsigned)cHeight));
+
+ /* Allocate the IO buffer. */
+ p = (VBVAREPORTINPUTMAPPING *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAREPORTINPUTMAPPING), HGSMI_CH_VBVA,
+ VBVA_REPORT_INPUT_MAPPING);
+ if (p)
+ {
+ /* Prepare data to be sent to the host. */
+ p->x = cOriginX;
+ p->y = cOriginY;
+ p->cx = cWidth;
+ p->cy = cHeight;
+ rc = VBoxHGSMIBufferSubmit(pCtx, p);
+ /* Free the IO buffer. */
+ VBoxHGSMIBufferFree(pCtx, p);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ // LogFunc(("rc = %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Get most recent video mode hints.
+ * @param pCtx the context containing the heap to use
+ * @param cScreens the number of screens to query hints for, starting at 0.
+ * @param paHints array of VBVAMODEHINT structures for receiving the hints.
+ * @returns iprt status code
+ * @returns VERR_NO_MEMORY HGSMI heap allocation failed.
+ * @returns VERR_NOT_SUPPORTED Host does not support this command.
+ */
+DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx,
+ unsigned cScreens, VBVAMODEHINT *paHints)
+{
+ int rc;
+ VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_HOST *pQuery;
+
+ AssertPtrReturn(paHints, VERR_INVALID_POINTER);
+ pQuery = (VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx,
+ sizeof(VBVAQUERYMODEHINTS)
+ + cScreens * sizeof(VBVAMODEHINT),
+ HGSMI_CH_VBVA, VBVA_QUERY_MODE_HINTS);
+ if (pQuery != NULL)
+ {
+ pQuery->cHintsQueried = cScreens;
+ pQuery->cbHintStructureGuest = sizeof(VBVAMODEHINT);
+ pQuery->rc = VERR_NOT_SUPPORTED;
+
+ VBoxHGSMIBufferSubmit(pCtx, pQuery);
+ rc = pQuery->rc;
+ if (RT_SUCCESS(rc))
+ memcpy(paHints, (void *)(pQuery + 1), cScreens * sizeof(VBVAMODEHINT));
+
+ VBoxHGSMIBufferFree(pCtx, pQuery);
+ }
+ else
+ {
+ // LogFunc(("HGSMIHeapAlloc failed\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+/**
+ * Query the supported flags in VBVAINFOSCREEN::u16Flags.
+ *
+ * @returns The mask of VBVA_SCREEN_F_* flags or 0 if host does not support the request.
+ * @param pCtx the context containing the heap to use
+ */
+DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx)
+{
+ uint32_t u32Flags = 0;
+ int rc = VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &u32Flags);
+ // LogFunc(("u32Flags = 0x%x rc %Rrc\n", u32Flags, rc));
+ if (RT_FAILURE(rc) || u32Flags > UINT16_MAX)
+ u32Flags = 0;
+ return (uint16_t)u32Flags;
+}
diff --git a/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp
new file mode 100644
index 00000000..e05ac630
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp
@@ -0,0 +1,378 @@
+/* $Id: VBVABase.cpp $ */
+/** @file
+ * VirtualBox Video driver, common code - VBVA initialisation and helper
+ * functions.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoIPRT.h>
+#include <HGSMIChannels.h>
+
+/*
+ * There is a hardware ring buffer in the graphics device video RAM, formerly
+ * in the VBox VMMDev PCI memory space.
+ * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate.
+ * and vboxHwBufferEndUpdate.
+ *
+ * off32Free is writing position. off32Data is reading position.
+ * off32Free == off32Data means buffer is empty.
+ * There must be always gap between off32Data and off32Free when data
+ * are in the buffer.
+ * Guest only changes off32Free, host changes off32Data.
+ */
+
+/* Forward declarations of internal functions. */
+static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx);
+static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
+ uint32_t cb, uint32_t offset);
+static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *p, uint32_t cb);
+
+
+static bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx, PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, int32_t cScreen, bool fEnable)
+{
+ bool fRc = false;
+
+#if 0 /* All callers check this */
+ if (ppdev->bHGSMISupported)
+#endif
+ {
+ VBVAENABLE_EX RT_UNTRUSTED_VOLATILE_HOST *pEnable =
+ (VBVAENABLE_EX RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pHGSMICtx, sizeof(VBVAENABLE_EX),
+ HGSMI_CH_VBVA, VBVA_ENABLE);
+ if (pEnable != NULL)
+ {
+ pEnable->Base.u32Flags = fEnable ? VBVA_F_ENABLE : VBVA_F_DISABLE;
+ pEnable->Base.u32Offset = pCtx->offVRAMBuffer;
+ pEnable->Base.i32Result = VERR_NOT_SUPPORTED;
+ if (cScreen >= 0)
+ {
+ pEnable->Base.u32Flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET;
+ pEnable->u32ScreenId = cScreen;
+ }
+
+ VBoxHGSMIBufferSubmit(pHGSMICtx, pEnable);
+
+ if (fEnable)
+ fRc = RT_SUCCESS(pEnable->Base.i32Result);
+ else
+ fRc = true;
+
+ VBoxHGSMIBufferFree(pHGSMICtx, pEnable);
+ }
+ else
+ {
+ // LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+ }
+
+ return fRc;
+}
+
+/*
+ * Public hardware buffer methods.
+ */
+DECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ VBVABUFFER *pVBVA, int32_t cScreen)
+{
+ bool fRc = false;
+
+ // LogFlowFunc(("pVBVA %p\n", pVBVA));
+
+#if 0 /* All callers check this */
+ if (ppdev->bHGSMISupported)
+#endif
+ {
+ // LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA, pCtx->offVRAMBuffer));
+
+ pVBVA->hostFlags.u32HostEvents = 0;
+ pVBVA->hostFlags.u32SupportedOrders = 0;
+ pVBVA->off32Data = 0;
+ pVBVA->off32Free = 0;
+ memset(pVBVA->aRecords, 0, sizeof (pVBVA->aRecords));
+ pVBVA->indexRecordFirst = 0;
+ pVBVA->indexRecordFree = 0;
+ pVBVA->cbPartialWriteThreshold = 256;
+ pVBVA->cbData = pCtx->cbBuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data);
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+ pCtx->pVBVA = pVBVA;
+
+ fRc = vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, true);
+ }
+
+ if (!fRc)
+ {
+ VBoxVBVADisable(pCtx, pHGSMICtx, cScreen);
+ }
+
+ return fRc;
+}
+
+DECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ int32_t cScreen)
+{
+ // LogFlowFunc(("\n"));
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+ pCtx->pVBVA = NULL;
+
+ vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, false);
+}
+
+DECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx)
+{
+ bool fRc = false;
+
+ // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1));
+
+ if ( pCtx->pVBVA
+ && (pCtx->pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED))
+ {
+ uint32_t indexRecordNext;
+
+ Assert(!pCtx->fHwBufferOverflow);
+ Assert(pCtx->pRecord == NULL);
+
+ indexRecordNext = (pCtx->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS;
+
+ if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
+ {
+ /* All slots in the records queue are used. */
+ vboxHwBufferFlush (pHGSMICtx);
+ }
+
+ if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
+ {
+ /* Even after flush there is no place. Fail the request. */
+ // LogFunc(("no space in the queue of records!!! first %d, last %d\n",
+ // pCtx->pVBVA->indexRecordFirst, pCtx->pVBVA->indexRecordFree));
+ }
+ else
+ {
+ /* Initialize the record. */
+ VBVARECORD *pRecord = &pCtx->pVBVA->aRecords[pCtx->pVBVA->indexRecordFree];
+
+ pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
+
+ pCtx->pVBVA->indexRecordFree = indexRecordNext;
+
+ // LogFunc(("indexRecordNext = %d\n", indexRecordNext));
+
+ /* Remember which record we are using. */
+ pCtx->pRecord = pRecord;
+
+ fRc = true;
+ }
+ }
+
+ return fRc;
+}
+
+DECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx)
+{
+ VBVARECORD *pRecord;
+
+ // LogFunc(("\n"));
+
+ Assert(pCtx->pVBVA);
+
+ pRecord = pCtx->pRecord;
+ Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
+
+ /* Mark the record completed. */
+ pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
+
+ pCtx->fHwBufferOverflow = false;
+ pCtx->pRecord = NULL;
+}
+
+/*
+ * Private operations.
+ */
+static uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA)
+{
+ int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free;
+
+ return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff;
+}
+
+static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx)
+{
+ /* Issue the flush command. */
+ VBVAFLUSH RT_UNTRUSTED_VOLATILE_HOST *pFlush =
+ (VBVAFLUSH RT_UNTRUSTED_VOLATILE_HOST * )VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAFLUSH), HGSMI_CH_VBVA, VBVA_FLUSH);
+ if (pFlush != NULL)
+ {
+ pFlush->u32Reserved = 0;
+
+ VBoxHGSMIBufferSubmit(pCtx, pFlush);
+
+ VBoxHGSMIBufferFree(pCtx, pFlush);
+ }
+ else
+ {
+ // LogFunc(("HGSMIHeapAlloc failed\n"));
+ }
+}
+
+static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
+ uint32_t cb, uint32_t offset)
+{
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+ uint32_t u32BytesTillBoundary = pVBVA->cbData - offset;
+ uint8_t *dst = &pVBVA->au8Data[offset];
+ int32_t i32Diff = cb - u32BytesTillBoundary;
+
+ if (i32Diff <= 0)
+ {
+ /* Chunk will not cross buffer boundary. */
+ memcpy (dst, p, cb);
+ }
+ else
+ {
+ /* Chunk crosses buffer boundary. */
+ memcpy (dst, p, u32BytesTillBoundary);
+ memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
+ }
+}
+
+static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *p, uint32_t cb)
+{
+ VBVARECORD *pRecord;
+ uint32_t cbHwBufferAvail;
+
+ uint32_t cbWritten = 0;
+
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+ Assert(pVBVA);
+
+ if (!pVBVA || pCtx->fHwBufferOverflow)
+ {
+ return false;
+ }
+
+ Assert(pVBVA->indexRecordFirst != pVBVA->indexRecordFree);
+
+ pRecord = pCtx->pRecord;
+ Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
+
+ // LogFunc(("%d\n", cb));
+
+ cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
+
+ while (cb > 0)
+ {
+ uint32_t cbChunk = cb;
+
+ // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
+ // pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
+
+ if (cbChunk >= cbHwBufferAvail)
+ {
+ // LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
+
+ vboxHwBufferFlush (pHGSMICtx);
+
+ cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
+
+ if (cbChunk >= cbHwBufferAvail)
+ {
+ // LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
+ // cb, cbHwBufferAvail));
+
+ if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold)
+ {
+ // LogFunc(("Buffer overflow!!!\n"));
+ pCtx->fHwBufferOverflow = true;
+ Assert(false);
+ return false;
+ }
+
+ cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold;
+ }
+ }
+
+ Assert(cbChunk <= cb);
+ Assert(cbChunk <= vboxHwBufferAvail (pVBVA));
+
+ vboxHwBufferPlaceDataAt (pCtx, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free);
+
+ pVBVA->off32Free = (pVBVA->off32Free + cbChunk) % pVBVA->cbData;
+ pRecord->cbRecord += cbChunk;
+ cbHwBufferAvail -= cbChunk;
+
+ cb -= cbChunk;
+ cbWritten += cbChunk;
+ }
+
+ return true;
+}
+
+/*
+ * Public writer to the hardware buffer.
+ */
+DECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx,
+ PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
+ const void *pv, uint32_t cb)
+{
+ return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb);
+}
+
+DECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code)
+{
+ VBVABUFFER *pVBVA = pCtx->pVBVA;
+
+ if (!pVBVA)
+ {
+ return false;
+ }
+
+ if (pVBVA->hostFlags.u32SupportedOrders & (1 << code))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+DECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx,
+ uint32_t offVRAMBuffer,
+ uint32_t cbBuffer)
+{
+ pCtx->offVRAMBuffer = offVRAMBuffer;
+ pCtx->cbBuffer = cbBuffer;
+}
diff --git a/src/VBox/Additions/common/VBoxVideo/todo-create-library-from-these-files-for-windows b/src/VBox/Additions/common/VBoxVideo/todo-create-library-from-these-files-for-windows
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxVideo/todo-create-library-from-these-files-for-windows
diff --git a/src/VBox/Additions/common/crOpenGL/.scm-settings b/src/VBox/Additions/common/crOpenGL/.scm-settings
new file mode 100644
index 00000000..e6362722
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/.scm-settings
@@ -0,0 +1,62 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for common/crOpenGL.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# Much of this is externally licensed, but not all, sigh.
+--external-copyright --no-convert-tabs
+
+/array/arrayspu.rc: --no-external-copyright --convert-tabs
+
+/feedback/feedbackspu.rc: --no-external-copyright --convert-tabs
+/feedback/feedback_context.c: --no-external-copyright --convert-tabs
+
+/pack/packspu.rc: --no-external-copyright --convert-tabs
+/pack/packspu_framebuffer.c: --no-external-copyright --convert-tabs
+/pack/packspu_getshaders.c: --no-external-copyright --convert-tabs
+/pack/packspu_glsl.c: --no-external-copyright --convert-tabs
+/pack/packspu_texture.c: --no-external-copyright --convert-tabs
+
+/passthrough/passthroughspu.rc: --no-external-copyright --convert-tabs
+
+/.scm-settings: --no-external-copyright --convert-tabs
+/dri_drv.c: --no-external-copyright --convert-tabs
+/dri_drv.h: --no-external-copyright --convert-tabs
+/dri_glx.h: --no-external-copyright --convert-tabs
+/egl.c: --no-external-copyright --convert-tabs
+/fakedri_drv.c: --no-external-copyright --convert-tabs
+/fakedri_drv.h: --no-external-copyright --convert-tabs
+/fakedri_glfuncsList.h: --no-external-copyright --convert-tabs --no-fix-header-guards
+/fakedri_glxfuncsList.h: --no-external-copyright --convert-tabs --no-fix-header-guards
+/glx.c: --no-external-copyright --convert-tabs
+/glx_c_exports.c: --no-external-copyright --convert-tabs
+/glx_proto.h: --no-external-copyright --convert-tabs
+/icd_drv.c: --no-external-copyright --convert-tabs
+/icd_drv.h: --no-external-copyright --convert-tabs
+/Linux_i386_glxapi_exports.py: --no-external-copyright --convert-tabs
+/Makefile.kmk: --no-external-copyright --convert-tabs
+/SunOS_i386_exports.py: --no-external-copyright --convert-tabs
+/SunOS_i386_exports_dri.py: --no-external-copyright --convert-tabs
+/SunOS_i386_glxapi_exports.py: --no-external-copyright --convert-tabs
+/VBoxCROGL.rc: --no-external-copyright --convert-tabs
+/VBoxICDList.h: --no-external-copyright --no-fix-header-guards
+
+*_special: --treat-as Makefile
+
+# Ignore some stuff.
+--filter-out-files /COPYRIGHT.LLNL
+--filter-out-files /COPYRIGHT.REDHAT
+--filter-out-files /LICENSE
+
diff --git a/src/VBox/Additions/common/crOpenGL/AIX_exports.py b/src/VBox/Additions/common/crOpenGL/AIX_exports.py
new file mode 100644
index 00000000..2f55db33
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/AIX_exports.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = ["TexImage3D", "MultiDrawElementsEXT" ]
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/COPYRIGHT.LLNL b/src/VBox/Additions/common/crOpenGL/COPYRIGHT.LLNL
new file mode 100644
index 00000000..5fc1a271
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/COPYRIGHT.LLNL
@@ -0,0 +1,61 @@
+This Chromium distribution contains information and code which is
+covered under the following notice:
+
+Copyright (c) 2002, The Regents of the University of California.
+Produced at the Lawrence Livermore National Laboratory
+For details, contact: Randall Frank (rjfrank@llnl.gov).
+UCRL-CODE-2002-058
+All rights reserved.
+
+This file is part of Chromium. For details, see accompanying
+documentation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the disclaimer below.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the disclaimer (as noted below) in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of the UC/LLNL nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF
+CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+Additional BSD Notice
+
+1. This notice is required to be provided under our contract with the U.S.
+ Department of Energy (DOE). This work was produced at the University of
+ California, Lawrence Livermore National Laboratory under Contract No.
+ W-7405-ENG-48 with the DOE.
+
+2. Neither the United States Government nor the University of California
+ nor any of their employees, makes any warranty, express or implied, or
+ assumes any liability or responsibility for the accuracy, completeness,
+ or usefulness of any information, apparatus, product, or process
+ disclosed, or represents that its use would not infringe privately-owned
+ rights.
+
+3. Also, reference herein to any specific commercial products, process, or
+ services by trade name, trademark, manufacturer or otherwise does not
+ necessarily constitute or imply its endorsement, recommendation, or
+ favoring by the United States Government or the University of
+ California. The views and opinions of authors expressed herein do not
+ necessarily state or reflect those of the United States Government or
+ the University of California, and shall not be used for advertising or
+ product endorsement purposes.
diff --git a/src/VBox/Additions/common/crOpenGL/COPYRIGHT.REDHAT b/src/VBox/Additions/common/crOpenGL/COPYRIGHT.REDHAT
new file mode 100644
index 00000000..7812243e
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/COPYRIGHT.REDHAT
@@ -0,0 +1,29 @@
+This Chromium distribution contains information and code which is
+covered under the following notice:
+
+/*
+ * Copyright 2001,2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * 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 on 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 (including the
+ * next paragraph) 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * 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.
+ */
diff --git a/src/VBox/Additions/common/crOpenGL/DD_glc.py b/src/VBox/Additions/common/crOpenGL/DD_glc.py
new file mode 100755
index 00000000..33aee4da
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/DD_glc.py
@@ -0,0 +1,139 @@
+from __future__ import print_function
+print("""
+/** @file
+ * VBox OpenGL chromium functions header
+ */
+
+/*
+ * Copyright (C) 2009-2016 """ """Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+""")
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE DD_gl.py SCRIPT */
+
+#include "chromium.h"
+#include "cr_string.h"
+#include "cr_version.h"
+#include "stub.h"
+#include "dri_drv.h"
+#include "cr_gl.h"
+""")
+
+commoncall_special = [
+ "ArrayElement",
+ "Begin",
+ "CallList",
+ "CallLists",
+ "Color3f",
+ "Color3fv",
+ "Color4f",
+ "Color4fv",
+ "EdgeFlag",
+ "End",
+ "EvalCoord1f",
+ "EvalCoord1fv",
+ "EvalCoord2f",
+ "EvalCoord2fv",
+ "EvalPoint1",
+ "EvalPoint2",
+ "FogCoordfEXT",
+ "FogCoordfvEXT",
+ "Indexf",
+ "Indexfv",
+ "Materialfv",
+ "MultiTexCoord1fARB",
+ "MultiTexCoord1fvARB",
+ "MultiTexCoord2fARB",
+ "MultiTexCoord2fvARB",
+ "MultiTexCoord3fARB",
+ "MultiTexCoord3fvARB",
+ "MultiTexCoord4fARB",
+ "MultiTexCoord4fvARB",
+ "Normal3f",
+ "Normal3fv",
+ "SecondaryColor3fEXT",
+ "SecondaryColor3fvEXT",
+ "TexCoord1f",
+ "TexCoord1fv",
+ "TexCoord2f",
+ "TexCoord2fv",
+ "TexCoord3f",
+ "TexCoord3fv",
+ "TexCoord4f",
+ "TexCoord4fv",
+ "Vertex2f",
+ "Vertex2fv",
+ "Vertex3f",
+ "Vertex3fv",
+ "Vertex4f",
+ "Vertex4fv",
+ "VertexAttrib1fNV",
+ "VertexAttrib1fvNV",
+ "VertexAttrib2fNV",
+ "VertexAttrib2fvNV",
+ "VertexAttrib3fNV",
+ "VertexAttrib3fvNV",
+ "VertexAttrib4fNV",
+ "VertexAttrib4fvNV",
+ "VertexAttrib1fARB",
+ "VertexAttrib1fvARB",
+ "VertexAttrib2fARB",
+ "VertexAttrib2fvARB",
+ "VertexAttrib3fARB",
+ "VertexAttrib3fvARB",
+ "VertexAttrib4fARB",
+ "VertexAttrib4fvARB",
+ "EvalMesh1",
+ "EvalMesh2",
+ "Rectf",
+ "DrawArrays",
+ "DrawElements",
+ "DrawRangeElements"
+]
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if "Chromium" == apiutil.Category(func_name):
+ continue
+ if func_name == "BoundsInfoCR":
+ continue
+
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ if func_name in commoncall_special:
+ print("%s vboxDD_gl%s(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params) ))
+ else:
+ if apiutil.MakeDeclarationString(params)=="void":
+ print("%s vboxDD_gl%s(GLcontext *ctx)" % (return_type, func_name ))
+ else:
+ print("%s vboxDD_gl%s(GLcontext *ctx, %s)" % (return_type, func_name, apiutil.MakeDeclarationString(params) ))
+ print("{")
+
+ if return_type != "void":
+ print("\treturn ", end=' ')
+
+ print("\tcr_gl%s(%s);" % (func_name, apiutil.MakeCallString(params)))
+ print("}")
+ print("")
+
diff --git a/src/VBox/Additions/common/crOpenGL/DD_glh.py b/src/VBox/Additions/common/crOpenGL/DD_glh.py
new file mode 100755
index 00000000..38d9ba60
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/DD_glh.py
@@ -0,0 +1,136 @@
+from __future__ import print_function
+print("""
+/** @file
+ * VBox OpenGL chromium functions header
+ */
+
+/*
+ * Copyright (C) 2009-2016 """ """Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+""")
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE DD_gl.py SCRIPT */
+#ifndef __DD_GL_H__
+#define __DD_GL_H__
+
+#include "chromium.h"
+#include "cr_string.h"
+#include "cr_version.h"
+#include "stub.h"
+
+""")
+
+commoncall_special = [
+ "ArrayElement",
+ "Begin",
+ "CallList",
+ "CallLists",
+ "Color3f",
+ "Color3fv",
+ "Color4f",
+ "Color4fv",
+ "EdgeFlag",
+ "End",
+ "EvalCoord1f",
+ "EvalCoord1fv",
+ "EvalCoord2f",
+ "EvalCoord2fv",
+ "EvalPoint1",
+ "EvalPoint2",
+ "FogCoordfEXT",
+ "FogCoordfvEXT",
+ "Indexf",
+ "Indexfv",
+ "Materialfv",
+ "MultiTexCoord1fARB",
+ "MultiTexCoord1fvARB",
+ "MultiTexCoord2fARB",
+ "MultiTexCoord2fvARB",
+ "MultiTexCoord3fARB",
+ "MultiTexCoord3fvARB",
+ "MultiTexCoord4fARB",
+ "MultiTexCoord4fvARB",
+ "Normal3f",
+ "Normal3fv",
+ "SecondaryColor3fEXT",
+ "SecondaryColor3fvEXT",
+ "TexCoord1f",
+ "TexCoord1fv",
+ "TexCoord2f",
+ "TexCoord2fv",
+ "TexCoord3f",
+ "TexCoord3fv",
+ "TexCoord4f",
+ "TexCoord4fv",
+ "Vertex2f",
+ "Vertex2fv",
+ "Vertex3f",
+ "Vertex3fv",
+ "Vertex4f",
+ "Vertex4fv",
+ "VertexAttrib1fNV",
+ "VertexAttrib1fvNV",
+ "VertexAttrib2fNV",
+ "VertexAttrib2fvNV",
+ "VertexAttrib3fNV",
+ "VertexAttrib3fvNV",
+ "VertexAttrib4fNV",
+ "VertexAttrib4fvNV",
+ "VertexAttrib1fARB",
+ "VertexAttrib1fvARB",
+ "VertexAttrib2fARB",
+ "VertexAttrib2fvARB",
+ "VertexAttrib3fARB",
+ "VertexAttrib3fvARB",
+ "VertexAttrib4fARB",
+ "VertexAttrib4fvARB",
+ "EvalMesh1",
+ "EvalMesh2",
+ "Rectf",
+ "DrawArrays",
+ "DrawElements",
+ "DrawRangeElements"
+]
+
+# Extern-like declarations
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if "Chromium" == apiutil.Category(func_name):
+ continue
+ if func_name == "BoundsInfoCR":
+ continue
+
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ if func_name in commoncall_special:
+ print("extern %s vboxDD_gl%s(%s);" % (return_type, func_name,
+ apiutil.MakeDeclarationString( params )))
+ else:
+ if apiutil.MakeDeclarationString(params)=="void":
+ print("extern %s vboxDD_gl%s(GLcontext *ctx);" % (return_type, func_name))
+ else:
+ print("extern %s vboxDD_gl%s(GLcontext *ctx, %s);" % (return_type, func_name,
+ apiutil.MakeDeclarationString( params )))
+
+print("#endif /* __DD_GL_H__ */")
diff --git a/src/VBox/Additions/common/crOpenGL/Darwin_exports.py b/src/VBox/Additions/common/crOpenGL/Darwin_exports.py
new file mode 100644
index 00000000..c3120e4d
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Darwin_exports.py
@@ -0,0 +1,12 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = ["TexImage3D", "TexImage2D", "TexImage1D", "MultiDrawArrays",
+ "BufferData", "BufferSubData", "GetBufferSubData" ]
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/FreeBSD_exports.py b/src/VBox/Additions/common/crOpenGL/FreeBSD_exports.py
new file mode 100644
index 00000000..8f63e0a5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/FreeBSD_exports.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = []
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/IRIX64_exports.py b/src/VBox/Additions/common/crOpenGL/IRIX64_exports.py
new file mode 100644
index 00000000..674efff1
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/IRIX64_exports.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = ["TexImage3D", "EdgeFlagPointer" ]
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/LICENSE b/src/VBox/Additions/common/crOpenGL/LICENSE
new file mode 100644
index 00000000..d609a358
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2002, Stanford University
+All rights reserved.
+
+Some portions of Chromium are copyrighted by individual organizations.
+Please see the files COPYRIGHT.LLNL and COPYRIGHT.REDHAT for more
+information.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of Stanford University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/VBox/Additions/common/crOpenGL/Linux_exports.py b/src/VBox/Additions/common/crOpenGL/Linux_exports.py
new file mode 100644
index 00000000..79dc8ddd
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Linux_exports.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = []
+
+entrypoints.GenerateEntrypoints(hacks)
diff --git a/src/VBox/Additions/common/crOpenGL/Linux_i386_exports.py b/src/VBox/Additions/common/crOpenGL/Linux_i386_exports.py
new file mode 100755
index 00000000..653b00a2
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Linux_i386_exports.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ #apiutil.CopyrightC()
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ print('%include "iprt/asmdefs.mac"')
+ print("")
+ print("%ifdef RT_ARCH_AMD64")
+ print("extern glim")
+ print("%else ; X86")
+ print("extern glim")
+ print("%endif")
+ print("")
+
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; Aliases')
+ print(';')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ # this dict lookup should never fail (raise an exception)!
+ index = keys.index(alias)
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; No-op stubs')
+ print(';')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("\tleave")
+ print("\tret")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/Linux_i386_exports_dri.py b/src/VBox/Additions/common/crOpenGL/Linux_i386_exports_dri.py
new file mode 100755
index 00000000..e212fb68
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Linux_i386_exports_dri.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ #apiutil.CopyrightC()
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ print('%include "iprt/asmdefs.mac"')
+ print("")
+ print("%ifdef RT_ARCH_AMD64")
+ print("extern glim")
+ print("%else ; X86")
+ print("extern glim")
+ print("%endif")
+ print("")
+
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; Aliases')
+ print(';')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ # this dict lookup should never fail (raise an exception)!
+ index = keys.index(alias)
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; No-op stubs')
+ print(';')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("\tleave")
+ print("\tret")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py b/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py
new file mode 100755
index 00000000..f345e679
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py
@@ -0,0 +1,109 @@
+from __future__ import print_function
+
+__copyright__ = \
+"""
+Copyright (C) 2009-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+"""
+
+import sys
+
+#Note, this should match the fakedri_glxfuncsList.h order
+glx_functions = [
+"CopyContext",
+"UseXFont",
+#"GetDriverConfig",
+"GetProcAddress",
+"QueryExtension",
+"IsDirect",
+"DestroyGLXPbufferSGIX",
+"QueryGLXPbufferSGIX",
+"CreateGLXPixmap",
+"CreateGLXPixmapWithConfigSGIX",
+"QueryContext",
+"CreateContextWithConfigSGIX",
+"SwapBuffers",
+"CreateNewContext",
+"SelectEventSGIX",
+"GetCurrentDrawable",
+"ChooseFBConfig",
+"WaitGL",
+"GetFBConfigs",
+"CreatePixmap",
+"GetSelectedEventSGIX",
+"GetCurrentReadDrawable",
+"GetCurrentDisplay",
+"QueryServerString",
+"CreateWindow",
+"SelectEvent",
+"GetVisualFromFBConfigSGIX",
+"GetFBConfigFromVisualSGIX",
+"QueryDrawable",
+"CreateContext",
+"GetConfig",
+"CreateGLXPbufferSGIX",
+"CreatePbuffer",
+"ChooseFBConfigSGIX",
+"WaitX",
+"GetVisualFromFBConfig",
+#"GetScreenDriver",
+"GetFBConfigAttrib",
+"GetCurrentContext",
+"GetClientString",
+"DestroyPixmap",
+"MakeCurrent",
+"DestroyContext",
+"GetProcAddressARB",
+"GetSelectedEvent",
+"DestroyPbuffer",
+"DestroyWindow",
+"DestroyGLXPixmap",
+"QueryVersion",
+"ChooseVisual",
+"MakeContextCurrent",
+"QueryExtensionsString",
+"GetFBConfigAttribSGIX",
+"FreeMemoryMESA",
+"QueryContextInfoEXT",
+"ImportContextEXT",
+"GetContextIDEXT",
+"MakeCurrentReadSGI",
+"AllocateMemoryMESA",
+"GetMemoryOffsetMESA",
+"CreateGLXPixmapMESA",
+"GetCurrentDisplayEXT",
+"FreeContextEXT"
+];
+
+print('%include "iprt/asmdefs.mac"')
+print("")
+print("%ifdef RT_ARCH_AMD64")
+print("extern glxim")
+print("%else ; X86")
+print("extern glxim")
+print("%endif")
+print("")
+
+## r=bird: This could all be done with macros in the assembler.
+
+for index in range(len(glx_functions)):
+ func_name = glx_functions[index]
+
+ print("BEGINPROC_EXPORTED vbox_glX%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glxim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword glxim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC vbox_glX%s" % func_name)
+ print("")
+
diff --git a/src/VBox/Additions/common/crOpenGL/Makefile.kmk b/src/VBox/Additions/common/crOpenGL/Makefile.kmk
new file mode 100644
index 00000000..4d0011d8
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/Makefile.kmk
@@ -0,0 +1,734 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VirtualBox Guest OpenGL part
+#
+
+#
+# Copyright (C) 2008-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Target lists.
+#
+BLDDIRS += \
+ $(VBOX_PATH_CROGL_GENFILES)/
+
+if1of ($(KBUILD_TARGET), win linux solaris freebsd)
+ DLLS += \
+ VBoxOGL \
+ VBoxOGLarrayspu \
+ VBoxOGLpassthroughspu \
+ VBoxOGLpackspu \
+ VBoxOGLfeedbackspu
+endif
+
+VBOX_OGL_X86_GUEST_DLLS = \
+ VBoxOGL-x86 \
+ VBoxOGLarrayspu-x86 \
+ VBoxOGLpassthroughspu-x86 \
+ VBoxOGLpackspu-x86 \
+ VBoxOGLfeedbackspu-x86
+
+ifdef VBOX_WITH_WDDM
+ DLLS.win.amd64 += $(VBOX_OGL_X86_GUEST_DLLS)
+endif
+
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ #VBoxOGL_DRI = 1
+ DLLS += VBoxEGL
+ ifn1of ($(KBUILD_TARGET),linux solaris) # No DRI on Solaris yet
+ VBoxOGL_FAKEDRI = 1
+ endif
+
+ # Only Solaris right now needs C stubs because I can't figure out how to
+ # generate the GOT based relocation ASM yet.
+ ifdef VBoxOGL_FAKEDRI
+ if1of ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH),solaris.x86 solaris.amd64 linux.x86 linux.amd64 freebsd.x86 freebsd.amd64)
+ VBOX_OGL_GLX_USE_CSTUBS = 1
+ endif
+ endif
+endif
+
+
+#
+# VBoxOGL
+#
+VBoxOGL_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxOGL_INCS = . $(VBOX_GRAPHICS_INCS)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ ifndef VBOX_USE_SYSTEM_GL_HEADERS
+ VBoxOGL_INCS += \
+ $(VBOX_PATH_X11_ROOT)/libXdamage-1.1 \
+ $(VBOX_PATH_X11_ROOT)/libXcomposite-0.4.0 \
+ $(VBOX_PATH_X11_ROOT)/libXext-1.3.1 \
+ $(VBOX_PATH_X11_ROOT)/libXfixes-4.0.3 \
+ $(VBOX_PATH_X11_ROOT)/damageproto-1.1.0 \
+ $(VBOX_PATH_X11_ROOT)/compositeproto-0.4 \
+ $(VBOX_PATH_X11_ROOT)/fixesproto-4.0 \
+ $(VBOX_PATH_X11_ROOT)/libx11-1.1.5-other \
+ $(VBOX_PATH_X11_ROOT)/xextproto-7.1.1 \
+ $(VBOX_PATH_X11_ROOT)/xproto-7.0.18 \
+ $(VBOX_GL_INCS)
+ endif
+ VBoxOGL_DEFS += VBOX_NO_NATIVEGL
+endif
+
+ifdef VBoxOGL_DRI
+ VBoxOGL_DEFS += VBOXOGL_DRI IN_DRI_DRIVER
+else ifdef VBoxOGL_FAKEDRI
+ VBoxOGL_DEFS += VBOXOGL_FAKEDRI
+ ifdef VBOX_OGL_GLX_USE_CSTUBS
+ VBoxOGL_DEFS += VBOX_OGL_GLX_USE_CSTUBS
+ endif
+endif
+
+ifdef VBOX_WITH_WDDM
+ VBoxOGL_DEFS.win += VBOX_WITH_WDDM
+ VBoxOGL_SDKS.win += $(VBOX_WINDDK_GST_WLH)
+endif
+
+ifeq ($(KBUILD_TARGET),win)
+#fixme?, INTERMEDIATES.win ain't working
+ VBoxOGL_INTERMEDIATES += $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h
+ if defined(VBOX_SIGNING_MODE) && defined(VBOX_WITH_WDDM)
+ VBoxOGL_INSTTYPE.win = none
+ VBoxOGL_DEBUG_INSTTYPE.win = both
+ endif
+endif
+ifdef VBoxOGL_DRI
+ VBoxOGL_INTERMEDIATES += \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h \
+ $(VBOX_PATH_CROGL_GENFILES)/DD_gl.h
+else ifdef VBoxOGL_FAKEDRI
+ VBoxOGL_INTERMEDIATES += \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h
+endif
+VBoxOGL_SOURCES += \
+ load.c \
+ stub.c \
+ context.c \
+ $(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c \
+ $(VBOX_PATH_CROGL_GENFILES)/NULLfuncs.c \
+ $(VBOX_PATH_CROGL_GENFILES)/tsfuncs.c
+
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ VBoxOGL_SOURCES += \
+ glx.c \
+ xfont.c
+ ifdef VBOX_OGL_GLX_USE_CSTUBS
+ VBoxOGL_SOURCES += glx_c_exports.c
+ endif
+
+ ifdef VBoxOGL_DRI
+ VBoxOGL_SOURCES += \
+ $(VBOX_PATH_CROGL_GENFILES)/DD_gl.c \
+ dri_drv.c
+ VBoxOGL_SOURCES.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm
+ VBoxOGL_SOURCES.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm
+ VBoxOGL_SOURCES.freebsd += \
+ $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports_dri.asm
+ else ifdef VBoxOGL_FAKEDRI
+ VBoxOGL_SOURCES += \
+ fakedri_drv.c
+ ifndef VBOX_OGL_GLX_USE_CSTUBS
+ VBoxOGL_SOURCES.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_glxapi_exports.asm \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm
+ VBoxOGL_SOURCES.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_glxapi_exports.asm \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm
+ VBoxOGL_SOURCES.freebsd += \
+ $(VBOX_PATH_CROGL_GENFILES)/freebsd_glxapi_exports.asm \
+ $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports_dri.asm
+ else
+ VBoxOGL_SOURCES.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c
+ VBoxOGL_SOURCES.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_exports.c
+ VBoxOGL_SOURCES.freebsd += \
+ $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c
+ endif
+ else
+ VBoxOGL_SOURCES.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_exports.c
+ VBoxOGL_SOURCES.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c
+ VBoxOGL_SOURCES.freebsd += \
+ $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c
+ endif
+endif
+
+VBoxOGL_SOURCES.win = \
+ wgl.c \
+ icd_drv.c \
+ VBoxCROGL.rc \
+ $(VBOX_PATH_CROGL_GENFILES)/windows_exports.asm \
+ $(VBOX_PATH_CROGL_GENFILES)/cropengl.def
+VBoxOGL_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c \
+ $(VBOX_PATH_CROGL_GENFILES)/NULLfuncs.c \
+ $(VBOX_PATH_CROGL_GENFILES)/tsfuncs.c
+VBoxOGL_CLEAN.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_exports.c
+VBoxOGL_CLEAN.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c
+VBoxOGL_CLEAN.win = \
+ $(VBOX_PATH_CROGL_GENFILES)/windows_exports.asm \
+ $(VBOX_PATH_CROGL_GENFILES)/cropengl.def
+if1of ($(KBUILD_TARGET), linux solaris)
+ ifdef VBoxOGL_DRI
+ VBoxOGL_CLEAN += \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h \
+ $(VBOX_PATH_CROGL_GENFILES)/DD_gl.h \
+ $(VBOX_PATH_CROGL_GENFILES)/DD_gl.c
+ else ifdef VBoxOGL_FAKEDRI
+ VBoxOGL_CLEAN += \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h
+ VBoxOGL_CLEAN.linux += \
+ $(VBOX_PATH_CROGL_GENFILES)/linux_glxapi_exports.asm
+ VBoxOGL_CLEAN.solaris += \
+ $(VBOX_PATH_CROGL_GENFILES)/solaris_glxapi_exports.asm
+ endif
+endif
+# VBoxOGL_LIBS = \ # VBOX_LIB_OGL_CRUTIL includes these and caused an interesting conflict.
+# $(VBOX_LIB_IPRT_GUEST_R3_SHARED) \
+# $(VBOX_LIB_VBGL_R3_SHARED) \
+
+VBoxOGL_LIBS = \
+ $(VBOX_LIB_OGL_CRUTIL) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload$(VBOX_SUFF_LIB)
+
+VBoxOGL_LIBS.win += \
+ $(PATH_STAGE_LIB)/additions/VBoxDispMpLogger$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxWddmUmKmt$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxCrHgsmi$(VBOX_SUFF_LIB)
+
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ ifdef VBOX_USE_SYSTEM_GL_HEADERS
+ VBoxOGL_LIBS += Xcomposite Xdamage Xfixes Xext
+ else
+ VBoxOGL_LIBS += \
+ $(PATH_STAGE_LIB)/libXcomposite.so \
+ $(PATH_STAGE_LIB)/libXdamage.so \
+ $(PATH_STAGE_LIB)/libXfixes.so \
+ $(PATH_STAGE_LIB)/libXext.so
+ endif
+ ifdef VBoxOGL_FAKEDRI
+ ifeq ($(KBUILD_TARGET), freebsd)
+ VBoxOGL_LIBS += \
+ elf
+ else
+ VBoxOGL_LIBS += \
+ dl
+ endif
+ else
+ VBoxOGL_SONAME.linux = libGL.so.1
+ VBoxOGL_LDFLAGS.linux += -Wl,-e,LibMain
+ endif
+endif
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGL_DEFS.win += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_WDDM
+VBoxOGL_DEFS.win += VBOX_WITH_WDDM
+endif
+if1of ($(KBUILD_TARGET), linux)
+VBoxOGL_LDFLAGS += -Wl,-z,nodelete
+endif
+ifdef VBOX_WITH_WDDM
+#
+# VBoxOGL-x86 - x86 VBoxOGL version built for amd64 build
+#
+VBoxOGL-x86_EXTENDS = VBoxOGL
+VBoxOGL-x86_BLD_TRG_ARCH = x86
+VBoxOGL-x86_LIBS = $(VBOX_LIB_IPRT_GUEST_R3_SHARED_X86) \
+ $(VBOX_LIB_VBGL_R3_SHARED_X86) \
+ $(VBOX_LIB_OGL_CRUTIL_X86) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload-x86$(VBOX_SUFF_LIB)
+
+VBoxOGL-x86_LIBS.win += \
+ $(PATH_STAGE_LIB)/additions/VBoxDispMpLogger-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxWddmUmKmt-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxCrHgsmi-x86$(VBOX_SUFF_LIB)
+
+
+VBoxOGL-x86_SOURCES.win = $(subst cropengl.def,cropengl-x86.def,$(VBoxOGL_SOURCES.win))
+VBoxOGL-x86_CLEAN.win = $(subst cropengl.def,cropengl-x86.def,$(VBoxOGL_CLEAN.win))
+VBoxOGL-x86_DEFS = $(VBoxOGL_DEFS) VBOX_WDDM_WOW64
+endif
+
+#
+# Generate files for VBoxOGL.
+#
+$(VBOX_PATH_CROGL_GENFILES)/NULLfuncs.c: $(PATH_SUB_CURRENT)/NULLfuncs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+$(VBOX_PATH_CROGL_GENFILES)/tsfuncs.c: $(PATH_SUB_CURRENT)/tsfuncs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+
+ifeq ($(KBUILD_TARGET),win)
+ # Windows
+$(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c: $(PATH_SUB_CURRENT)/windows_getprocaddress.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ ifeq ($(KBUILD_TARGET_ARCH),amd64)
+$(VBOX_PATH_CROGL_GENFILES)/cropengl.def: $(PATH_SUB_CURRENT)/defs64.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ else
+$(VBOX_PATH_CROGL_GENFILES)/cropengl.def: $(PATH_SUB_CURRENT)/defs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ endif
+
+$(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+$(VBOX_PATH_CROGL_GENFILES)/windows_exports.asm: \
+ $(PATH_SUB_CURRENT)/windows_i386_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ ifdef VBOX_WITH_WDDM
+ ifeq ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH),win.amd64)
+$(VBOX_PATH_CROGL_GENFILES)/cropengl-x86.def: $(PATH_SUB_CURRENT)/defs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ endif #ifeq ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH),win.amd64)
+ endif #ifdef VBOX_WITH_WDDM
+
+
+else if1of ($(KBUILD_TARGET), freebsd linux solaris)
+ # FreeBSD, Linux, Solaris
+$(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c: $(PATH_SUB_CURRENT)/getprocaddress.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ if !defined(VBoxOGL_DRI) && !defined(VBoxOGL_FAKEDRI)
+ ifeq ($(KBUILD_TARGET),solaris)
+$(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c: \
+ $(PATH_SUB_CURRENT)/SunOS_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ else ifeq ($(KBUILD_TARGET),freebsd)
+$(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c: \
+ $(PATH_SUB_CURRENT)/FreeBSD_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ else
+$(VBOX_PATH_CROGL_GENFILES)/linux_exports.c: \
+ $(PATH_SUB_CURRENT)/Linux_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ endif
+
+ else ifdef VBoxOGL_DRI
+$(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+$(VBOX_PATH_CROGL_GENFILES)/DD_gl.h: $(PATH_SUB_CURRENT)/DD_glh.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+$(VBOX_PATH_CROGL_GENFILES)/DD_gl.c: $(PATH_SUB_CURRENT)/DD_glc.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ ifeq ($(KBUILD_TARGET),solaris)
+$(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm: \
+ $(PATH_SUB_CURRENT)/SunOS_i386_exports_dri.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ else ifeq ($(KBUILD_TARGET),freebsd)
+$(VBOX_PATH_CROGL_GENFILES)/freebsd_exports_dri.asm: \
+ $(PATH_SUB_CURRENT)/FreeBSD_i386_exports_dri.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ else
+$(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm: \
+ $(PATH_SUB_CURRENT)/Linux_i386_exports_dri.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ endif
+
+ else ifdef VBoxOGL_FAKEDRI
+$(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ ifndef VBOX_OGL_GLX_USE_CSTUBS
+ ifeq ($(KBUILD_TARGET),solaris)
+$(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm: \
+ $(PATH_SUB_CURRENT)/SunOS_i386_exports_dri.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+$(VBOX_PATH_CROGL_GENFILES)/solaris_glxapi_exports.asm: $(PATH_SUB_CURRENT)/SunOS_i386_glxapi_exports.py | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $<
+
+ else
+$(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm: \
+ $(PATH_SUB_CURRENT)/Linux_i386_exports_dri.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+$(VBOX_PATH_CROGL_GENFILES)/linux_glxapi_exports.asm: $(PATH_SUB_CURRENT)/Linux_i386_glxapi_exports.py | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $<
+ endif
+
+ else
+ ifeq ($(KBUILD_TARGET),solaris)
+$(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c: \
+ $(PATH_SUB_CURRENT)/SunOS_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+ else ifeq ($(KBUILD_TARGET),freebsd)
+$(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c: \
+ $(PATH_SUB_CURRENT)/FreeBSD_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ else
+$(VBOX_PATH_CROGL_GENFILES)/linux_exports.c: \
+ $(PATH_SUB_CURRENT)/Linux_exports.py \
+ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+ endif
+
+ endif # VBOX_OGL_GLX_USE_CSTUBS
+ endif
+endif
+
+#
+# VBoxOGLarrayspu
+#
+VBoxOGLarrayspu_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxOGLarrayspu_INCS = \
+ array \
+ $(VBOX_GRAPHICS_INCS)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ VBoxOGLarrayspu_INCS += \
+ $(VBOX_GL_INCS)
+endif
+if defined(VBOX_SIGNING_MODE) && defined(VBOX_WITH_WDDM)
+ VBoxOGLarrayspu_INSTTYPE.win = none
+ VBoxOGLarrayspu_DEBUG_INSTTYPE.win = both
+endif
+VBoxOGLarrayspu_INCS.darwin += $(PATH_OUT)/obj/VBoxOGL
+VBoxOGLarrayspu_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h
+VBoxOGLarrayspu_SOURCES = \
+ array/arrayspu.c \
+ array/arrayspu_config.c \
+ array/arrayspu_init.c
+VBoxOGLarrayspu_SOURCES.win = \
+ array/arrayspu.def \
+ array/arrayspu.rc
+VBoxOGLarrayspu_LIBS = \
+ $(VBOX_LIB_OGL_CRUTIL) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate$(VBOX_SUFF_LIB)
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLarrayspu_DEFS.win += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_WDDM
+VBoxOGLarrayspu_DEFS.win += VBOX_WITH_WDDM
+endif
+
+ifdef VBOX_WITH_WDDM
+#
+# VBoxOGLarrayspu-x86 - x86 version of VBoxOGLarrayspu built for amd64 build
+#
+VBoxOGLarrayspu-x86_EXTENDS = VBoxOGLarrayspu
+VBoxOGLarrayspu-x86_BLD_TRG_ARCH = x86
+VBoxOGLarrayspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate-x86$(VBOX_SUFF_LIB)
+VBoxOGLarrayspu-x86_DEFS = $(VBoxOGLarrayspu_DEFS) VBOX_WDDM_WOW64
+endif
+
+#
+# VBoxOGLpassthroughspu
+#
+VBoxOGLpassthroughspu_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxOGLpassthroughspu_INCS = \
+ passthrough \
+ $(VBOX_GRAPHICS_INCS)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ VBoxOGLpassthroughspu_INCS += \
+ $(VBOX_GL_INCS)
+endif
+if defined(VBOX_SIGNING_MODE) && defined(VBOX_WITH_WDDM)
+ VBoxOGLpassthroughspu_INSTTYPE.win = none
+ VBoxOGLpassthroughspu_DEBUG_INSTTYPE.win = both
+endif
+VBoxOGLpassthroughspu_SOURCES = \
+ passthrough/passthroughspu_init.c \
+ $(VBOX_PATH_CROGL_GENFILES)/passthroughspu.c
+VBoxOGLpassthroughspu_SOURCES.win = \
+ passthrough/passthrough.def \
+ passthrough/passthroughspu.rc
+VBoxOGLpassthroughspu_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/passthroughspu.c
+VBoxOGLpassthroughspu_LIBS = \
+ $(VBOX_LIB_OGL_CRUTIL)
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLpassthroughspu_DEFS.win += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_WDDM
+VBoxOGLpassthroughspu_DEFS.win += VBOX_WITH_WDDM
+endif
+
+ifdef VBOX_WITH_WDDM
+#
+# VBoxOGLpassthroughspu-x86 - x86 version of VBoxOGLpassthroughspu built for amd64 build
+#
+VBoxOGLpassthroughspu-x86_EXTENDS = VBoxOGLpassthroughspu
+VBoxOGLpassthroughspu-x86_BLD_TRG_ARCH = x86
+VBoxOGLpassthroughspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86)
+VBoxOGLpassthroughspu-x86_DEFS = $(VBoxOGLpassthroughspu_DEFS) VBOX_WDDM_WOW64
+endif
+
+#
+# Generate files for VBoxOGLpassthroughspu.
+#
+$(VBOX_PATH_CROGL_GENFILES)/passthroughspu.c: $(PATH_SUB_CURRENT)/passthrough/passthrough.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI)
+
+#
+# VBoxOGLpackspu
+#
+VBoxOGLpackspu_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxOGLpackspu_DEFS = TRACKS_STATE=1 PACKS=1
+VBoxOGLpackspu_INCS = \
+ pack \
+ $(VBOX_GRAPHICS_INCS)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ VBoxOGLpackspu_INCS += \
+ $(VBOX_GL_INCS)
+endif
+if defined(VBOX_SIGNING_MODE) && defined(VBOX_WITH_WDDM)
+ VBoxOGLpackspu_INSTTYPE.win = none
+ VBoxOGLpackspu_DEBUG_INSTTYPE.win = both
+endif
+VBoxOGLpackspu_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_proto.h \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_packfunctions.h
+VBoxOGLpackspu_SOURCES = \
+ pack/packspu_bufferobject.c \
+ pack/packspu_client.c \
+ pack/packspu_config.c \
+ pack/packspu_context.c \
+ pack/packspu_getstring.c \
+ pack/packspu_init.c \
+ pack/packspu_misc.c \
+ pack/packspu_net.c \
+ pack/packspu_swapbuf.c \
+ pack/packspu_pixel.c \
+ pack/packspu_texture.c \
+ pack/packspu_getshaders.c \
+ pack/packspu_glsl.c \
+ pack/packspu_framebuffer.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_get.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_flush.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_beginend.c
+VBoxOGLpackspu_SOURCES.win = \
+ pack/pack.def \
+ pack/packspu.rc
+VBoxOGLpackspu_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_proto.h \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_get.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_flush.c \
+ $(VBOX_PATH_CROGL_GENFILES)/packspu_beginend.c
+VBoxOGLpackspu_LIBS = \
+ $(VBOX_LIB_OGL_CRUTIL) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrpacker$(VBOX_SUFF_LIB)
+VBoxOGLpackspu_LIBS.win += \
+ $(PATH_STAGE_LIB)/additions/VBoxDispMpLogger$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxWddmUmKmt$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxCrHgsmi$(VBOX_SUFF_LIB)
+
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLpackspu_DEFS.win += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_CRDUMPER
+VBoxOGLpackspu_DEFS += VBOX_WITH_CRDUMPER
+endif
+ifdef VBOX_WITH_CRPACKSPU_DUMPER
+VBoxOGLpackspu_DEFS += VBOX_WITH_CRPACKSPU_DUMPER
+endif
+ifdef VBOX_WITH_WDDM
+VBoxOGLpackspu_DEFS.win += VBOX_WITH_WDDM
+endif
+
+ifdef VBOX_WITH_WDDM
+#
+# VBoxOGLpackspu-x86 - x86 version of VBoxOGLpackspu built for amd64 build
+#
+VBoxOGLpackspu-x86_EXTENDS = VBoxOGLpackspu
+VBoxOGLpackspu-x86_BLD_TRG_ARCH = x86
+VBoxOGLpackspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrpacker-x86$(VBOX_SUFF_LIB)
+VBoxOGLpackspu-x86_LIBS.win += \
+ $(PATH_STAGE_LIB)/additions/VBoxDispMpLogger-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxWddmUmKmt-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxCrHgsmi-x86$(VBOX_SUFF_LIB)
+VBoxOGLpackspu-x86_DEFS = $(VBoxOGLpackspu_DEFS) VBOX_WDDM_WOW64
+endif
+
+#
+# Generate files for VBoxOGLpackspu.
+#
+$(VBOX_PATH_CROGL_GENFILES)/packspu.c: $(addprefix $(PATH_SUB_CURRENT)/pack/,pack.py packspu_special packspu_unimplemented_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/packspu_get.c: $(PATH_SUB_CURRENT)/pack/packspu_get.py $(PATH_SUB_CURRENT)/pack/packspu_special $(PATH_ROOT)/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/packspu_flush.c: $(PATH_SUB_CURRENT)/pack/packspu_flush.py $(PATH_SUB_CURRENT)/pack/packspu_flush_special $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/packspu_beginend.c: $(PATH_SUB_CURRENT)/pack/packspu_beginend.py $(PATH_SUB_CURRENT)/pack/packspu_vertex_special $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/packspu_proto.h: $(addprefix $(PATH_SUB_CURRENT)/pack/,packspu_proto.py packspu_special packspu_unimplemented_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+#
+# VBoxOGLfeedbackspu
+#
+VBoxOGLfeedbackspu_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxOGLfeedbackspu_INCS = \
+ feedback \
+ $(VBOX_GRAPHICS_INCS)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ VBoxOGLfeedbackspu_INCS += \
+ $(VBOX_GL_INCS)
+endif
+if defined(VBOX_SIGNING_MODE) && defined(VBOX_WITH_WDDM)
+ VBoxOGLfeedbackspu_INSTTYPE.win = none
+ VBoxOGLfeedbackspu_DEBUG_INSTTYPE.win = both
+endif
+VBoxOGLarrayspu_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu_proto.h
+VBoxOGLfeedbackspu_SOURCES = \
+ feedback/feedbackspu_config.c \
+ feedback/feedbackspu_init.c \
+ feedback/feedback_context.c \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu.c \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu_state.c
+VBoxOGLfeedbackspu_SOURCES.win = \
+ feedback/feedback.def \
+ feedback/feedbackspu.rc
+VBoxOGLfeedbackspu_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu_proto.h \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu.c \
+ $(VBOX_PATH_CROGL_GENFILES)/feedbackspu_state.c
+VBoxOGLfeedbackspu_LIBS = \
+ $(VBOX_LIB_OGL_CRUTIL) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate$(VBOX_SUFF_LIB)
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLfeedbackspu_DEFS.win += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_WDDM
+VBoxOGLfeedbackspu_DEFS.win += VBOX_WITH_WDDM
+endif
+
+ifdef VBOX_WITH_WDDM
+#
+# VBoxOGLfeedbackspu-x86 - x86 version of VBoxOGLfeedbackspu built for amd64 build
+#
+VBoxOGLfeedbackspu-x86_EXTENDS = VBoxOGLfeedbackspu
+VBoxOGLfeedbackspu-x86_BLD_TRG_ARCH = x86
+VBoxOGLfeedbackspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLspuload-x86$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/additions/VBoxOGLcrstate-x86$(VBOX_SUFF_LIB)
+VBoxOGLfeedbackspu-x86_DEFS = $(VBoxOGLfeedbackspu_DEFS) VBOX_WDDM_WOW64
+endif
+
+#
+# Generate files for VBoxOGLfeedbackspu.
+#
+$(VBOX_PATH_CROGL_GENFILES)/feedbackspu.c: $(addprefix $(PATH_SUB_CURRENT)/feedback/,feedback.py feedback_special select_special feedback_state_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/feedbackspu_state.c: $(addprefix $(PATH_SUB_CURRENT)/feedback/,feedback_state.py feedback_state_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/feedbackspu_proto.h: $(addprefix $(PATH_SUB_CURRENT)/feedback/,feedbackspu_proto.py feedback_special select_special feedback_state_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+VBoxEGL_TEMPLATE = VBOXCROGLR3GUESTDLL
+VBoxEGL_SOURCES = egl.c
+ifndef VBOX_USE_SYSTEM_GL_HEADERS
+ VBoxEGL_INCS = $(VBOX_PATH_X11_ROOT)/mesa-11.0.7
+endif
+VBoxEGL_LIBS = $(VBOX_LIB_OGL) # $(VBOX_LIB_IPRT_GUEST_R3_SHARED)
+VBoxEGL_SONAME.linux = libEGL.so.1
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/crOpenGL/NULLfuncs.py b/src/VBox/Additions/common/crOpenGL/NULLfuncs.py
new file mode 100755
index 00000000..a6427837
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/NULLfuncs.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE NULLfuncs.py SCRIPT */
+
+#include "cr_error.h"
+#include "stub.h"
+""")
+
+for func_name in keys:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ print("static %s SPULOAD_APIENTRY NULL_%s(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("{")
+ print("\t/* do nothing */")
+ print("\tcrWarning(\"YOU ARE CALLING A NULLED FUNCTION (%s)\");" % func_name)
+ for (name, type, vecSize) in params:
+ print("\t(void) %s;" % name)
+ if return_type != "void":
+ print("\treturn 0;")
+ print("}")
+ print("")
+
+
+print("DECLEXPORT(SPUDispatchTable) stubNULLDispatch = {")
+for func_name in keys:
+ print("\tNULL_%s," % (func_name))
+print("\tNULL, /* copyList */")
+print("\tNULL, /* copy_of */")
+print("\t0, /* mark */")
+print("\tNULL /* server */")
+print("};")
+
+print("")
+print("/* Declare and initialize the glim dispatch table here so that we */")
+print("/* can initialize all entries to no-op routines. */")
+print("SPUDispatchTable glim = {")
+for func_name in keys:
+ print("\tNULL_%s," % (func_name))
+print("\tNULL, /* copyList */")
+print("\tNULL, /* copy_of */")
+print("\t0, /* mark */")
+print("\tNULL /* server */")
+print("};")
diff --git a/src/VBox/Additions/common/crOpenGL/OSF1_exports.py b/src/VBox/Additions/common/crOpenGL/OSF1_exports.py
new file mode 100644
index 00000000..8f63e0a5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/OSF1_exports.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = []
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_exports.py b/src/VBox/Additions/common/crOpenGL/SunOS_exports.py
new file mode 100644
index 00000000..8f63e0a5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/SunOS_exports.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import entrypoints
+
+hacks = []
+
+entrypoints.GenerateEntrypoints(hacks)
+
diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py
new file mode 100755
index 00000000..b3591339
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py
@@ -0,0 +1,98 @@
+"""
+Copyright (C) 2009-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+"""
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ #apiutil.CopyrightC()
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ print('%include "iprt/asmdefs.mac"')
+ print("")
+ print("%ifdef RT_ARCH_AMD64")
+ print("extern glim")
+ print("%else ; X86")
+ print("extern glim")
+ print("%endif")
+ print("")
+
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tjmp \t[glim+%d wrt rip wrt ..gotpcrel]" % (8*index))
+ print("%else ; X86")
+ print("\tjmp \t[glim+%d wrt ..gotpc]" % (4*index))
+ print("%endif")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; Aliases')
+ print(';')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ # this dict lookup should never fail (raise an exception)!
+ index = keys.index(alias)
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tjmp \t[glim+%d wrt rip wrt ..gotpcrel]" % (8*index))
+ print("%else ; X86")
+ print("\tjmp \t[glim+%d wrt ..gotpc]" % (4*index))
+ print("%endif")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; No-op stubs')
+ print(';')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ print("BEGINPROC_EXPORTED gl%s" % func_name)
+ print("\tleave")
+ print("\tret")
+ print("ENDPROC gl%s" % func_name)
+ print("")
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py
new file mode 100755
index 00000000..a8b951bd
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py
@@ -0,0 +1,98 @@
+"""
+Copyright (C) 2009-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+"""
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ #apiutil.CopyrightC()
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ print('%include "iprt/asmdefs.mac"')
+ print("")
+ print("%ifdef RT_ARCH_AMD64")
+ print("extern glim")
+ print("%else ; X86")
+ print("extern glim")
+ print("%endif")
+ print("")
+
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tjmp \t[glim+%d wrt rip wrt ..gotpcrel]" % (8*index))
+ print("%else ; X86")
+ print("\tjmp \t[glim+%d wrt ..gotpc]" % (4*index))
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; Aliases')
+ print(';')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ # this dict lookup should never fail (raise an exception)!
+ index = keys.index(alias)
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tjmp \t[glim+%d wrt rip wrt ..gotpcrel]" % (8*index))
+ print("%else ; X86")
+ print("\tjmp \t[glim+%d wrt ..gotpc]" % (4*index))
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; No-op stubs')
+ print(';')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("\tleave")
+ print("\tret")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py
new file mode 100755
index 00000000..24d7cc53
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py
@@ -0,0 +1,103 @@
+"""
+Copyright (C) 2009-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+"""
+
+from __future__ import print_function
+import sys
+
+#Note, this should match the fakedri_glxfuncsList.h order
+glx_functions = [
+"CopyContext",
+"UseXFont",
+#"GetDriverConfig",
+"GetProcAddress",
+"QueryExtension",
+"IsDirect",
+"DestroyGLXPbufferSGIX",
+"QueryGLXPbufferSGIX",
+"CreateGLXPixmap",
+"CreateGLXPixmapWithConfigSGIX",
+"QueryContext",
+"CreateContextWithConfigSGIX",
+"SwapBuffers",
+"CreateNewContext",
+"SelectEventSGIX",
+"GetCurrentDrawable",
+"ChooseFBConfig",
+"WaitGL",
+"GetFBConfigs",
+"CreatePixmap",
+"GetSelectedEventSGIX",
+"GetCurrentReadDrawable",
+"GetCurrentDisplay",
+"QueryServerString",
+"CreateWindow",
+"SelectEvent",
+"GetVisualFromFBConfigSGIX",
+"GetFBConfigFromVisualSGIX",
+"QueryDrawable",
+"CreateContext",
+"GetConfig",
+"CreateGLXPbufferSGIX",
+"CreatePbuffer",
+"ChooseFBConfigSGIX",
+"WaitX",
+"GetVisualFromFBConfig",
+#"GetScreenDriver",
+"GetFBConfigAttrib",
+"GetCurrentContext",
+"GetClientString",
+"DestroyPixmap",
+"MakeCurrent",
+"DestroyContext",
+"GetProcAddressARB",
+"GetSelectedEvent",
+"DestroyPbuffer",
+"DestroyWindow",
+"DestroyGLXPixmap",
+"QueryVersion",
+"ChooseVisual",
+"MakeContextCurrent",
+"QueryExtensionsString",
+"GetFBConfigAttribSGIX",
+"FreeMemoryMESA",
+"QueryContextInfoEXT",
+"ImportContextEXT",
+"GetContextIDEXT",
+"MakeCurrentReadSGI",
+"AllocateMemoryMESA",
+"GetMemoryOffsetMESA",
+"CreateGLXPixmapMESA",
+"GetCurrentDisplayEXT",
+"FreeContextEXT"
+];
+
+print('%include "iprt/asmdefs.mac"')
+print("")
+print("%ifdef RT_ARCH_AMD64")
+print("extern glxim")
+print("%else ; X86")
+print("extern glxim")
+print("%endif")
+print("")
+
+for index in range(len(glx_functions)):
+ func_name = glx_functions[index]
+
+ print("BEGINPROC_EXPORTED vbox_glX%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tjmp \t[glxim+%d wrt rip wrt ..gotpcrel]" % (8*index))
+ print("%else ; X86")
+ print("\tjmp \t[glxim+%d wrt ..gotpc]" % (4*index))
+ print("%endif")
+ print("ENDPROC vbox_glX%s" % func_name)
+ print("")
+
diff --git a/src/VBox/Additions/common/crOpenGL/VBoxCROGL.rc b/src/VBox/Additions/common/crOpenGL/VBoxCROGL.rc
new file mode 100644
index 00000000..1cafaca7
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/VBoxCROGL.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxCROGL.rc $ */
+/** @file
+ * VBoxCROGL - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2008-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_DISPLAY
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxCROGL\0"
+#ifdef VBOX_WDDM_WOW64
+ VALUE "OriginalFilename", "VBoxCROGL-x86.dll\0"
+#else
+ VALUE "OriginalFilename", "VBoxCROGL.dll\0"
+#endif
+ 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
+
+1 RCDATA
+BEGIN
+// Machine dependent parameters
+ 17, // Height of vertical thumb
+ 17, // Width of horizontal thumb
+ 2, // Icon horiz compression factor
+ 2, // Icon vert compression factor
+ 1, // Cursor horz compression factor
+ 1, // Cursor vert compression factor
+ 0, // Kanji window height
+ 1, // cxBorder (thickness of vertical lines)
+ 1 // cyBorder (thickness of horizontal lines)
+END
diff --git a/src/VBox/Additions/common/crOpenGL/VBoxICDList.h b/src/VBox/Additions/common/crOpenGL/VBoxICDList.h
new file mode 100644
index 00000000..dcc901da
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/VBoxICDList.h
@@ -0,0 +1,385 @@
+/* $Id: VBoxICDList.h $ */
+/** @file
+ * VirtualBox Windows NT/2000/XP guest OpenGL ICD
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * Mesa 3-D graphics library
+ * Version: 6.1
+ *
+ * Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+ *
+ * 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
+ * BRIAN PAUL 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.
+ */
+
+/*
+ * File name: icd.c
+ * Author: Gregor Anich
+ *
+ * ICD (Installable Client Driver) interface.
+ * Based on the windows GDI/WGL driver.
+ */
+
+ICD_ENTRY(NewList) /* 0 */
+ICD_ENTRY(EndList) /* 1 */
+ICD_ENTRY(CallList) /* 2 */
+ICD_ENTRY(CallLists) /* 3 */
+ICD_ENTRY(DeleteLists) /* 4 */
+ICD_ENTRY(GenLists) /* 5 */
+ICD_ENTRY(ListBase) /* 6 */
+ICD_ENTRY(Begin) /* 7 */
+ICD_ENTRY(Bitmap) /* 8 */
+ICD_ENTRY(Color3b) /* 9 */
+ICD_ENTRY(Color3bv) /* 10 */
+ICD_ENTRY(Color3d) /* 11 */
+ICD_ENTRY(Color3dv) /* 12 */
+ICD_ENTRY(Color3f) /* 13 */
+ICD_ENTRY(Color3fv) /* 14 */
+ICD_ENTRY(Color3i) /* 15 */
+ICD_ENTRY(Color3iv) /* 16 */
+ICD_ENTRY(Color3s) /* 17 */
+ICD_ENTRY(Color3sv) /* 18 */
+ICD_ENTRY(Color3ub) /* 19 */
+ICD_ENTRY(Color3ubv) /* 20 */
+ICD_ENTRY(Color3ui) /* 21 */
+ICD_ENTRY(Color3uiv) /* 22 */
+ICD_ENTRY(Color3us) /* 23 */
+ICD_ENTRY(Color3usv) /* 24 */
+ICD_ENTRY(Color4b) /* 25 */
+ICD_ENTRY(Color4bv) /* 26 */
+ICD_ENTRY(Color4d) /* 27 */
+ICD_ENTRY(Color4dv) /* 28 */
+ICD_ENTRY(Color4f) /* 29 */
+ICD_ENTRY(Color4fv) /* 30 */
+ICD_ENTRY(Color4i) /* 31 */
+ICD_ENTRY(Color4iv) /* 32 */
+ICD_ENTRY(Color4s) /* 33 */
+ICD_ENTRY(Color4sv) /* 34 */
+ICD_ENTRY(Color4ub) /* 35 */
+ICD_ENTRY(Color4ubv) /* 36 */
+ICD_ENTRY(Color4ui) /* 37 */
+ICD_ENTRY(Color4uiv) /* 38 */
+ICD_ENTRY(Color4us) /* 39 */
+ICD_ENTRY(Color4usv) /* 40 */
+ICD_ENTRY(EdgeFlag) /* 41 */
+ICD_ENTRY(EdgeFlagv) /* 42 */
+ICD_ENTRY(End) /* 43 */
+ICD_ENTRY(Indexd) /* 44 */
+ICD_ENTRY(Indexdv) /* 45 */
+ICD_ENTRY(Indexf) /* 46 */
+ICD_ENTRY(Indexfv) /* 47 */
+ICD_ENTRY(Indexi) /* 48 */
+ICD_ENTRY(Indexiv) /* 49 */
+ICD_ENTRY(Indexs) /* 50 */
+ICD_ENTRY(Indexsv) /* 51 */
+ICD_ENTRY(Normal3b) /* 52 */
+ICD_ENTRY(Normal3bv) /* 53 */
+ICD_ENTRY(Normal3d) /* 54 */
+ICD_ENTRY(Normal3dv) /* 55 */
+ICD_ENTRY(Normal3f) /* 56 */
+ICD_ENTRY(Normal3fv) /* 57 */
+ICD_ENTRY(Normal3i) /* 58 */
+ICD_ENTRY(Normal3iv) /* 59 */
+ICD_ENTRY(Normal3s) /* 60 */
+ICD_ENTRY(Normal3sv) /* 61 */
+ICD_ENTRY(RasterPos2d) /* 62 */
+ICD_ENTRY(RasterPos2dv) /* 63 */
+ICD_ENTRY(RasterPos2f) /* 64 */
+ICD_ENTRY(RasterPos2fv) /* 65 */
+ICD_ENTRY(RasterPos2i) /* 66 */
+ICD_ENTRY(RasterPos2iv) /* 67 */
+ICD_ENTRY(RasterPos2s) /* 68 */
+ICD_ENTRY(RasterPos2sv) /* 69 */
+ICD_ENTRY(RasterPos3d) /* 70 */
+ICD_ENTRY(RasterPos3dv) /* 71 */
+ICD_ENTRY(RasterPos3f) /* 72 */
+ICD_ENTRY(RasterPos3fv) /* 73 */
+ICD_ENTRY(RasterPos3i) /* 74 */
+ICD_ENTRY(RasterPos3iv) /* 75 */
+ICD_ENTRY(RasterPos3s) /* 76 */
+ICD_ENTRY(RasterPos3sv) /* 77 */
+ICD_ENTRY(RasterPos4d) /* 78 */
+ICD_ENTRY(RasterPos4dv) /* 79 */
+ICD_ENTRY(RasterPos4f) /* 80 */
+ICD_ENTRY(RasterPos4fv) /* 81 */
+ICD_ENTRY(RasterPos4i) /* 82 */
+ICD_ENTRY(RasterPos4iv) /* 83 */
+ICD_ENTRY(RasterPos4s) /* 84 */
+ICD_ENTRY(RasterPos4sv) /* 85 */
+ICD_ENTRY(Rectd) /* 86 */
+ICD_ENTRY(Rectdv) /* 87 */
+ICD_ENTRY(Rectf) /* 88 */
+ICD_ENTRY(Rectfv) /* 89 */
+ICD_ENTRY(Recti) /* 90 */
+ICD_ENTRY(Rectiv) /* 91 */
+ICD_ENTRY(Rects) /* 92 */
+ICD_ENTRY(Rectsv) /* 93 */
+ICD_ENTRY(TexCoord1d) /* 94 */
+ICD_ENTRY(TexCoord1dv) /* 95 */
+ICD_ENTRY(TexCoord1f) /* 96 */
+ICD_ENTRY(TexCoord1fv) /* 97 */
+ICD_ENTRY(TexCoord1i) /* 98 */
+ICD_ENTRY(TexCoord1iv) /* 99 */
+ICD_ENTRY(TexCoord1s) /* 100 */
+ICD_ENTRY(TexCoord1sv) /* 101 */
+ICD_ENTRY(TexCoord2d) /* 102 */
+ICD_ENTRY(TexCoord2dv) /* 103 */
+ICD_ENTRY(TexCoord2f) /* 104 */
+ICD_ENTRY(TexCoord2fv) /* 105 */
+ICD_ENTRY(TexCoord2i) /* 106 */
+ICD_ENTRY(TexCoord2iv) /* 107 */
+ICD_ENTRY(TexCoord2s) /* 108 */
+ICD_ENTRY(TexCoord2sv) /* 109 */
+ICD_ENTRY(TexCoord3d) /* 110 */
+ICD_ENTRY(TexCoord3dv) /* 111 */
+ICD_ENTRY(TexCoord3f) /* 112 */
+ICD_ENTRY(TexCoord3fv) /* 113 */
+ICD_ENTRY(TexCoord3i) /* 114 */
+ICD_ENTRY(TexCoord3iv) /* 115 */
+ICD_ENTRY(TexCoord3s) /* 116 */
+ICD_ENTRY(TexCoord3sv) /* 117 */
+ICD_ENTRY(TexCoord4d) /* 118 */
+ICD_ENTRY(TexCoord4dv) /* 119 */
+ICD_ENTRY(TexCoord4f) /* 120 */
+ICD_ENTRY(TexCoord4fv) /* 121 */
+ICD_ENTRY(TexCoord4i) /* 122 */
+ICD_ENTRY(TexCoord4iv) /* 123 */
+ICD_ENTRY(TexCoord4s) /* 124 */
+ICD_ENTRY(TexCoord4sv) /* 125 */
+ICD_ENTRY(Vertex2d) /* 126 */
+ICD_ENTRY(Vertex2dv) /* 127 */
+ICD_ENTRY(Vertex2f) /* 128 */
+ICD_ENTRY(Vertex2fv) /* 129 */
+ICD_ENTRY(Vertex2i) /* 130 */
+ICD_ENTRY(Vertex2iv) /* 131 */
+ICD_ENTRY(Vertex2s) /* 132 */
+ICD_ENTRY(Vertex2sv) /* 133 */
+ICD_ENTRY(Vertex3d) /* 134 */
+ICD_ENTRY(Vertex3dv) /* 135 */
+ICD_ENTRY(Vertex3f) /* 136 */
+ICD_ENTRY(Vertex3fv) /* 137 */
+ICD_ENTRY(Vertex3i) /* 138 */
+ICD_ENTRY(Vertex3iv) /* 139 */
+ICD_ENTRY(Vertex3s) /* 140 */
+ICD_ENTRY(Vertex3sv) /* 141 */
+ICD_ENTRY(Vertex4d) /* 142 */
+ICD_ENTRY(Vertex4dv) /* 143 */
+ICD_ENTRY(Vertex4f) /* 144 */
+ICD_ENTRY(Vertex4fv) /* 145 */
+ICD_ENTRY(Vertex4i) /* 146 */
+ICD_ENTRY(Vertex4iv) /* 147 */
+ICD_ENTRY(Vertex4s) /* 148 */
+ICD_ENTRY(Vertex4sv) /* 149 */
+ICD_ENTRY(ClipPlane) /* 150 */
+ICD_ENTRY(ColorMaterial) /* 151 */
+ICD_ENTRY(CullFace) /* 152 */
+ICD_ENTRY(Fogf) /* 153 */
+ICD_ENTRY(Fogfv) /* 154 */
+ICD_ENTRY(Fogi) /* 155 */
+ICD_ENTRY(Fogiv) /* 156 */
+ICD_ENTRY(FrontFace) /* 157 */
+ICD_ENTRY(Hint) /* 158 */
+ICD_ENTRY(Lightf) /* 159 */
+ICD_ENTRY(Lightfv) /* 160 */
+ICD_ENTRY(Lighti) /* 161 */
+ICD_ENTRY(Lightiv) /* 162 */
+ICD_ENTRY(LightModelf) /* 163 */
+ICD_ENTRY(LightModelfv) /* 164 */
+ICD_ENTRY(LightModeli) /* 165 */
+ICD_ENTRY(LightModeliv) /* 166 */
+ICD_ENTRY(LineStipple) /* 167 */
+ICD_ENTRY(LineWidth) /* 168 */
+ICD_ENTRY(Materialf) /* 169 */
+ICD_ENTRY(Materialfv) /* 170 */
+ICD_ENTRY(Materiali) /* 171 */
+ICD_ENTRY(Materialiv) /* 172 */
+ICD_ENTRY(PointSize) /* 173 */
+ICD_ENTRY(PolygonMode) /* 174 */
+ICD_ENTRY(PolygonStipple) /* 175 */
+ICD_ENTRY(Scissor) /* 176 */
+ICD_ENTRY(ShadeModel) /* 177 */
+ICD_ENTRY(TexParameterf) /* 178 */
+ICD_ENTRY(TexParameterfv) /* 179 */
+ICD_ENTRY(TexParameteri) /* 180 */
+ICD_ENTRY(TexParameteriv) /* 181 */
+ICD_ENTRY(TexImage1D) /* 182 */
+ICD_ENTRY(TexImage2D) /* 183 */
+ICD_ENTRY(TexEnvf) /* 184 */
+ICD_ENTRY(TexEnvfv) /* 185 */
+ICD_ENTRY(TexEnvi) /* 186 */
+ICD_ENTRY(TexEnviv) /* 187 */
+ICD_ENTRY(TexGend) /* 188 */
+ICD_ENTRY(TexGendv) /* 189 */
+ICD_ENTRY(TexGenf) /* 190 */
+ICD_ENTRY(TexGenfv) /* 191 */
+ICD_ENTRY(TexGeni) /* 192 */
+ICD_ENTRY(TexGeniv) /* 193 */
+ICD_ENTRY(FeedbackBuffer) /* 194 */
+ICD_ENTRY(SelectBuffer) /* 195 */
+ICD_ENTRY(RenderMode) /* 196 */
+ICD_ENTRY(InitNames) /* 197 */
+ICD_ENTRY(LoadName) /* 198 */
+ICD_ENTRY(PassThrough) /* 199 */
+ICD_ENTRY(PopName) /* 200 */
+ICD_ENTRY(PushName) /* 201 */
+ICD_ENTRY(DrawBuffer) /* 202 */
+ICD_ENTRY(Clear) /* 203 */
+ICD_ENTRY(ClearAccum) /* 204 */
+ICD_ENTRY(ClearIndex) /* 205 */
+ICD_ENTRY(ClearColor) /* 206 */
+ICD_ENTRY(ClearStencil) /* 207 */
+ICD_ENTRY(ClearDepth) /* 208 */
+ICD_ENTRY(StencilMask) /* 209 */
+ICD_ENTRY(ColorMask) /* 210 */
+ICD_ENTRY(DepthMask) /* 211 */
+ICD_ENTRY(IndexMask) /* 212 */
+ICD_ENTRY(Accum) /* 213 */
+ICD_ENTRY(Disable) /* 214 */
+ICD_ENTRY(Enable) /* 215 */
+ICD_ENTRY(Finish) /* 216 */
+ICD_ENTRY(Flush) /* 217 */
+ICD_ENTRY(PopAttrib) /* 218 */
+ICD_ENTRY(PushAttrib) /* 219 */
+ICD_ENTRY(Map1d) /* 220 */
+ICD_ENTRY(Map1f) /* 221 */
+ICD_ENTRY(Map2d) /* 222 */
+ICD_ENTRY(Map2f) /* 223 */
+ICD_ENTRY(MapGrid1d) /* 224 */
+ICD_ENTRY(MapGrid1f) /* 225 */
+ICD_ENTRY(MapGrid2d) /* 226 */
+ICD_ENTRY(MapGrid2f) /* 227 */
+ICD_ENTRY(EvalCoord1d) /* 228 */
+ICD_ENTRY(EvalCoord1dv) /* 229 */
+ICD_ENTRY(EvalCoord1f) /* 230 */
+ICD_ENTRY(EvalCoord1fv) /* 231 */
+ICD_ENTRY(EvalCoord2d) /* 232 */
+ICD_ENTRY(EvalCoord2dv) /* 233 */
+ICD_ENTRY(EvalCoord2f) /* 234 */
+ICD_ENTRY(EvalCoord2fv) /* 235 */
+ICD_ENTRY(EvalMesh1) /* 236 */
+ICD_ENTRY(EvalPoint1) /* 237 */
+ICD_ENTRY(EvalMesh2) /* 238 */
+ICD_ENTRY(EvalPoint2) /* 239 */
+ICD_ENTRY(AlphaFunc) /* 240 */
+ICD_ENTRY(BlendFunc) /* 241 */
+ICD_ENTRY(LogicOp) /* 242 */
+ICD_ENTRY(StencilFunc) /* 243 */
+ICD_ENTRY(StencilOp) /* 244 */
+ICD_ENTRY(DepthFunc) /* 245 */
+ICD_ENTRY(PixelZoom) /* 246 */
+ICD_ENTRY(PixelTransferf) /* 247 */
+ICD_ENTRY(PixelTransferi) /* 248 */
+ICD_ENTRY(PixelStoref) /* 249 */
+ICD_ENTRY(PixelStorei) /* 250 */
+ICD_ENTRY(PixelMapfv) /* 251 */
+ICD_ENTRY(PixelMapuiv) /* 252 */
+ICD_ENTRY(PixelMapusv) /* 253 */
+ICD_ENTRY(ReadBuffer) /* 254 */
+ICD_ENTRY(CopyPixels) /* 255 */
+ICD_ENTRY(ReadPixels) /* 256 */
+ICD_ENTRY(DrawPixels) /* 257 */
+ICD_ENTRY(GetBooleanv) /* 258 */
+ICD_ENTRY(GetClipPlane) /* 259 */
+ICD_ENTRY(GetDoublev) /* 260 */
+ICD_ENTRY(GetError) /* 261 */
+ICD_ENTRY(GetFloatv) /* 262 */
+ICD_ENTRY(GetIntegerv) /* 263 */
+ICD_ENTRY(GetLightfv) /* 264 */
+ICD_ENTRY(GetLightiv) /* 265 */
+ICD_ENTRY(GetMapdv) /* 266 */
+ICD_ENTRY(GetMapfv) /* 267 */
+ICD_ENTRY(GetMapiv) /* 268 */
+ICD_ENTRY(GetMaterialfv) /* 269 */
+ICD_ENTRY(GetMaterialiv) /* 270 */
+ICD_ENTRY(GetPixelMapfv) /* 271 */
+ICD_ENTRY(GetPixelMapuiv) /* 272 */
+ICD_ENTRY(GetPixelMapusv) /* 273 */
+ICD_ENTRY(GetPolygonStipple) /* 274 */
+ICD_ENTRY(GetString) /* 275 */
+ICD_ENTRY(GetTexEnvfv) /* 276 */
+ICD_ENTRY(GetTexEnviv) /* 277 */
+ICD_ENTRY(GetTexGendv) /* 278 */
+ICD_ENTRY(GetTexGenfv) /* 279 */
+ICD_ENTRY(GetTexGeniv) /* 280 */
+ICD_ENTRY(GetTexImage) /* 281 */
+ICD_ENTRY(GetTexParameterfv) /* 282 */
+ICD_ENTRY(GetTexParameteriv) /* 283 */
+ICD_ENTRY(GetTexLevelParameterfv) /* 284 */
+ICD_ENTRY(GetTexLevelParameteriv) /* 285 */
+ICD_ENTRY(IsEnabled) /* 286 */
+ICD_ENTRY(IsList) /* 287 */
+ICD_ENTRY(DepthRange) /* 288 */
+ICD_ENTRY(Frustum) /* 289 */
+ICD_ENTRY(LoadIdentity) /* 290 */
+ICD_ENTRY(LoadMatrixf) /* 291 */
+ICD_ENTRY(LoadMatrixd) /* 292 */
+ICD_ENTRY(MatrixMode) /* 293 */
+ICD_ENTRY(MultMatrixf) /* 294 */
+ICD_ENTRY(MultMatrixd) /* 295 */
+ICD_ENTRY(Ortho) /* 296 */
+ICD_ENTRY(PopMatrix) /* 297 */
+ICD_ENTRY(PushMatrix) /* 298 */
+ICD_ENTRY(Rotated) /* 299 */
+ICD_ENTRY(Rotatef) /* 300 */
+ICD_ENTRY(Scaled) /* 301 */
+ICD_ENTRY(Scalef) /* 302 */
+ICD_ENTRY(Translated) /* 303 */
+ICD_ENTRY(Translatef) /* 304 */
+ICD_ENTRY(Viewport) /* 305 */
+ICD_ENTRY(ArrayElement) /* 306 */
+ICD_ENTRY(BindTexture) /* 307 */
+ICD_ENTRY(ColorPointer) /* 308 */
+ICD_ENTRY(DisableClientState) /* 309 */
+ICD_ENTRY(DrawArrays) /* 310 */
+ICD_ENTRY(DrawElements) /* 311 */
+ICD_ENTRY(EdgeFlagPointer) /* 312 */
+ICD_ENTRY(EnableClientState) /* 313 */
+ICD_ENTRY(IndexPointer) /* 314 */
+ICD_ENTRY(Indexub) /* 315 */
+ICD_ENTRY(Indexubv) /* 316 */
+ICD_ENTRY(InterleavedArrays) /* 317 */
+ICD_ENTRY(NormalPointer) /* 318 */
+ICD_ENTRY(PolygonOffset) /* 319 */
+ICD_ENTRY(TexCoordPointer) /* 320 */
+ICD_ENTRY(VertexPointer) /* 321 */
+ICD_ENTRY(AreTexturesResident) /* 322 */
+ICD_ENTRY(CopyTexImage1D) /* 323 */
+ICD_ENTRY(CopyTexImage2D) /* 324 */
+ICD_ENTRY(CopyTexSubImage1D) /* 325 */
+ICD_ENTRY(CopyTexSubImage2D) /* 326 */
+ICD_ENTRY(DeleteTextures) /* 327 */
+ICD_ENTRY(GenTextures) /* 328 */
+ICD_ENTRY(GetPointerv) /* 329 */
+ICD_ENTRY(IsTexture) /* 330 */
+ICD_ENTRY(PrioritizeTextures) /* 331 */
+ICD_ENTRY(TexSubImage1D) /* 332 */
+ICD_ENTRY(TexSubImage2D) /* 333 */
+ICD_ENTRY(PopClientAttrib) /* 334 */
+ICD_ENTRY(PushClientAttrib) /* 335 */
diff --git a/src/VBox/Additions/common/crOpenGL/alias_exports.py b/src/VBox/Additions/common/crOpenGL/alias_exports.py
new file mode 100644
index 00000000..5d16139a
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/alias_exports.py
@@ -0,0 +1,168 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+aliases = [
+ # GL_ARB_multitexture / OpenGL 1.2.1
+ ('ActiveTexture', 'ActiveTextureARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('ClientActiveTexture', 'ClientActiveTextureARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1d', 'MultiTexCoord1dARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1dv','MultiTexCoord1dvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1f', 'MultiTexCoord1fARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1fv','MultiTexCoord1fvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1i', 'MultiTexCoord1iARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1iv','MultiTexCoord1ivARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1s', 'MultiTexCoord1sARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord1sv','MultiTexCoord1svARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2d', 'MultiTexCoord2dARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2dv','MultiTexCoord2dvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2f', 'MultiTexCoord2fARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2fv','MultiTexCoord2fvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2i', 'MultiTexCoord2iARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2iv','MultiTexCoord2ivARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2s', 'MultiTexCoord2sARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord2sv','MultiTexCoord2svARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3d', 'MultiTexCoord3dARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3dv','MultiTexCoord3dvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3f', 'MultiTexCoord3fARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3fv','MultiTexCoord3fvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3i', 'MultiTexCoord3iARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3iv','MultiTexCoord3ivARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3s', 'MultiTexCoord3sARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord3sv','MultiTexCoord3svARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4d', 'MultiTexCoord4dARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4dv','MultiTexCoord4dvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4f', 'MultiTexCoord4fARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4fv','MultiTexCoord4fvARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4i', 'MultiTexCoord4iARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4iv','MultiTexCoord4ivARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4s', 'MultiTexCoord4sARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ ('MultiTexCoord4sv','MultiTexCoord4svARB', 'ARB_multitexture', 'CR_OPENGL_VERSION_1_2_1'),
+ # GL_ARB_transpose_matrix / OpenGL 1.3
+ ('LoadTransposeMatrixf','LoadTransposeMatrixfARB', 'ARB_transpose_matrix', 'CR_OPENGL_VERSION_1_3'),
+ ('LoadTransposeMatrixd','LoadTransposeMatrixdARB', 'ARB_transpose_matrix', 'CR_OPENGL_VERSION_1_3'),
+ ('MultTransposeMatrixf','MultTransposeMatrixfARB', 'ARB_transpose_matrix', 'CR_OPENGL_VERSION_1_3'),
+ ('MultTransposeMatrixd','MultTransposeMatrixdARB', 'ARB_transpose_matrix', 'CR_OPENGL_VERSION_1_3'),
+ # GL_ARB_texture_compression / OpenGL 1.3
+ ('CompressedTexImage3D', 'CompressedTexImage3DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('CompressedTexImage2D', 'CompressedTexImage2DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('CompressedTexImage1D', 'CompressedTexImage1DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('CompressedTexSubImage3D', 'CompressedTexSubImage3DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('CompressedTexSubImage2D', 'CompressedTexSubImage2DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('CompressedTexSubImage1D', 'CompressedTexSubImage1DARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ ('GetCompressedTexImage', 'GetCompressedTexImageARB', 'ARB_texture_compression', 'CR_OPENGL_VERSION_1_3'),
+ # GL_ARB_multisample / OpenGL 1.3
+ ('SampleCoverage', 'SampleCoverageARB', 'ARB_multisample', 'CR_OPENGL_VERSION_1_3'),
+ # GL_ARB_window_pos / OpenGL 1.4
+ ('WindowPos2d', 'WindowPos2dARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+
+ ('WindowPos2dv', 'WindowPos2dvARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2f', 'WindowPos2fARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2fv', 'WindowPos2fvARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2i', 'WindowPos2iARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2iv', 'WindowPos2ivARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2s', 'WindowPos2sARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos2sv', 'WindowPos2svARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3d', 'WindowPos3dARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3dv', 'WindowPos3dvARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3f', 'WindowPos3fARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3fv', 'WindowPos3fvARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3i', 'WindowPos3iARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3iv', 'WindowPos3ivARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3s', 'WindowPos3sARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ ('WindowPos3sv', 'WindowPos3svARB', 'ARB_window_pos', 'CR_OPENGL_VERSION_1_4'),
+ # GL_ARB_point_parameters / OpenGL 1.4
+ ('PointParameterf', 'PointParameterfARB', 'ARB_point_parameters', 'CR_OPENGL_VERSION_1_4'),
+ ('PointParameterfv', 'PointParameterfvARB', 'ARB_point_parameters', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_blend_color / OpenGL 1.4
+ ('BlendColor', 'BlendColorEXT', 'EXT_blend_color', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_blend_func_separate / OpenGL 1.4
+ ('BlendFuncSeparate', 'BlendFuncSeparateEXT', 'EXT_blend_func_separate', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_blend_equation / OpenGL 1.4
+ ('BlendEquation', 'BlendEquationEXT', 'EXT_blend_equation', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_multi_draw_arrays / OpenGL 1.4
+ ('MultiDrawArrays', 'MultiDrawArraysEXT', 'EXT_multi_draw_arrays', 'CR_OPENGL_VERSION_1_4'),
+ ('MultiDrawElements', 'MultiDrawElementsEXT', 'EXT_multi_draw_arrays', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_secondary_color / OpenGL 1.4
+ ('SecondaryColor3b', 'SecondaryColor3bEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3bv', 'SecondaryColor3bvEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3d', 'SecondaryColor3dEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3dv', 'SecondaryColor3dvEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3f', 'SecondaryColor3fEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3fv', 'SecondaryColor3fvEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3i', 'SecondaryColor3iEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3iv', 'SecondaryColor3ivEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3s', 'SecondaryColor3sEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3sv', 'SecondaryColor3svEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3ub', 'SecondaryColor3ubEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3ubv', 'SecondaryColor3ubvEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3ui', 'SecondaryColor3uiEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3uiv', 'SecondaryColor3uivEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3us', 'SecondaryColor3usEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColor3usv', 'SecondaryColor3usvEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ ('SecondaryColorPointer', 'SecondaryColorPointerEXT', 'EXT_secondary_color', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_fog_coord / OpenGL 1.4
+ ('FogCoordf', 'FogCoordfEXT', 'EXT_fog_coord', 'CR_OPENGL_VERSION_1_4'),
+ ('FogCoordfv', 'FogCoordfvEXT', 'EXT_fog_coord', 'CR_OPENGL_VERSION_1_4'),
+ ('FogCoordd', 'FogCoorddEXT', 'EXT_fog_coord', 'CR_OPENGL_VERSION_1_4'),
+ ('FogCoorddv', 'FogCoorddvEXT', 'EXT_fog_coord', 'CR_OPENGL_VERSION_1_4'),
+ ('FogCoordPointer', 'FogCoordPointerEXT', 'EXT_fog_coord', 'CR_OPENGL_VERSION_1_4'),
+ # GL_EXT_texture_object / OpenGL 1.1
+ ('AreTexturesResidentEXT', 'AreTexturesResident', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ ('BindTextureEXT', 'BindTexture', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ ('DeleteTexturesEXT', 'DeleteTextures', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ ('GenTexturesEXT', 'GenTextures', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ ('IsTextureEXT', 'IsTexture', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ ('PrioritizeTexturesEXT', 'PrioritizeTextures', 'EXT_texture_object', 'CR_OPENGL_VERSION_1_1'),
+ # GL_EXT_texture3D / OpenGL 1.2
+ ('TexSubImage3DEXT', 'TexSubImage3D', 'EXT_texture3D', 'CR_OPENGL_VERSION_1_2'),
+
+ # GL_NV_vertex_program / GL_ARB_vertex_program
+ ('VertexAttrib1sNV', 'VertexAttrib1sARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib1svNV', 'VertexAttrib1svARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib1fNV', 'VertexAttrib1fARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib1fvNV', 'VertexAttrib1fvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib1dNV', 'VertexAttrib1dARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib1dvNV', 'VertexAttrib1dvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2sNV', 'VertexAttrib2sARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2svNV', 'VertexAttrib2svARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2fNV', 'VertexAttrib2fARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2fvNV', 'VertexAttrib2fvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2dNV', 'VertexAttrib2dARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib2dvNV', 'VertexAttrib2dvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3sNV', 'VertexAttrib3sARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3svNV', 'VertexAttrib3svARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3fNV', 'VertexAttrib3fARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3fvNV', 'VertexAttrib3fvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3dNV', 'VertexAttrib3dARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib3dvNV', 'VertexAttrib3dvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4sNV', 'VertexAttrib4sARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4svNV', 'VertexAttrib4svARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4fNV', 'VertexAttrib4fARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4fvNV', 'VertexAttrib4fvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4dNV', 'VertexAttrib4dARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4dvNV', 'VertexAttrib4dvARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4ubNV', 'VertexAttrib4NubARB', 'ARB_vertex_program', ''),
+ ('VertexAttrib4ubvNV', 'VertexAttrib4NubvARB', 'ARB_vertex_program', ''),
+ ('DeleteProgramsNV', 'DeleteProgramsARB', 'ARB_vertex_program', ''),
+ ('IsProgramNV', 'IsProgramARB', 'ARB_vertex_program', '')
+]
+
+def AliasMap( func_name ):
+ for (aliased_func_name, real_func_name, ext_define, gl_version) in aliases:
+ if real_func_name == func_name:
+ return aliased_func_name;
+ return None
+
+def ExtDefine( func_name ):
+ for (aliased_func_name, real_func_name, ext_define, gl_version) in aliases:
+ if real_func_name == func_name:
+ return ext_define;
+ return None
+
+def GLversion( func_name ):
+ for (aliased_func_name, real_func_name, ext_define, gl_version) in aliases:
+ if real_func_name == func_name:
+ return gl_version;
+ return None
diff --git a/src/VBox/Additions/common/crOpenGL/array/Makefile.kup b/src/VBox/Additions/common/crOpenGL/array/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/Makefile.kup
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu.c b/src/VBox/Additions/common/crOpenGL/array/arrayspu.c
new file mode 100644
index 00000000..da230ee5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu.c
@@ -0,0 +1,944 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include <stdio.h>
+#include "cr_spu.h"
+#include "cr_error.h"
+#include "state/cr_limits.h"
+#include "arrayspu.h"
+
+ArraySPU array_spu;
+
+#ifdef CHROMIUM_THREADSAFE
+CRmutex _ArrayMutex;
+#endif
+
+static void ARRAYSPU_APIENTRY arrayspu_ArrayElement( GLint index )
+{
+ const CRClientState *c = &(crStateGetCurrent()->client);
+ const CRVertexArrays *array = &(c->array);
+ const GLboolean vpEnabled = crStateGetCurrent()->program.vpEnabled;
+ unsigned char *p;
+ unsigned int unit, attr;
+
+ if (array->e.enabled)
+ {
+ p = array->e.p + index * array->e.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->e.buffer && array->e.buffer->data)
+ {
+ p = (unsigned char *)(array->e.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ array_spu.self.EdgeFlagv(p);
+ }
+
+ /*
+ * Vertex attribute arrays (GL_NV_vertex_program) have priority over
+ * the conventional vertex arrays.
+ */
+ if (vpEnabled)
+ {
+ for (attr = 1; attr < VERT_ATTRIB_MAX; attr++)
+ {
+ if (array->a[attr].enabled)
+ {
+ GLint *iPtr;
+ p = array->a[attr].p + index * array->a[attr].stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->a[attr].buffer && array->a[attr].buffer->data)
+ {
+ p = (unsigned char *)(array->a[attr].buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->a[attr].type)
+ {
+ case GL_SHORT:
+ switch (array->a[attr].size)
+ {
+ case 1: array_spu.self.VertexAttrib1svARB(attr, (GLshort *)p); break;
+ case 2: array_spu.self.VertexAttrib2svARB(attr, (GLshort *)p); break;
+ case 3: array_spu.self.VertexAttrib3svARB(attr, (GLshort *)p); break;
+ case 4: array_spu.self.VertexAttrib4svARB(attr, (GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ iPtr = (GLint *) p;
+ switch (array->a[attr].size)
+ {
+ case 1: array_spu.self.VertexAttrib1fARB(attr, p[0]); break;
+ case 2: array_spu.self.VertexAttrib2fARB(attr, p[0], p[1]); break;
+ case 3: array_spu.self.VertexAttrib3fARB(attr, p[0], p[1], p[2]); break;
+ case 4: array_spu.self.VertexAttrib4fARB(attr, p[0], p[1], p[2], p[3]); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (array->a[attr].size)
+ {
+ case 1: array_spu.self.VertexAttrib1fvARB(attr, (GLfloat *)p); break;
+ case 2: array_spu.self.VertexAttrib2fvARB(attr, (GLfloat *)p); break;
+ case 3: array_spu.self.VertexAttrib3fvARB(attr, (GLfloat *)p); break;
+ case 4: array_spu.self.VertexAttrib4fvARB(attr, (GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (array->a[attr].size)
+ {
+ case 1: array_spu.self.VertexAttrib1dvARB(attr, (GLdouble *)p); break;
+ case 2: array_spu.self.VertexAttrib2dvARB(attr, (GLdouble *)p); break;
+ case 3: array_spu.self.VertexAttrib3dvARB(attr, (GLdouble *)p); break;
+ case 4: array_spu.self.VertexAttrib4dvARB(attr, (GLdouble *)p); break;
+ }
+ break;
+ default:
+ crWarning("Bad datatype for vertex attribute [%d] array: 0x%x\n",
+ attr, array->a[attr].type);
+ }
+ }
+ }
+ }
+
+ /* Now do conventional arrays, unless overridden by generic arrays above */
+ for (unit = 0 ; unit < crStateGetCurrent()->limits.maxTextureUnits ; unit++)
+ {
+ if (array->t[unit].enabled && !(vpEnabled && array->a[VERT_ATTRIB_TEX0+unit].enabled))
+ {
+ p = array->t[unit].p + index * array->t[unit].stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->t[unit].buffer && array->t[unit].buffer->data)
+ {
+ p = (unsigned char *)(array->t[unit].buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->t[unit].type)
+ {
+ case GL_SHORT:
+ switch (array->t[unit].size)
+ {
+ case 1: array_spu.self.MultiTexCoord1svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 2: array_spu.self.MultiTexCoord2svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 3: array_spu.self.MultiTexCoord3svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 4: array_spu.self.MultiTexCoord4svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (array->t[unit].size)
+ {
+ case 1: array_spu.self.MultiTexCoord1ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 2: array_spu.self.MultiTexCoord2ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 3: array_spu.self.MultiTexCoord3ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 4: array_spu.self.MultiTexCoord4ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (array->t[unit].size)
+ {
+ case 1: array_spu.self.MultiTexCoord1fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 2: array_spu.self.MultiTexCoord2fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 3: array_spu.self.MultiTexCoord3fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 4: array_spu.self.MultiTexCoord4fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (array->t[unit].size)
+ {
+ case 1: array_spu.self.MultiTexCoord1dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 2: array_spu.self.MultiTexCoord2dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 3: array_spu.self.MultiTexCoord3dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 4: array_spu.self.MultiTexCoord4dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ }
+ break;
+ }
+ }
+ }
+ if (array->i.enabled)
+ {
+ p = array->i.p + index * array->i.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->i.buffer && array->i.buffer->data)
+ {
+ p = (unsigned char *)(array->i.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->i.type)
+ {
+ case GL_SHORT: array_spu.self.Indexsv((GLshort *)p); break;
+ case GL_INT: array_spu.self.Indexiv((GLint *)p); break;
+ case GL_FLOAT: array_spu.self.Indexfv((GLfloat *)p); break;
+ case GL_DOUBLE: array_spu.self.Indexdv((GLdouble *)p); break;
+ }
+ }
+ if (array->c.enabled && !(vpEnabled && array->a[VERT_ATTRIB_COLOR0].enabled))
+ {
+ p = array->c.p + index * array->c.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->c.buffer && array->c.buffer->data)
+ {
+ p = (unsigned char *)(array->c.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->c.type)
+ {
+ case GL_BYTE:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3bv((GLbyte *)p); break;
+ case 4: array_spu.self.Color4bv((GLbyte *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_BYTE:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3ubv((GLubyte *)p); break;
+ case 4: array_spu.self.Color4ubv((GLubyte *)p); break;
+ }
+ break;
+ case GL_SHORT:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3sv((GLshort *)p); break;
+ case 4: array_spu.self.Color4sv((GLshort *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3usv((GLushort *)p); break;
+ case 4: array_spu.self.Color4usv((GLushort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3iv((GLint *)p); break;
+ case 4: array_spu.self.Color4iv((GLint *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3uiv((GLuint *)p); break;
+ case 4: array_spu.self.Color4uiv((GLuint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3fv((GLfloat *)p); break;
+ case 4: array_spu.self.Color4fv((GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (array->c.size)
+ {
+ case 3: array_spu.self.Color3dv((GLdouble *)p); break;
+ case 4: array_spu.self.Color4dv((GLdouble *)p); break;
+ }
+ break;
+ }
+ }
+ if (array->n.enabled && !(vpEnabled && array->a[VERT_ATTRIB_NORMAL].enabled))
+ {
+ p = array->n.p + index * array->n.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->n.buffer && array->n.buffer->data)
+ {
+ p = (unsigned char *)(array->n.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->n.type)
+ {
+ case GL_BYTE: array_spu.self.Normal3bv((GLbyte *)p); break;
+ case GL_SHORT: array_spu.self.Normal3sv((GLshort *)p); break;
+ case GL_INT: array_spu.self.Normal3iv((GLint *)p); break;
+ case GL_FLOAT: array_spu.self.Normal3fv((GLfloat *)p); break;
+ case GL_DOUBLE: array_spu.self.Normal3dv((GLdouble *)p); break;
+ }
+ }
+#ifdef CR_EXT_secondary_color
+ if (array->s.enabled && !(vpEnabled && array->a[VERT_ATTRIB_COLOR1].enabled))
+ {
+ p = array->s.p + index * array->s.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->s.buffer && array->s.buffer->data)
+ {
+ p = (unsigned char *)(array->s.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->s.type)
+ {
+ case GL_BYTE:
+ array_spu.self.SecondaryColor3bvEXT((GLbyte *)p); break;
+ case GL_UNSIGNED_BYTE:
+ array_spu.self.SecondaryColor3ubvEXT((GLubyte *)p); break;
+ case GL_SHORT:
+ array_spu.self.SecondaryColor3svEXT((GLshort *)p); break;
+ case GL_UNSIGNED_SHORT:
+ array_spu.self.SecondaryColor3usvEXT((GLushort *)p); break;
+ case GL_INT:
+ array_spu.self.SecondaryColor3ivEXT((GLint *)p); break;
+ case GL_UNSIGNED_INT:
+ array_spu.self.SecondaryColor3uivEXT((GLuint *)p); break;
+ case GL_FLOAT:
+ array_spu.self.SecondaryColor3fvEXT((GLfloat *)p); break;
+ case GL_DOUBLE:
+ array_spu.self.SecondaryColor3dvEXT((GLdouble *)p); break;
+ }
+ }
+#endif // CR_EXT_secondary_color
+#ifdef CR_EXT_fog_coord
+ if (array->f.enabled && !(vpEnabled && array->a[VERT_ATTRIB_FOG].enabled))
+ {
+ p = array->f.p + index * array->f.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->f.buffer && array->f.buffer->data)
+ {
+ p = (unsigned char *)(array->f.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ array_spu.self.FogCoordfEXT( *((GLfloat *) p) );
+ }
+#endif // CR_EXT_fog_coord
+
+ /* Need to do attrib[0] / vertex position last */
+ if (array->a[VERT_ATTRIB_POS].enabled) {
+ GLint *iPtr;
+ p = array->a[VERT_ATTRIB_POS].p + index * array->a[VERT_ATTRIB_POS].stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->a[VERT_ATTRIB_POS].buffer && array->a[VERT_ATTRIB_POS].buffer->data)
+ {
+ p = (unsigned char *)(array->a[VERT_ATTRIB_POS].buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->a[VERT_ATTRIB_POS].type)
+ {
+ case GL_SHORT:
+ switch (array->a[VERT_ATTRIB_POS].size)
+ {
+ case 1: array_spu.self.VertexAttrib1svARB(0, (GLshort *)p); break;
+ case 2: array_spu.self.VertexAttrib2svARB(0, (GLshort *)p); break;
+ case 3: array_spu.self.VertexAttrib3svARB(0, (GLshort *)p); break;
+ case 4: array_spu.self.VertexAttrib4svARB(0, (GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ iPtr = (GLint *) p;
+ switch (array->a[VERT_ATTRIB_POS].size)
+ {
+ case 1: array_spu.self.VertexAttrib1fARB(0, p[0]); break;
+ case 2: array_spu.self.VertexAttrib2fARB(0, p[0], p[1]); break;
+ case 3: array_spu.self.VertexAttrib3fARB(0, p[0], p[1], p[2]); break;
+ case 4: array_spu.self.VertexAttrib4fARB(0, p[0], p[1], p[2], p[3]); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (array->a[VERT_ATTRIB_POS].size)
+ {
+ case 1: array_spu.self.VertexAttrib1fvARB(0, (GLfloat *)p); break;
+ case 2: array_spu.self.VertexAttrib2fvARB(0, (GLfloat *)p); break;
+ case 3: array_spu.self.VertexAttrib3fvARB(0, (GLfloat *)p); break;
+ case 4: array_spu.self.VertexAttrib4fvARB(0, (GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (array->a[VERT_ATTRIB_POS].size)
+ {
+ case 1: array_spu.self.VertexAttrib1dvARB(0, (GLdouble *)p); break;
+ case 2: array_spu.self.VertexAttrib2dvARB(0, (GLdouble *)p); break;
+ case 3: array_spu.self.VertexAttrib3dvARB(0, (GLdouble *)p); break;
+ case 4: array_spu.self.VertexAttrib4dvARB(0, (GLdouble *)p); break;
+ }
+ break;
+ default:
+ crWarning("Bad datatype for vertex attribute [0] array: 0x%x\n", array->a[0].type);
+ }
+ }
+ else if (array->v.enabled)
+ {
+ p = array->v.p + index * array->v.stride;
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (array->v.buffer && array->v.buffer->data)
+ {
+ p = (unsigned char *)(array->v.buffer->data) + (unsigned long)p;
+ }
+#endif
+
+ switch (array->v.type)
+ {
+ case GL_SHORT:
+ switch (array->v.size)
+ {
+ case 2: array_spu.self.Vertex2sv((GLshort *)p); break;
+ case 3: array_spu.self.Vertex3sv((GLshort *)p); break;
+ case 4: array_spu.self.Vertex4sv((GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (array->v.size)
+ {
+ case 2: array_spu.self.Vertex2iv((GLint *)p); break;
+ case 3: array_spu.self.Vertex3iv((GLint *)p); break;
+ case 4: array_spu.self.Vertex4iv((GLint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (array->v.size)
+ {
+ case 2: array_spu.self.Vertex2fv((GLfloat *)p); break;
+ case 3: array_spu.self.Vertex3fv((GLfloat *)p); break;
+ case 4: array_spu.self.Vertex4fv((GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (array->v.size)
+ {
+ case 2: array_spu.self.Vertex2dv((GLdouble *)p); break;
+ case 3: array_spu.self.Vertex3dv((GLdouble *)p); break;
+ case 4: array_spu.self.Vertex4dv((GLdouble *)p); break;
+ }
+ break;
+ default:
+ crWarning("Bad datatype for vertex array: 0x%x\n", array->v.type);
+ }
+ }
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_DrawArrays(GLenum mode, GLint first, GLsizei count)
+{
+ int i;
+
+ if (count < 0)
+ {
+ crError("array_spu.self.DrawArrays passed negative count: %d", count);
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crError("array_spu.self.DrawArrays called with invalid mode: %d", mode);
+ }
+
+ array_spu.self.Begin(mode);
+ for (i=0; i<count; i++)
+ {
+ array_spu.self.ArrayElement(first++);
+ }
+ array_spu.self.End();
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_DrawElements(GLenum mode, GLsizei count,
+ GLenum type, const GLvoid *indices)
+{
+ int i;
+ GLubyte *p = (GLubyte *)indices;
+#ifdef CR_ARB_vertex_buffer_object
+ CRBufferObject *elementsBuffer = crStateGetCurrent()->bufferobject.elementsBuffer;
+#endif
+
+ if (count < 0)
+ {
+ crError("array_spu.self.DrawElements passed negative count: %d", count);
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crError("array_spu.self.DrawElements called with invalid mode: %d", mode);
+ }
+
+ if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT)
+ {
+ crError("array_spu.self.DrawElements called with invalid type: %d", type);
+ }
+
+#ifdef CR_ARB_vertex_buffer_object
+ if (elementsBuffer && elementsBuffer->data)
+ {
+ p = (unsigned char *)(elementsBuffer->data) + (unsigned long)p;
+ }
+#endif
+ //crDebug("arrayspu_DrawElements mode:0x%x, count:%d, type:0x%x", mode, count, type);
+
+
+ array_spu.self.Begin(mode);
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ for (i=0; i<count; i++)
+ {
+ array_spu.self.ArrayElement((GLint) *p++);
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ for (i=0; i<count; i++)
+ {
+ array_spu.self.ArrayElement((GLint) * (GLushort *) p);
+ p+=sizeof (GLushort);
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ for (i=0; i<count; i++)
+ {
+ array_spu.self.ArrayElement((GLint) * (GLuint *) p);
+ p+=sizeof (GLuint);
+ }
+ break;
+ default:
+ crError( "this can't happen: array_spu.self.DrawElements" );
+ break;
+ }
+ array_spu.self.End();
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)
+{
+ if (start>end)
+ {
+ crError("array_spu.self.arrayspu_DrawRangeElements start>end (%d>%d)", start, end);
+ }
+
+ arrayspu_DrawElements(mode, count, type, indices);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_ColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateColorPointer(size, type, stride, pointer);
+ array_spu.child.ColorPointer(size, type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_SecondaryColorPointerEXT( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateSecondaryColorPointerEXT(size, type, stride, pointer);
+ array_spu.child.SecondaryColorPointerEXT(size, type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_VertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateVertexPointer(size, type, stride, pointer);
+ array_spu.child.VertexPointer(size, type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_TexCoordPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateTexCoordPointer(size, type, stride, pointer);
+ array_spu.child.TexCoordPointer(size, type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_NormalPointer( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateNormalPointer(type, stride, pointer);
+ array_spu.child.NormalPointer(type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_IndexPointer( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateIndexPointer(type, stride, pointer);
+ array_spu.child.IndexPointer(type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_EdgeFlagPointer( GLsizei stride, const GLvoid *pointer )
+{
+ crStateEdgeFlagPointer(stride, pointer);
+ array_spu.child.EdgeFlagPointer(stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_VertexAttribPointerNV( GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer )
+{
+ crStateVertexAttribPointerNV(index, size, type, stride, pointer);
+ array_spu.child.VertexAttribPointerNV(index, size, type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_FogCoordPointerEXT( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+ crStateFogCoordPointerEXT(type, stride, pointer);
+ array_spu.child.FogCoordPointerEXT(type, stride, pointer);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_GetPointerv( GLenum pname, GLvoid **params )
+{
+ crStateGetPointerv(pname, params);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_EnableClientState( GLenum array )
+{
+ crStateEnableClientState(array);
+ array_spu.child.EnableClientState(array);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_DisableClientState( GLenum array )
+{
+ crStateDisableClientState(array);
+ array_spu.child.DisableClientState(array);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_ClientActiveTextureARB( GLenum texture )
+{
+ crStateClientActiveTextureARB(texture);
+ array_spu.child.ClientActiveTextureARB(texture);
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_MultiDrawArraysEXT(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount)
+{
+ int i;
+
+ if (primcount < 0)
+ {
+ crError("array_spu.self.MultiDrawArraysEXT passed negative count: %d", primcount);
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crError("array_spu.self.MultiDrawArraysEXT called with invalid mode: %d", mode);
+ }
+
+ for (i = 0; i < primcount; i++)
+ {
+ array_spu.self.DrawArrays(mode, first[i], count[i]);
+ }
+}
+
+static void ARRAYSPU_APIENTRY arrayspu_MultiDrawElementsEXT(GLenum mode, GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount)
+{
+ int i;
+
+ if (primcount < 0)
+ {
+ crError("array_spu.self.MultiDrawElementsEXT passed negative count: %d", primcount);
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crError("array_spu.self.MultiDrawElementsEXT called with invalid mode: %d", mode);
+ }
+
+ if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT)
+ {
+ crError("array_spu.self.MultiDrawElementsEXT called with invalid type: %d", type);
+ }
+
+ for (i = 0; i < primcount; i++)
+ {
+ array_spu.self.DrawElements(mode, count[i], type, indices[i]);
+ }
+}
+
+/*
+ * We need to know when vertex program mode is enabled/disabled
+ * in order to handle vertex attribute arrays correctly.
+ */
+static void ARRAYSPU_APIENTRY arrayspu_Enable(GLenum cap)
+{
+ if (cap == GL_VERTEX_PROGRAM_NV) {
+ crStateGetCurrent()->program.vpEnabled = GL_TRUE;
+ }
+ array_spu.child.Enable(cap);
+}
+
+
+static void ARRAYSPU_APIENTRY arrayspu_Disable(GLenum cap)
+{
+ if (cap == GL_VERTEX_PROGRAM_NV) {
+ crStateGetCurrent()->program.vpEnabled = GL_FALSE;
+ }
+ array_spu.child.Disable(cap);
+}
+
+/** @todo it's a hack, as GLSL shouldn't blindly reuse this bit from nv_vertex_program*/
+static void ARRAYSPU_APIENTRY arrayspu_UseProgram(GLuint program)
+{
+ crStateGetCurrent()->program.vpEnabled = program>0;
+ array_spu.child.UseProgram(program);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_VertexAttribPointerARB(GLuint index, GLint size, GLenum type,
+ GLboolean normalized, GLsizei stride,
+ const GLvoid *pointer)
+{
+ crStateVertexAttribPointerARB(index, size, type, normalized, stride, pointer);
+ array_spu.child.VertexAttribPointerARB(index, size, type, normalized, stride, pointer);
+}
+
+
+static void ARRAYSPU_APIENTRY
+arrayspu_EnableVertexAttribArrayARB(GLuint index)
+{
+ crStateEnableVertexAttribArrayARB(index);
+}
+
+
+static void ARRAYSPU_APIENTRY
+arrayspu_DisableVertexAttribArrayARB(GLuint index)
+{
+ crStateDisableVertexAttribArrayARB(index);
+}
+
+
+/* We need to implement Push/PopClientAttrib here so that _our_ state
+ * tracker gets used. Also, pass the call onto the next SPU (in case
+ * it's the GL_CLIENT_PIXEL_STORE_BIT, etc).
+ */
+static void ARRAYSPU_APIENTRY
+arrayspu_PushClientAttrib( GLbitfield mask )
+{
+ crStatePushClientAttrib(mask);
+ array_spu.child.PushClientAttrib(mask);
+}
+
+
+static void ARRAYSPU_APIENTRY
+arrayspu_PopClientAttrib( void )
+{
+ crStatePopClientAttrib();
+ array_spu.child.PopClientAttrib();
+}
+
+
+static void ARRAYSPU_APIENTRY
+arrayspu_GenBuffersARB( GLsizei n, GLuint * buffers )
+{
+ array_spu.child.GenBuffersARB(n, buffers);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_DeleteBuffersARB( GLsizei n, const GLuint *buffers )
+{
+ crStateDeleteBuffersARB(n, buffers);
+ array_spu.child.DeleteBuffersARB(n, buffers);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_BindBufferARB( GLenum target, GLuint buffer )
+{
+ crStateBindBufferARB(target, buffer);
+ array_spu.child.BindBufferARB(target, buffer);
+}
+
+static GLboolean ARRAYSPU_APIENTRY
+arrayspu_IsBufferARB (GLuint buffer)
+{
+ return array_spu.child.IsBufferARB(buffer);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_BufferDataARB( GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage )
+{
+ crStateBufferDataARB(target, size, data, usage);
+ array_spu.child.BufferDataARB(target, size, data, usage);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_BufferSubDataARB( GLenum target, GLintptrARB offset,
+ GLsizeiptrARB size, const GLvoid * data )
+{
+ crStateBufferSubDataARB(target, offset, size, data);
+ array_spu.child.BufferSubDataARB(target, offset, size, data);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_GetBufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data)
+{
+ crStateGetBufferSubDataARB(target, offset, size, data);
+}
+
+static void * ARRAYSPU_APIENTRY
+arrayspu_MapBufferARB(GLenum target, GLenum access)
+{
+ return crStateMapBufferARB(target, access);
+}
+
+static GLboolean ARRAYSPU_APIENTRY
+arrayspu_UnmapBufferARB(GLenum target)
+{
+ crStateUnmapBufferARB(target);
+ return array_spu.child.UnmapBufferARB(target);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
+{
+ crStateGetBufferParameterivARB(target, pname, params);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
+{
+ crStateGetBufferPointervARB(target, pname, params);
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_InterleavedArrays(GLenum format, GLsizei stride, const GLvoid *p)
+{
+ crStateInterleavedArrays(format, stride, p);
+}
+
+static GLint ARRAYSPU_APIENTRY
+arrayspu_CreateContext( const char *dpyName, GLint visual, GLint shareCtx )
+{
+ GLint ctx, slot;
+
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_ArrayMutex);
+#endif
+
+ ctx = array_spu.child.CreateContext(dpyName, visual, shareCtx);
+
+ /* find an empty context slot */
+ for (slot = 0; slot < array_spu.numContexts; slot++) {
+ if (!array_spu.context[slot].clientState) {
+ /* found empty slot */
+ break;
+ }
+ }
+ if (slot == array_spu.numContexts) {
+ array_spu.numContexts++;
+ }
+
+ array_spu.context[slot].clientState = crStateCreateContext(NULL, visual, NULL);
+ array_spu.context[slot].clientCtx = ctx;
+#ifdef CR_ARB_vertex_buffer_object
+ array_spu.context[slot].clientState->bufferobject.retainBufferData = GL_TRUE;
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_ArrayMutex);
+#endif
+
+ return ctx;
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_MakeCurrent( GLint window, GLint nativeWindow, GLint ctx )
+{
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_ArrayMutex);
+#endif
+ array_spu.child.MakeCurrent(window, nativeWindow, ctx);
+
+ if (ctx) {
+ int slot;
+
+ for (slot=0; slot<array_spu.numContexts; ++slot)
+ if (array_spu.context[slot].clientCtx == ctx) break;
+ CRASSERT(slot < array_spu.numContexts);
+
+ crStateMakeCurrent(array_spu.context[slot].clientState);
+ }
+ else
+ {
+ crStateMakeCurrent(NULL);
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_ArrayMutex);
+#endif
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_DestroyContext( GLint ctx )
+{
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_ArrayMutex);
+#endif
+ array_spu.child.DestroyContext(ctx);
+
+ if (ctx) {
+ int slot;
+
+ for (slot=0; slot<array_spu.numContexts; ++slot)
+ if (array_spu.context[slot].clientCtx == ctx) break;
+ CRASSERT(slot < array_spu.numContexts);
+
+ crStateDestroyContext(array_spu.context[slot].clientState);
+
+ array_spu.context[slot].clientState = NULL;
+ array_spu.context[slot].clientCtx = 0;
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_ArrayMutex);
+#endif
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_VBoxAttachThread(void)
+{
+ crStateVBoxAttachThread();
+ array_spu.child.VBoxAttachThread();
+}
+
+static void ARRAYSPU_APIENTRY
+arrayspu_VBoxDetachThread(void)
+{
+ crStateVBoxDetachThread();
+ array_spu.child.VBoxDetachThread();
+}
+
+
+SPUNamedFunctionTable _cr_array_table[] = {
+ { "ArrayElement", (SPUGenericFunction) arrayspu_ArrayElement },
+ { "DrawArrays", (SPUGenericFunction) arrayspu_DrawArrays},
+ { "DrawElements", (SPUGenericFunction) arrayspu_DrawElements},
+ { "DrawRangeElements", (SPUGenericFunction) arrayspu_DrawRangeElements},
+ { "ColorPointer", (SPUGenericFunction) arrayspu_ColorPointer},
+ { "SecondaryColorPointerEXT", (SPUGenericFunction) arrayspu_SecondaryColorPointerEXT},
+ { "VertexPointer", (SPUGenericFunction) arrayspu_VertexPointer},
+ { "TexCoordPointer", (SPUGenericFunction) arrayspu_TexCoordPointer},
+ { "NormalPointer", (SPUGenericFunction) arrayspu_NormalPointer},
+ { "IndexPointer", (SPUGenericFunction) arrayspu_IndexPointer},
+ { "EdgeFlagPointer", (SPUGenericFunction) arrayspu_EdgeFlagPointer},
+ { "VertexAttribPointerNV", (SPUGenericFunction) arrayspu_VertexAttribPointerNV},
+ { "FogCoordPointerEXT", (SPUGenericFunction) arrayspu_FogCoordPointerEXT},
+ { "GetPointerv", (SPUGenericFunction) arrayspu_GetPointerv},
+ { "EnableClientState", (SPUGenericFunction) arrayspu_EnableClientState},
+ { "DisableClientState", (SPUGenericFunction) arrayspu_DisableClientState},
+ { "ClientActiveTextureARB", (SPUGenericFunction) arrayspu_ClientActiveTextureARB },
+ { "MultiDrawArraysEXT", (SPUGenericFunction) arrayspu_MultiDrawArraysEXT },
+ { "MultiDrawElementsEXT", (SPUGenericFunction) arrayspu_MultiDrawElementsEXT },
+ { "Enable", (SPUGenericFunction) arrayspu_Enable },
+ { "Disable", (SPUGenericFunction) arrayspu_Disable },
+ { "PushClientAttrib", (SPUGenericFunction) arrayspu_PushClientAttrib },
+ { "PopClientAttrib", (SPUGenericFunction) arrayspu_PopClientAttrib },
+ { "VertexAttribPointerARB", (SPUGenericFunction) arrayspu_VertexAttribPointerARB },
+ { "EnableVertexAttribArrayARB", (SPUGenericFunction) arrayspu_EnableVertexAttribArrayARB },
+ { "DisableVertexAttribArrayARB", (SPUGenericFunction) arrayspu_DisableVertexAttribArrayARB },
+ { "GenBuffersARB", (SPUGenericFunction) arrayspu_GenBuffersARB },
+ { "DeleteBuffersARB", (SPUGenericFunction) arrayspu_DeleteBuffersARB },
+ { "BindBufferARB", (SPUGenericFunction) arrayspu_BindBufferARB },
+ { "IsBufferARB", (SPUGenericFunction) arrayspu_IsBufferARB },
+ { "BufferDataARB", (SPUGenericFunction) arrayspu_BufferDataARB },
+ { "BufferSubDataARB", (SPUGenericFunction) arrayspu_BufferSubDataARB },
+ { "GetBufferSubDataARB", (SPUGenericFunction) arrayspu_GetBufferSubDataARB },
+ { "MapBufferARB", (SPUGenericFunction) arrayspu_MapBufferARB },
+ { "UnmapBufferARB", (SPUGenericFunction) arrayspu_UnmapBufferARB },
+ { "GetBufferParameterivARB", (SPUGenericFunction) arrayspu_GetBufferParameterivARB},
+ { "GetBufferPointervARB", (SPUGenericFunction) arrayspu_GetBufferPointervARB},
+ { "InterleavedArrays", (SPUGenericFunction) arrayspu_InterleavedArrays},
+ { "CreateContext", (SPUGenericFunction) arrayspu_CreateContext},
+ { "MakeCurrent", (SPUGenericFunction) arrayspu_MakeCurrent},
+ { "DestroyContext", (SPUGenericFunction) arrayspu_DestroyContext},
+ { "UseProgram", (SPUGenericFunction) arrayspu_UseProgram},
+ { "VBoxAttachThread", (SPUGenericFunction) arrayspu_VBoxAttachThread},
+ { "VBoxDetachThread", (SPUGenericFunction) arrayspu_VBoxDetachThread},
+ { NULL, NULL }
+};
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu.def b/src/VBox/Additions/common/crOpenGL/array/arrayspu.def
new file mode 100644
index 00000000..9edc7163
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu.def
@@ -0,0 +1,6 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu.h b/src/VBox/Additions/common/crOpenGL/array/arrayspu.h
new file mode 100644
index 00000000..f31dea5c
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_array_arrayspu_h
+#define GA_INCLUDED_SRC_common_crOpenGL_array_arrayspu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef WINDOWS
+#define ARRAYSPU_APIENTRY __stdcall
+#else
+#define ARRAYSPU_APIENTRY
+#endif
+
+#include "cr_spu.h"
+#include "cr_glstate.h"
+
+void arrayspuSetVBoxConfiguration( void );
+
+typedef struct context_info_t ContextInfo;
+
+struct context_info_t {
+ CRContext *clientState; /* used to store client-side GL state */
+ GLint clientCtx; /* client context ID */
+};
+
+typedef struct {
+ int id;
+ int has_child;
+ CRContext *defaultctx;
+ SPUDispatchTable self, child, super;
+ int numContexts;
+ ContextInfo context[CR_MAX_CONTEXTS];
+} ArraySPU;
+
+extern ArraySPU array_spu;
+
+#ifdef CHROMIUM_THREADSAFE
+extern CRmutex _ArrayMutex;
+#endif
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_array_arrayspu_h */
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu.rc b/src/VBox/Additions/common/crOpenGL/array/arrayspu.rc
new file mode 100644
index 00000000..894a6584
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu.rc
@@ -0,0 +1,69 @@
+/* $Id: arrayspu.rc $ */
+/** @file
+ * VBoxOGLarrayspu - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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_DRV
+ FILESUBTYPE VFT2_DRV_DISPLAY
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxOGLarrayspu\0"
+#ifdef VBOX_WDDM_WOW64
+ VALUE "OriginalFilename", "VBoxOGLarrayspu-x86.dll\0"
+#else
+ VALUE "OriginalFilename", "VBoxOGLarrayspu.dll\0"
+#endif
+ 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
+
+1 RCDATA
+BEGIN
+// Machine dependent parameters
+ 17, // Height of vertical thumb
+ 17, // Width of horizontal thumb
+ 2, // Icon horiz compression factor
+ 2, // Icon vert compression factor
+ 1, // Cursor horz compression factor
+ 1, // Cursor vert compression factor
+ 0, // Kanji window height
+ 1, // cxBorder (thickness of vertical lines)
+ 1 // cyBorder (thickness of horizontal lines)
+END
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu_config.c b/src/VBox/Additions/common/crOpenGL/array/arrayspu_config.c
new file mode 100644
index 00000000..d2161739
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu_config.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "arrayspu.h"
+
+#include "cr_string.h"
+
+#include <stdio.h>
+
+static void __setDefaults( void )
+{
+}
+
+/* No SPU options yet.
+ */
+SPUOptions arraySPUOptions[] = {
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+};
+
+
+void arrayspuSetVBoxConfiguration( void )
+{
+ __setDefaults();
+}
diff --git a/src/VBox/Additions/common/crOpenGL/array/arrayspu_init.c b/src/VBox/Additions/common/crOpenGL/array/arrayspu_init.c
new file mode 100644
index 00000000..bd773f59
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/array/arrayspu_init.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "arrayspu.h"
+#include "cr_mem.h"
+#include <stdio.h>
+
+extern SPUNamedFunctionTable _cr_array_table[];
+
+extern SPUOptions arraySPUOptions[];
+
+static SPUFunctions array_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_array_table /* THE ACTUAL FUNCTIONS */
+};
+
+static SPUFunctions *arraySPUInit( int id, SPU *child, SPU *self,
+ unsigned int context_id,
+ unsigned int num_contexts )
+{
+
+ (void) context_id;
+ (void) num_contexts;
+
+#ifdef CHROMIUM_THREADSAFE
+ crInitMutex(&_ArrayMutex);
+#endif
+
+ array_spu.id = id;
+ array_spu.has_child = 0;
+ if (child)
+ {
+ crSPUInitDispatchTable( &(array_spu.child) );
+ crSPUCopyDispatchTable( &(array_spu.child), &(child->dispatch_table) );
+ array_spu.has_child = 1;
+ }
+ crSPUInitDispatchTable( &(array_spu.super) );
+ crSPUCopyDispatchTable( &(array_spu.super), &(self->superSPU->dispatch_table) );
+ arrayspuSetVBoxConfiguration();
+
+ crStateInit();
+/** @todo seems default context ain't needed at all*/
+ array_spu.defaultctx = crStateCreateContext( NULL, 0, NULL );
+#ifdef CR_ARB_vertex_buffer_object
+ array_spu.defaultctx->bufferobject.retainBufferData = GL_TRUE;
+#endif
+ /* we call SetCurrent instead of MakeCurrent as the differencer
+ * isn't setup yet anyway */
+ crStateSetCurrent( array_spu.defaultctx );
+
+ array_spu.numContexts = 0;
+ crMemZero(array_spu.context, CR_MAX_CONTEXTS * sizeof(ContextInfo));
+
+ return &array_functions;
+}
+
+static void arraySPUSelfDispatch(SPUDispatchTable *self)
+{
+ crSPUInitDispatchTable( &(array_spu.self) );
+ crSPUCopyDispatchTable( &(array_spu.self), self );
+}
+
+static int arraySPUCleanup(void)
+{
+ return 1;
+}
+
+int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
+ SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
+ SPUOptionsPtr *options, int *flags )
+{
+ *name = "array";
+ *super = "passthrough";
+ *init = arraySPUInit;
+ *self = arraySPUSelfDispatch;
+ *cleanup = arraySPUCleanup;
+ *options = arraySPUOptions;
+ *flags = (SPU_NO_PACKER|SPU_NOT_TERMINAL|SPU_MAX_SERVERS_ZERO);
+
+ return 1;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/context.c b/src/VBox/Additions/common/crOpenGL/context.c
new file mode 100644
index 00000000..29b93aa1
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/context.c
@@ -0,0 +1,1463 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+/**
+ * \mainpage OpenGL_stub
+ *
+ * \section OpenGL_stubIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The OpenGL_stub module basically takes care of API dispatch,
+ * and OpenGL state management.
+ *
+ */
+
+/**
+ * This file manages OpenGL rendering contexts in the faker library.
+ * The big issue is switching between Chromium and native GL context
+ * management. This is where we support multiple client OpenGL
+ * windows. Typically, one window is handled by Chromium while any
+ * other windows are handled by the native OpenGL library.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_spu.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "cr_environment.h"
+#include "stub.h"
+
+/**
+ * This function should be called from MakeCurrent(). It'll detect if
+ * we're in a multi-thread situation, and do the right thing for dispatch.
+ */
+#ifdef CHROMIUM_THREADSAFE
+ static void
+stubCheckMultithread( void )
+{
+ static unsigned long knownID;
+ static GLboolean firstCall = GL_TRUE;
+
+ if (stub.threadSafe)
+ return; /* nothing new, nothing to do */
+
+ if (firstCall) {
+ knownID = crThreadID();
+ firstCall = GL_FALSE;
+ }
+ else if (knownID != crThreadID()) {
+ /* going thread-safe now! */
+ stub.threadSafe = GL_TRUE;
+ crSPUCopyDispatchTable(&glim, &stubThreadsafeDispatch);
+ }
+}
+#endif
+
+
+/**
+ * Install the given dispatch table as the table used for all gl* calls.
+ */
+ static void
+stubSetDispatch( SPUDispatchTable *table )
+{
+ CRASSERT(table);
+
+#ifdef CHROMIUM_THREADSAFE
+ /* always set the per-thread dispatch pointer */
+ crSetTSD(&stub.dispatchTSD, (void *) table);
+ if (stub.threadSafe) {
+ /* Do nothing - the thread-safe dispatch functions will call GetTSD()
+ * to get a pointer to the dispatch table, and jump through it.
+ */
+ }
+ else
+#endif
+ {
+ /* Single thread mode - just install the caller's dispatch table */
+ /* This conditional is an optimization to try to avoid unnecessary
+ * copying. It seems to work with atlantis, multiwin, etc. but
+ * _could_ be a problem. (Brian)
+ */
+ if (glim.copy_of != table->copy_of)
+ crSPUCopyDispatchTable(&glim, table);
+ }
+}
+
+void stubForcedFlush(GLint con)
+{
+#if 0
+ GLint buffer;
+ stub.spu->dispatch_table.GetIntegerv(GL_DRAW_BUFFER, &buffer);
+ stub.spu->dispatch_table.DrawBuffer(GL_FRONT);
+ stub.spu->dispatch_table.Flush();
+ stub.spu->dispatch_table.DrawBuffer(buffer);
+#else
+ if (con)
+ {
+ stub.spu->dispatch_table.VBoxConFlush(con);
+ }
+ else
+ {
+ stub.spu->dispatch_table.Flush();
+ }
+#endif
+}
+
+void stubConChromiumParameteriCR(GLint con, GLenum param, GLint value)
+{
+// if (con)
+ stub.spu->dispatch_table.VBoxConChromiumParameteriCR(con, param, value);
+// else
+// crError("VBoxConChromiumParameteriCR called with null connection");
+}
+
+void stubConChromiumParametervCR(GLint con, GLenum target, GLenum type, GLsizei count, const GLvoid *values)
+{
+// if (con)
+ stub.spu->dispatch_table.VBoxConChromiumParametervCR(con, target, type, count, values);
+// else
+// crError("VBoxConChromiumParameteriCR called with null connection");
+}
+
+void stubConFlush(GLint con)
+{
+ if (con)
+ stub.spu->dispatch_table.VBoxConFlush(con);
+ else
+ crError("stubConFlush called with null connection");
+}
+
+static void stubWindowCleanupForContextsCB(unsigned long key, void *data1, void *data2)
+{
+ ContextInfo *context = (ContextInfo *) data1;
+ RT_NOREF(key);
+
+ CRASSERT(context);
+
+ if (context->currentDrawable == data2)
+ context->currentDrawable = NULL;
+}
+
+void stubDestroyWindow( GLint con, GLint window )
+{
+ WindowInfo *winInfo = (WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo && winInfo->type == CHROMIUM && stub.spu)
+ {
+ crHashtableLock(stub.windowTable);
+
+ stub.spu->dispatch_table.VBoxWindowDestroy(con, winInfo->spuWindow );
+
+#ifdef WINDOWS
+ if (winInfo->hVisibleRegion != INVALID_HANDLE_VALUE)
+ {
+ DeleteObject(winInfo->hVisibleRegion);
+ }
+#elif defined(GLX)
+ if (winInfo->pVisibleRegions)
+ {
+ XFree(winInfo->pVisibleRegions);
+ }
+# ifdef CR_NEWWINTRACK
+ if (winInfo->syncDpy)
+ {
+ XCloseDisplay(winInfo->syncDpy);
+ }
+# endif
+#endif
+
+ stubForcedFlush(con);
+
+ crHashtableWalk(stub.contextTable, stubWindowCleanupForContextsCB, winInfo);
+
+ crHashtableDelete(stub.windowTable, window, crFree);
+
+ crHashtableUnlock(stub.windowTable);
+ }
+}
+
+/**
+ * Create a new _Chromium_ window, not GLX, WGL or CGL.
+ * Called by crWindowCreate() only.
+ */
+ GLint
+stubNewWindow( const char *dpyName, GLint visBits )
+{
+ WindowInfo *winInfo;
+ GLint spuWin, size[2];
+
+ spuWin = stub.spu->dispatch_table.WindowCreate( dpyName, visBits );
+ if (spuWin < 0) {
+ return -1;
+ }
+
+ winInfo = (WindowInfo *) crCalloc(sizeof(WindowInfo));
+ if (!winInfo) {
+ stub.spu->dispatch_table.WindowDestroy(spuWin);
+ return -1;
+ }
+
+ winInfo->type = CHROMIUM;
+
+ /* Ask the head SPU for the initial window size */
+ size[0] = size[1] = 0;
+ stub.spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, size);
+ if (size[0] == 0 && size[1] == 0) {
+ /* use some reasonable defaults */
+ size[0] = size[1] = 512;
+ }
+ winInfo->width = size[0];
+ winInfo->height = size[1];
+#ifdef VBOX_WITH_WDDM
+ if (stub.bRunningUnderWDDM)
+ {
+ crError("Should not be here: WindowCreate/Destroy & VBoxPackGetInjectID require connection id!");
+ winInfo->mapped = 0;
+ }
+ else
+#endif
+ {
+ winInfo->mapped = 1;
+ }
+
+ if (!dpyName)
+ dpyName = "";
+
+ crStrncpy(winInfo->dpyName, dpyName, MAX_DPY_NAME);
+ winInfo->dpyName[MAX_DPY_NAME-1] = 0;
+
+ /* Use spuWin as the hash table index and GLX/WGL handle */
+#ifdef WINDOWS
+ winInfo->drawable = (HDC) spuWin;
+ winInfo->hVisibleRegion = INVALID_HANDLE_VALUE;
+#elif defined(Darwin)
+ winInfo->drawable = (CGSWindowID) spuWin;
+#elif defined(GLX)
+ winInfo->drawable = (GLXDrawable) spuWin;
+ winInfo->pVisibleRegions = NULL;
+ winInfo->cVisibleRegions = 0;
+#endif
+#ifdef CR_NEWWINTRACK
+ winInfo->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(0);
+#endif
+ winInfo->spuWindow = spuWin;
+
+ crHashtableAdd(stub.windowTable, (unsigned int) spuWin, winInfo);
+
+ return spuWin;
+}
+
+#ifdef GLX
+# if 0 /* unused */
+static XErrorHandler oldErrorHandler;
+static unsigned char lastXError = Success;
+
+static int
+errorHandler (Display *dpy, XErrorEvent *e)
+{
+ RT_NOREF(dpy);
+
+ lastXError = e->error_code;
+ return 0;
+}
+# endif /* unused */
+#endif
+
+GLboolean
+stubIsWindowVisible(WindowInfo *win)
+{
+#if defined(WINDOWS)
+# ifdef VBOX_WITH_WDDM
+ if (stub.bRunningUnderWDDM)
+ return win->mapped;
+# endif
+ return GL_TRUE;
+#elif defined(Darwin)
+ return GL_TRUE;
+#elif defined(GLX)
+ Display *dpy = stubGetWindowDisplay(win);
+ if (dpy)
+ {
+ XWindowAttributes attr;
+ XLOCK(dpy);
+ XGetWindowAttributes(dpy, win->drawable, &attr);
+ XUNLOCK(dpy);
+
+ if (attr.map_state == IsUnmapped)
+ {
+ return GL_FALSE;
+ }
+# if 1
+ return GL_TRUE;
+# else
+ if (attr.override_redirect)
+ {
+ return GL_TRUE;
+ }
+
+ if (!stub.bXExtensionsChecked)
+ {
+ stubCheckXExtensions(win);
+ }
+
+ if (!stub.bHaveXComposite)
+ {
+ return GL_TRUE;
+ }
+ else
+ {
+ Pixmap p;
+
+ crLockMutex(&stub.mutex);
+
+ XLOCK(dpy);
+ XSync(dpy, false);
+ oldErrorHandler = XSetErrorHandler(errorHandler);
+ /** @todo this will create new pixmap for window every call*/
+ p = XCompositeNameWindowPixmap(dpy, win->drawable);
+ XSync(dpy, false);
+ XSetErrorHandler(oldErrorHandler);
+ XUNLOCK(dpy);
+
+ switch (lastXError)
+ {
+ case Success:
+ XFreePixmap(dpy, p);
+ crUnlockMutex(&stub.mutex);
+ return GL_FALSE;
+ break;
+ case BadMatch:
+ /*Window isn't redirected*/
+ lastXError = Success;
+ break;
+ default:
+ crWarning("Unexpected XError %i", (int)lastXError);
+ lastXError = Success;
+ }
+
+ crUnlockMutex(&stub.mutex);
+
+ return GL_TRUE;
+ }
+# endif
+ }
+ else {
+ /* probably created by crWindowCreate() */
+ return win->mapped;
+ }
+#endif
+}
+
+
+/**
+ * Given a Windows HDC or GLX Drawable, return the corresponding
+ * WindowInfo structure. Create a new one if needed.
+ */
+WindowInfo *
+#ifdef WINDOWS
+ stubGetWindowInfo( HDC drawable )
+#elif defined(Darwin)
+ stubGetWindowInfo( CGSWindowID drawable )
+#elif defined(GLX)
+stubGetWindowInfo( Display *dpy, GLXDrawable drawable )
+#endif
+{
+#ifndef WINDOWS
+ WindowInfo *winInfo = (WindowInfo *) crHashtableSearch(stub.windowTable, (unsigned int) drawable);
+#else
+ WindowInfo *winInfo;
+ HWND hwnd;
+ hwnd = WindowFromDC(drawable);
+
+ if (!hwnd)
+ {
+ return NULL;
+ }
+
+ winInfo = (WindowInfo *) crHashtableSearch(stub.windowTable, (unsigned int) hwnd);
+#endif
+ if (!winInfo) {
+ winInfo = (WindowInfo *) crCalloc(sizeof(WindowInfo));
+ if (!winInfo)
+ return NULL;
+#ifdef GLX
+ crStrncpy(winInfo->dpyName, DisplayString(dpy), MAX_DPY_NAME);
+ winInfo->dpyName[MAX_DPY_NAME-1] = 0;
+ winInfo->dpy = dpy;
+ winInfo->pVisibleRegions = NULL;
+#elif defined(Darwin)
+ winInfo->connection = _CGSDefaultConnection(); // store our connection as default
+#elif defined(WINDOWS)
+ winInfo->hVisibleRegion = INVALID_HANDLE_VALUE;
+ winInfo->hWnd = hwnd;
+#endif
+ winInfo->drawable = drawable;
+ winInfo->type = UNDECIDED;
+ winInfo->spuWindow = -1;
+#ifdef VBOX_WITH_WDDM
+ if (stub.bRunningUnderWDDM)
+ winInfo->mapped = 0;
+ else
+#endif
+ {
+ winInfo->mapped = -1; /* don't know */
+ }
+ winInfo->pOwner = NULL;
+#ifdef CR_NEWWINTRACK
+ winInfo->u32ClientID = -1;
+#endif
+#ifndef WINDOWS
+ crHashtableAdd(stub.windowTable, (unsigned int) drawable, winInfo);
+#else
+ crHashtableAdd(stub.windowTable, (unsigned int) hwnd, winInfo);
+#endif
+ }
+#ifdef WINDOWS
+ else
+ {
+ winInfo->drawable = drawable;
+ }
+#endif
+ return winInfo;
+}
+
+static void stubWindowCheckOwnerCB(unsigned long key, void *data1, void *data2);
+
+static void
+stubContextFree( ContextInfo *context )
+{
+ crMemZero(context, sizeof(ContextInfo)); /* just to be safe */
+ crFree(context);
+}
+
+static void
+stubDestroyContextLocked( ContextInfo *context )
+{
+ unsigned long contextId = context->id;
+ if (context->type == NATIVE) {
+#ifdef WINDOWS
+ stub.wsInterface.wglDeleteContext( context->hglrc );
+#elif defined(Darwin)
+ stub.wsInterface.CGLDestroyContext( context->cglc );
+#elif defined(GLX)
+ stub.wsInterface.glXDestroyContext( context->dpy, context->glxContext );
+#endif
+ }
+ else if (context->type == CHROMIUM) {
+ /* Have pack SPU or tilesort SPU, etc. destroy the context */
+ CRASSERT(context->spuContext >= 0);
+ stub.spu->dispatch_table.DestroyContext( context->spuContext );
+ crHashtableWalk(stub.windowTable, stubWindowCheckOwnerCB, context);
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (context->spuConnection)
+ {
+ stub.spu->dispatch_table.VBoxConDestroy(context->spuConnection);
+ context->spuConnection = 0;
+ }
+#endif
+ }
+
+#ifdef GLX
+ crFreeHashtable(context->pGLXPixmapsHash, crFree);
+#endif
+
+ crHashtableDelete(stub.contextTable, contextId, NULL);
+}
+
+#ifdef CHROMIUM_THREADSAFE
+static DECLCALLBACK(void) stubContextDtor(void*pvContext)
+{
+ stubContextFree((ContextInfo*)pvContext);
+}
+#endif
+
+/**
+ * Allocate a new ContextInfo object, initialize it, put it into the
+ * context hash table. If type==CHROMIUM, call the head SPU's
+ * CreateContext() function too.
+ */
+ ContextInfo *
+stubNewContext(char *dpyName, GLint visBits, ContextType type, unsigned long shareCtx
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , struct VBOXUHGSMI *pHgsmi
+#endif
+ )
+{
+ GLint spuContext = -1, spuShareCtx = 0, spuConnection = 0;
+ ContextInfo *context;
+
+ if (shareCtx > 0) {
+ /* translate shareCtx to a SPU context ID */
+ context = (ContextInfo *)
+ crHashtableSearch(stub.contextTable, shareCtx);
+ if (context)
+ spuShareCtx = context->spuContext;
+ }
+
+ if (type == CHROMIUM) {
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (pHgsmi)
+ {
+ spuConnection = stub.spu->dispatch_table.VBoxConCreate(pHgsmi);
+ if (!spuConnection)
+ {
+ crWarning("VBoxConCreate failed");
+ return NULL;
+ }
+ }
+#endif
+ spuContext
+ = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, dpyName, visBits, spuShareCtx);
+ if (spuContext < 0)
+ {
+ crWarning("VBoxCreateContext failed");
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (spuConnection)
+ stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
+#endif
+ return NULL;
+ }
+ }
+
+ context = crCalloc(sizeof(ContextInfo));
+ if (!context) {
+ stub.spu->dispatch_table.DestroyContext(spuContext);
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (spuConnection)
+ stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
+#endif
+ return NULL;
+ }
+
+ if (!dpyName)
+ dpyName = "";
+
+ context->id = stub.freeContextNumber++;
+ context->type = type;
+ context->spuContext = spuContext;
+ context->visBits = visBits;
+ context->currentDrawable = NULL;
+ crStrncpy(context->dpyName, dpyName, MAX_DPY_NAME);
+ context->dpyName[MAX_DPY_NAME-1] = 0;
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ context->spuConnection = spuConnection;
+ context->pHgsmi = pHgsmi;
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+ VBoxTlsRefInit(context, stubContextDtor);
+#endif
+
+#if defined(GLX) || defined(DARWIN)
+ context->share = (ContextInfo *)
+ crHashtableSearch(stub.contextTable, (unsigned long) shareCtx);
+#endif
+
+#ifdef GLX
+ context->pGLXPixmapsHash = crAllocHashtable();
+ context->damageQueryFailed = GL_FALSE;
+ context->damageEventsBase = 0;
+#endif
+
+ crHashtableAdd(stub.contextTable, context->id, (void *) context);
+
+ return context;
+}
+
+
+#ifdef Darwin
+
+#define SET_ATTR(l,i,a) ( (l)[(i)++] = (a) )
+#define SET_ATTR_V(l,i,a,v) ( SET_ATTR(l,i,a), SET_ATTR(l,i,v) )
+
+void stubSetPFA( ContextInfo *ctx, CGLPixelFormatAttribute *attribs, int size, GLint *num ) {
+ GLuint visual = ctx->visBits;
+ int i = 0;
+
+ CRASSERT(visual & CR_RGB_BIT);
+
+ SET_ATTR_V(attribs, i, kCGLPFAColorSize, 8);
+
+ if( visual & CR_DEPTH_BIT )
+ SET_ATTR_V(attribs, i, kCGLPFADepthSize, 16);
+
+ if( visual & CR_ACCUM_BIT )
+ SET_ATTR_V(attribs, i, kCGLPFAAccumSize, 1);
+
+ if( visual & CR_STENCIL_BIT )
+ SET_ATTR_V(attribs, i, kCGLPFAStencilSize, 1);
+
+ if( visual & CR_ALPHA_BIT )
+ SET_ATTR_V(attribs, i, kCGLPFAAlphaSize, 1);
+
+ if( visual & CR_DOUBLE_BIT )
+ SET_ATTR(attribs, i, kCGLPFADoubleBuffer);
+
+ if( visual & CR_STEREO_BIT )
+ SET_ATTR(attribs, i, kCGLPFAStereo);
+
+/* SET_ATTR_V(attribs, i, kCGLPFASampleBuffers, 1);
+ SET_ATTR_V(attribs, i, kCGLPFASamples, 0);
+ SET_ATTR_V(attribs, i, kCGLPFADisplayMask, 0); */
+ SET_ATTR(attribs, i, kCGLPFABackingStore);
+ //SET_ATTR(attribs, i, kCGLPFAWindow); // kCGLPFAWindow deprecated starting from OSX 10.7
+ SET_ATTR_V(attribs, i, kCGLPFADisplayMask, ctx->disp_mask);
+
+ SET_ATTR(attribs, i, 0);
+
+ *num = i;
+}
+
+#endif
+
+#ifndef GLX
+/**
+ * This creates a native GLX/WGL context.
+ */
+static GLboolean
+InstantiateNativeContext( WindowInfo *window, ContextInfo *context )
+{
+#ifdef WINDOWS
+ context->hglrc = stub.wsInterface.wglCreateContext( window->drawable );
+ return context->hglrc ? GL_TRUE : GL_FALSE;
+#elif defined(Darwin)
+ CGLContextObj shareCtx = NULL;
+ CGLPixelFormatObj pix;
+ long npix;
+
+ CGLPixelFormatAttribute attribs[16];
+ GLint ind = 0;
+
+ if( context->share ) {
+ if( context->cglc != context->share->cglc ) {
+ crWarning("CGLCreateContext() is trying to share a non-existant "
+ "CGL context. Setting share context to zero.");
+ shareCtx = 0;
+ }
+ else
+ shareCtx = context->cglc;
+ }
+
+ stubSetPFA( context, attribs, 16, &ind );
+
+ stub.wsInterface.CGLChoosePixelFormat( attribs, &pix, &npix );
+ stub.wsInterface.CGLCreateContext( pix, shareCtx, &context->cglc );
+ if( !context->cglc )
+ crError("InstantiateNativeContext: Couldn't Create the context!");
+
+ stub.wsInterface.CGLDestroyPixelFormat( pix );
+
+ if( context->parambits ) {
+ /* Set the delayed parameters */
+ if( context->parambits & VISBIT_SWAP_RECT )
+ stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPSwapRectangle, context->swap_rect );
+
+ if( context->parambits & VISBIT_SWAP_INTERVAL )
+ stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPSwapInterval, &(context->swap_interval) );
+
+ if( context->parambits & VISBIT_CLIENT_STORAGE )
+ stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPClientStorage, (long*)&(context->client_storage) );
+
+ context->parambits = 0;
+ }
+
+ return context->cglc ? GL_TRUE : GL_FALSE;
+#elif defined(GLX)
+ GLXContext shareCtx = 0;
+
+ /* sort out context sharing here */
+ if (context->share) {
+ if (context->glxContext != context->share->glxContext) {
+ crWarning("glXCreateContext() is trying to share a non-existant "
+ "GLX context. Setting share context to zero.");
+ shareCtx = 0;
+ }
+ else {
+ shareCtx = context->glxContext;
+ }
+ }
+
+ context->glxContext = stub.wsInterface.glXCreateContext( window->dpy,
+ context->visual, shareCtx, context->direct );
+
+ return context->glxContext ? GL_TRUE : GL_FALSE;
+#endif
+}
+#endif /* !GLX */
+
+
+/**
+ * Utility functions to get window size and titlebar text.
+ */
+#ifdef WINDOWS
+
+void
+stubGetWindowGeometry(WindowInfo *window, int *x, int *y,
+ unsigned int *w, unsigned int *h )
+{
+ RECT rect;
+
+ if (!window->drawable || !window->hWnd) {
+ *w = *h = 0;
+ return;
+ }
+
+ if (window->hWnd!=WindowFromDC(window->drawable))
+ {
+ crWarning("Window(%i) DC is no longer valid", window->spuWindow);
+ return;
+ }
+
+ if (!GetClientRect(window->hWnd, &rect))
+ {
+ crWarning("GetClientRect failed for %p", window->hWnd);
+ *w = *h = 0;
+ return;
+ }
+ *w = rect.right - rect.left;
+ *h = rect.bottom - rect.top;
+
+ if (!ClientToScreen( window->hWnd, (LPPOINT) &rect ))
+ {
+ crWarning("ClientToScreen failed for %p", window->hWnd);
+ *w = *h = 0;
+ return;
+ }
+ *x = rect.left;
+ *y = rect.top;
+}
+
+static void
+GetWindowTitle( const WindowInfo *window, char *title )
+{
+ /* XXX - we don't handle recurseUp */
+ if (window->hWnd)
+ GetWindowText(window->hWnd, title, 100);
+ else
+ title[0] = 0;
+}
+
+static void
+GetCursorPosition(WindowInfo *window, int pos[2])
+{
+ RECT rect;
+ POINT point;
+ GLint size[2], x, y;
+ unsigned int NativeHeight, NativeWidth, ChromiumHeight, ChromiumWidth;
+ float WidthRatio, HeightRatio;
+ static int DebugFlag = 0;
+
+ // apparently the "window" parameter passed to this
+ // function contains the native window information
+ HWND NATIVEhwnd = window->hWnd;
+
+ if (NATIVEhwnd!=WindowFromDC(window->drawable))
+ {
+ crWarning("Window(%i) DC is no longer valid", window->spuWindow);
+ return;
+ }
+
+ // get the native window's height and width
+ stubGetWindowGeometry(window, &x, &y, &NativeWidth, &NativeHeight);
+
+ // get the spu window's height and width
+ stub.spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, window->spuWindow, GL_INT, 2, size);
+ ChromiumWidth = size[0];
+ ChromiumHeight = size[1];
+
+ // get the ratio of the size of the native window to the cr window
+ WidthRatio = (float)ChromiumWidth / (float)NativeWidth;
+ HeightRatio = (float)ChromiumHeight / (float)NativeHeight;
+
+ // output some debug information at the beginning
+ if(DebugFlag)
+ {
+ DebugFlag = 0;
+ crDebug("Native Window Handle = %d", NATIVEhwnd);
+ crDebug("Native Width = %i", NativeWidth);
+ crDebug("Native Height = %i", NativeHeight);
+ crDebug("Chromium Width = %i", ChromiumWidth);
+ crDebug("Chromium Height = %i", ChromiumHeight);
+ }
+
+ if (NATIVEhwnd)
+ {
+ GetClientRect( NATIVEhwnd, &rect );
+ GetCursorPos (&point);
+
+ // make sure these coordinates are relative to the native window,
+ // not the whole desktop
+ ScreenToClient(NATIVEhwnd, &point);
+
+ // calculate the new position of the virtual cursor
+ pos[0] = (int)(point.x * WidthRatio);
+ pos[1] = (int)((NativeHeight - point.y) * HeightRatio);
+ }
+ else
+ {
+ pos[0] = 0;
+ pos[1] = 0;
+ }
+}
+
+#elif defined(Darwin)
+
+extern OSStatus CGSGetScreenRectForWindow( CGSConnectionID cid, CGSWindowID wid, float *outRect );
+extern OSStatus CGSGetWindowBounds( CGSConnectionID cid, CGSWindowID wid, float *bounds );
+
+void
+stubGetWindowGeometry( const WindowInfo *window, int *x, int *y, unsigned int *w, unsigned int *h )
+{
+ float rect[4];
+
+ if( !window ||
+ !window->connection ||
+ !window->drawable ||
+ CGSGetWindowBounds( window->connection, window->drawable, rect ) != noErr )
+ {
+ *x = *y = 0;
+ *w = *h = 0;
+ } else {
+ *x = (int) rect[0];
+ *y = (int) rect[1];
+ *w = (int) rect[2];
+ *h = (int) rect[3];
+ }
+}
+
+
+static void
+GetWindowTitle( const WindowInfo *window, char *title )
+{
+ /* XXX \todo Darwin window Title */
+ title[0] = '\0';
+}
+
+
+static void
+GetCursorPosition( const WindowInfo *window, int pos[2] )
+{
+ Point mouse_pos;
+ float window_rect[4];
+
+ GetMouse( &mouse_pos );
+ CGSGetScreenRectForWindow( window->connection, window->drawable, window_rect );
+
+ pos[0] = mouse_pos.h - (int) window_rect[0];
+ pos[1] = (int) window_rect[3] - (mouse_pos.v - (int) window_rect[1]);
+
+ /*crDebug( "%i %i", pos[0], pos[1] );*/
+}
+
+#elif defined(GLX)
+
+void
+stubGetWindowGeometry(WindowInfo *window, int *x, int *y, unsigned int *w, unsigned int *h)
+{
+ Window root, child;
+ unsigned int border, depth;
+ Display *dpy;
+
+ dpy = stubGetWindowDisplay(window);
+
+ /// @todo Performing those checks is expensive operation, especially for simple apps with high FPS.
+ // Disabling those triples glxgears fps, thus using xevents instead of per frame polling is much more preferred.
+ /// @todo Check similar on windows guests, though doubtful as there're no XSync like calls on windows.
+ if (window && dpy)
+ {
+ XLOCK(dpy);
+ }
+
+ if (!window
+ || !dpy
+ || !window->drawable
+ || !XGetGeometry(dpy, window->drawable, &root, x, y, w, h, &border, &depth)
+ || !XTranslateCoordinates(dpy, window->drawable, root, 0, 0, x, y, &child))
+ {
+ crWarning("Failed to get windows geometry for %p, try xwininfo", window);
+ *x = *y = 0;
+ *w = *h = 0;
+ }
+
+ if (window && dpy)
+ {
+ XUNLOCK(dpy);
+ }
+}
+
+static char *
+GetWindowTitleHelper( Display *dpy, Window window, GLboolean recurseUp )
+{
+ while (1) {
+ char *name;
+ if (!XFetchName(dpy, window, &name))
+ return NULL;
+ if (name[0]) {
+ return name;
+ }
+ else if (recurseUp) {
+ /* This window has no name, try the parent */
+ Status stat;
+ Window root, parent, *children;
+ unsigned int numChildren;
+ stat = XQueryTree( dpy, window, &root, &parent,
+ &children, &numChildren );
+ if (!stat || window == root)
+ return NULL;
+ if (children)
+ XFree(children);
+ window = parent;
+ }
+ else {
+ XFree(name);
+ return NULL;
+ }
+ }
+}
+
+static void
+GetWindowTitle( const WindowInfo *window, char *title )
+{
+ char *t = GetWindowTitleHelper(window->dpy, window->drawable, GL_TRUE);
+ if (t) {
+ crStrcpy(title, t);
+ XFree(t);
+ }
+ else {
+ title[0] = 0;
+ }
+}
+
+
+/**
+ *Return current cursor position in local window coords.
+ */
+static void
+GetCursorPosition(WindowInfo *window, int pos[2] )
+{
+ int rootX, rootY;
+ Window root, child;
+ unsigned int mask;
+ int x, y;
+
+ XLOCK(window->dpy);
+
+ Bool q = XQueryPointer(window->dpy, window->drawable, &root, &child,
+ &rootX, &rootY, &pos[0], &pos[1], &mask);
+ if (q) {
+ unsigned int w, h;
+ stubGetWindowGeometry( window, &x, &y, &w, &h );
+ /* invert Y */
+ pos[1] = (int) h - pos[1] - 1;
+ }
+ else {
+ pos[0] = pos[1] = 0;
+ }
+
+ XUNLOCK(window->dpy);
+}
+
+#endif
+
+
+/**
+ * This function is called by MakeCurrent() and determines whether or
+ * not a new rendering context should be bound to Chromium or the native
+ * OpenGL.
+ * \return GL_FALSE if native OpenGL should be used, or GL_TRUE if Chromium
+ * should be used.
+ */
+static GLboolean
+stubCheckUseChromium( WindowInfo *window )
+{
+ int x, y;
+ unsigned int w, h;
+
+ /* If the provided window is CHROMIUM, we're clearly intended
+ * to create a CHROMIUM context.
+ */
+ if (window->type == CHROMIUM)
+ return GL_TRUE;
+
+ if (stub.ignoreFreeglutMenus) {
+ const char *glutMenuTitle = "freeglut menu";
+ char title[1000];
+ GetWindowTitle(window, title);
+ if (crStrcmp(title, glutMenuTitle) == 0) {
+ crDebug("GL faker: Ignoring freeglut menu window");
+ return GL_FALSE;
+ }
+ }
+
+ /* If the user's specified a window count for Chromium, see if
+ * this window satisfies that criterium.
+ */
+ stub.matchChromiumWindowCounter++;
+ if (stub.matchChromiumWindowCount > 0) {
+ if (stub.matchChromiumWindowCounter != stub.matchChromiumWindowCount) {
+ crDebug("Using native GL, app window doesn't meet match_window_count");
+ return GL_FALSE;
+ }
+ }
+
+ /* If the user's specified a window list to ignore, see if this
+ * window satisfies that criterium.
+ */
+ if (stub.matchChromiumWindowID) {
+ GLuint i;
+
+ for (i = 0; i <= stub.numIgnoreWindowID; i++) {
+ if (stub.matchChromiumWindowID[i] == stub.matchChromiumWindowCounter) {
+ crDebug("Ignore window ID %d, using native GL", stub.matchChromiumWindowID[i]);
+ return GL_FALSE;
+ }
+ }
+ }
+
+ /* If the user's specified a minimum window size for Chromium, see if
+ * this window satisfies that criterium.
+ */
+ if (stub.minChromiumWindowWidth > 0 &&
+ stub.minChromiumWindowHeight > 0) {
+ stubGetWindowGeometry( window, &x, &y, &w, &h );
+ if (w >= stub.minChromiumWindowWidth &&
+ h >= stub.minChromiumWindowHeight) {
+
+ /* Check for maximum sized window now too */
+ if (stub.maxChromiumWindowWidth &&
+ stub.maxChromiumWindowHeight) {
+ if (w < stub.maxChromiumWindowWidth &&
+ h < stub.maxChromiumWindowHeight)
+ return GL_TRUE;
+ else
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+ }
+ crDebug("Using native GL, app window doesn't meet minimum_window_size");
+ return GL_FALSE;
+ }
+ else if (stub.matchWindowTitle) {
+ /* If the user's specified a window title for Chromium, see if this
+ * window satisfies that criterium.
+ */
+ GLboolean wildcard = GL_FALSE;
+ char title[1000];
+ char *titlePattern;
+ int len;
+ /* check for leading '*' wildcard */
+ if (stub.matchWindowTitle[0] == '*') {
+ titlePattern = crStrdup( stub.matchWindowTitle + 1 );
+ wildcard = GL_TRUE;
+ }
+ else {
+ titlePattern = crStrdup( stub.matchWindowTitle );
+ }
+ /* check for trailing '*' wildcard */
+ len = crStrlen(titlePattern);
+ if (len > 0 && titlePattern[len - 1] == '*') {
+ titlePattern[len - 1] = '\0'; /* terminate here */
+ wildcard = GL_TRUE;
+ }
+
+ GetWindowTitle( window, title );
+ if (title[0]) {
+ if (wildcard) {
+ if (crStrstr(title, titlePattern)) {
+ crFree(titlePattern);
+ return GL_TRUE;
+ }
+ }
+ else if (crStrcmp(title, titlePattern) == 0) {
+ crFree(titlePattern);
+ return GL_TRUE;
+ }
+ }
+ crFree(titlePattern);
+ crDebug("Using native GL, app window title doesn't match match_window_title string (\"%s\" != \"%s\")", title, stub.matchWindowTitle);
+ return GL_FALSE;
+ }
+
+ /* Window title and size don't matter */
+ CRASSERT(stub.minChromiumWindowWidth == 0);
+ CRASSERT(stub.minChromiumWindowHeight == 0);
+ CRASSERT(stub.matchWindowTitle == NULL);
+
+ /* User hasn't specified a width/height or window title.
+ * We'll use chromium for this window (and context) if no other is.
+ */
+
+ return GL_TRUE; /* use Chromium! */
+}
+
+static void stubWindowCheckOwnerCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *pWindow = (WindowInfo *) data1;
+ ContextInfo *pCtx = (ContextInfo *) data2;
+
+ RT_NOREF(key);
+
+
+ if (pWindow->pOwner == pCtx)
+ {
+#ifdef WINDOWS
+ /* Note: can't use WindowFromDC(context->pOwnWindow->drawable) here
+ because GL context is already released from DC and actual guest window
+ could be destroyed.
+ */
+ stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->hWnd);
+#else
+ stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->drawable);
+#endif
+ }
+}
+
+GLboolean stubCtxCreate(ContextInfo *context)
+{
+ /*
+ * Create a Chromium context.
+ */
+#if defined(GLX) || defined(DARWIN)
+ GLint spuShareCtx = context->share ? context->share->spuContext : 0;
+#else
+ GLint spuShareCtx = 0;
+#endif
+ GLint spuConnection = 0;
+ CRASSERT(stub.spu);
+ CRASSERT(stub.spu->dispatch_table.CreateContext);
+ context->type = CHROMIUM;
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (context->pHgsmi)
+ {
+ spuConnection = stub.spu->dispatch_table.VBoxConCreate(context->pHgsmi);
+ if (!spuConnection)
+ {
+ crError("VBoxConCreate failed");
+ return GL_FALSE;
+ }
+ context->spuConnection = spuConnection;
+ }
+#endif
+
+ context->spuContext
+ = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, context->dpyName,
+ context->visBits,
+ spuShareCtx);
+
+ return GL_TRUE;
+}
+
+GLboolean stubCtxCheckCreate(ContextInfo *context)
+{
+ if (context->type == UNDECIDED)
+ return stubCtxCreate(context);
+ return CHROMIUM == context->type;
+}
+
+
+GLboolean
+stubMakeCurrent( WindowInfo *window, ContextInfo *context )
+{
+ GLboolean retVal = GL_FALSE;
+
+ /*
+ * Get WindowInfo and ContextInfo pointers.
+ */
+
+ if (!context || !window) {
+ ContextInfo * currentContext = stubGetCurrentContext();
+ if (currentContext)
+ currentContext->currentDrawable = NULL;
+ if (context)
+ context->currentDrawable = NULL;
+ stubSetCurrentContext(NULL);
+ return GL_TRUE; /* OK */
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ stubCheckMultithread();
+#endif
+
+ if (context->type == UNDECIDED) {
+ /* Here's where we really create contexts */
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&stub.mutex);
+#endif
+
+ if (stubCheckUseChromium(window)) {
+ GLint spuConnection = 0;
+
+ if (!stubCtxCreate(context))
+ {
+ crWarning("stubCtxCreate failed");
+ return GL_FALSE;
+ }
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ spuConnection = context->spuConnection;
+#endif
+
+ if (window->spuWindow == -1)
+ {
+ /*crDebug("(1)stubMakeCurrent ctx=%p(%i) window=%p(%i)", context, context->spuContext, window, window->spuWindow);*/
+ window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate(spuConnection, window->dpyName, context->visBits );
+#ifdef CR_NEWWINTRACK
+ window->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(spuConnection);
+#endif
+ }
+ }
+#ifndef GLX
+ else {
+ /*
+ * Create a native OpenGL context.
+ */
+ if (!InstantiateNativeContext(window, context))
+ {
+# ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&stub.mutex);
+# endif
+ return 0; /* false */
+ }
+ context->type = NATIVE;
+ }
+#endif /* !GLX */
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&stub.mutex);
+#endif
+ }
+
+
+ if (context->type == NATIVE) {
+ /*
+ * Native OpenGL MakeCurrent().
+ */
+#ifdef WINDOWS
+ retVal = (GLboolean) stub.wsInterface.wglMakeCurrent( window->drawable, context->hglrc );
+#elif defined(Darwin)
+ // XXX \todo We need to differentiate between these two..
+ retVal = ( stub.wsInterface.CGLSetSurface(context->cglc, window->connection, window->drawable, window->surface) == noErr );
+ retVal = ( stub.wsInterface.CGLSetCurrentContext(context->cglc) == noErr );
+#elif defined(GLX)
+ retVal = (GLboolean) stub.wsInterface.glXMakeCurrent( window->dpy, window->drawable, context->glxContext );
+#endif
+ }
+ else {
+ /*
+ * SPU chain MakeCurrent().
+ */
+ CRASSERT(context->type == CHROMIUM);
+ CRASSERT(context->spuContext >= 0);
+
+ /*if (context->currentDrawable && context->currentDrawable != window)
+ crDebug("Rebinding context %p to a different window", context);*/
+
+ if (window->type == NATIVE) {
+ crWarning("Can't rebind a chromium context to a native window\n");
+ retVal = 0;
+ }
+ else {
+ if (window->spuWindow == -1)
+ {
+ /*crDebug("(2)stubMakeCurrent ctx=%p(%i) window=%p(%i)", context, context->spuContext, window, window->spuWindow);*/
+ window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ context->spuConnection,
+#else
+ 0,
+#endif
+ window->dpyName, context->visBits );
+#ifdef CR_NEWWINTRACK
+ window->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(
+# if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ context->spuConnection
+# else
+ 0
+# endif
+ );
+#endif
+ if (context->currentDrawable && context->currentDrawable->type==CHROMIUM
+ && context->currentDrawable->pOwner==context)
+ {
+#ifdef WINDOWS
+ if (context->currentDrawable->hWnd!=WindowFromDC(context->currentDrawable->drawable))
+ {
+ stubDestroyWindow(CR_CTX_CON(context), (GLint)context->currentDrawable->hWnd);
+ }
+#else
+ Window root;
+ int x, y;
+ unsigned int border, depth, w, h;
+
+ XLOCK(context->currentDrawable->dpy);
+ if (!XGetGeometry(context->currentDrawable->dpy, context->currentDrawable->drawable, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ stubDestroyWindow(CR_CTX_CON(context), (GLint)context->currentDrawable->drawable);
+ }
+ XUNLOCK(context->currentDrawable->dpy);
+#endif
+
+ }
+ }
+
+ if (window->spuWindow != (GLint)window->drawable)
+ stub.spu->dispatch_table.MakeCurrent( window->spuWindow, (GLint) window->drawable, context->spuContext );
+ else
+ stub.spu->dispatch_table.MakeCurrent( window->spuWindow, 0, /* native window handle */ context->spuContext );
+
+ retVal = 1;
+ }
+ }
+
+ window->type = context->type;
+ window->pOwner = context;
+ context->currentDrawable = window;
+ stubSetCurrentContext(context);
+
+ if (retVal) {
+ /* Now, if we've transitions from Chromium to native rendering, or
+ * vice versa, we have to change all the OpenGL entrypoint pointers.
+ */
+ if (context->type == NATIVE) {
+ /* Switch to native API */
+ /*printf(" Switching to native API\n");*/
+ stubSetDispatch(&stub.nativeDispatch);
+ }
+ else if (context->type == CHROMIUM) {
+ /* Switch to stub (SPU) API */
+ /*printf(" Switching to spu API\n");*/
+ stubSetDispatch(&stub.spuDispatch);
+ }
+ else {
+ /* no API switch needed */
+ }
+ }
+
+ if (!window->width && window->type == CHROMIUM) {
+ /* One time window setup */
+ int x, y;
+ unsigned int winW, winH;
+
+ stubGetWindowGeometry( window, &x, &y, &winW, &winH );
+
+ /* If we're not using GLX/WGL (no app window) we'll always get
+ * a width and height of zero here. In that case, skip the viewport
+ * call since we're probably using a tilesort SPU with fake_window_dims
+ * which the tilesort SPU will use for the viewport.
+ */
+ window->width = winW;
+ window->height = winH;
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ if (stubIsWindowVisible(window))
+#endif
+ {
+ if (stub.trackWindowSize)
+ stub.spuDispatch.WindowSize( window->spuWindow, winW, winH );
+ if (stub.trackWindowPos)
+ stub.spuDispatch.WindowPosition(window->spuWindow, x, y);
+ if (winW > 0 && winH > 0)
+ stub.spu->dispatch_table.Viewport( 0, 0, winW, winH );
+ }
+#ifdef VBOX_WITH_WDDM
+ if (stub.trackWindowVisibleRgn)
+ stub.spu->dispatch_table.WindowVisibleRegion(window->spuWindow, 0, NULL);
+#endif
+ }
+
+ /* Update window mapping state.
+ * Basically, this lets us hide render SPU windows which correspond
+ * to unmapped application windows. Without this, "pertly" (for example)
+ * opens *lots* of temporary windows which otherwise clutter the screen.
+ */
+ if (stub.trackWindowVisibility && window->type == CHROMIUM && window->drawable) {
+ const int mapped = stubIsWindowVisible(window);
+ if (mapped != window->mapped) {
+ crDebug("Dispatched: WindowShow(%i, %i)", window->spuWindow, mapped);
+ stub.spu->dispatch_table.WindowShow(window->spuWindow, mapped);
+ window->mapped = mapped;
+ }
+ }
+
+ return retVal;
+}
+
+void
+stubDestroyContext( unsigned long contextId )
+{
+ ContextInfo *context;
+
+ if (!stub.contextTable) {
+ return;
+ }
+
+ /* the lock order is windowTable->contextTable (see wglMakeCurrent_prox, glXMakeCurrent)
+ * this is why we need to take a windowTable lock since we will later do stub.windowTable access & locking */
+ crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, contextId);
+ if (context)
+ stubDestroyContextLocked(context);
+ else
+ crError("No context.");
+
+#ifdef CHROMIUM_THREADSAFE
+ if (stubGetCurrentContext() == context) {
+ stubSetCurrentContext(NULL);
+ }
+
+ VBoxTlsRefMarkDestroy(context);
+ VBoxTlsRefRelease(context);
+#else
+ if (stubGetCurrentContext() == context) {
+ stubSetCurrentContext(NULL);
+ }
+ stubContextFree(context);
+#endif
+ crHashtableUnlock(stub.contextTable);
+ crHashtableUnlock(stub.windowTable);
+}
+
+void
+stubSwapBuffers(WindowInfo *window, GLint flags)
+{
+ if (!window)
+ return;
+
+ /* Determine if this window is being rendered natively or through
+ * Chromium.
+ */
+
+ if (window->type == NATIVE) {
+ /*printf("*** Swapping native window %d\n", (int) drawable);*/
+#ifdef WINDOWS
+ (void) stub.wsInterface.wglSwapBuffers( window->drawable );
+#elif defined(Darwin)
+ /* ...is this ok? */
+/* stub.wsInterface.CGLFlushDrawable( context->cglc ); */
+ crDebug("stubSwapBuffers: unable to swap (no context!)");
+#elif defined(GLX)
+ stub.wsInterface.glXSwapBuffers( window->dpy, window->drawable );
+#endif
+ }
+ else if (window->type == CHROMIUM) {
+ /* Let the SPU do the buffer swap */
+ /*printf("*** Swapping chromium window %d\n", (int) drawable);*/
+ if (stub.appDrawCursor) {
+ int pos[2];
+ GetCursorPosition(window, pos);
+ stub.spu->dispatch_table.ChromiumParametervCR(GL_CURSOR_POSITION_CR, GL_INT, 2, pos);
+ }
+ stub.spu->dispatch_table.SwapBuffers( window->spuWindow, flags );
+ }
+ else {
+ crDebug("Calling SwapBuffers on a window we haven't seen before (no-op).");
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/cr_gl.py b/src/VBox/Additions/common/crOpenGL/cr_gl.py
new file mode 100755
index 00000000..12e332ad
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/cr_gl.py
@@ -0,0 +1,65 @@
+from __future__ import print_function
+print("""
+/** @file
+ * VBox OpenGL chromium functions header
+ */
+
+/*
+ * Copyright (C) 2008-2016 """ """Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+""")
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE cr_gl.py SCRIPT */
+#ifndef __CR_GL_H__
+#define __CR_GL_H__
+
+#include "chromium.h"
+#include "cr_string.h"
+#include "cr_version.h"
+#include "stub.h"
+
+#ifdef WINDOWS
+#pragma warning( disable: 4055 )
+#endif
+
+""")
+
+
+# Extern-like declarations
+keys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+for func_name in keys:
+ if "Chromium" == apiutil.Category(func_name):
+ continue
+ if "VBox" == apiutil.Category(func_name):
+ continue
+ if func_name == "BoundsInfoCR":
+ continue
+ if "GL_chromium" == apiutil.Category(func_name):
+ pass #continue
+
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ print("extern %s cr_gl%s(%s);" % (return_type, func_name,
+ apiutil.MakeDeclarationString( params )))
+
+print("#endif /* __CR_GL_H__ */")
diff --git a/src/VBox/Additions/common/crOpenGL/defs.py b/src/VBox/Additions/common/crOpenGL/defs.py
new file mode 100755
index 00000000..c0703088
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/defs.py
@@ -0,0 +1,505 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightDef()
+
+# NOTE: if we need a LIBRARY statement, we would need to create a defs-x86.py to generate a .def file for VBoxOGL-x86 library
+#print "LIBRARY VBoxOGL"
+#print "DESCRIPTION \"\"" - warning LNK4017: DESCRIPTION statement not supported for the target platform; ignored
+print("EXPORTS")
+
+# XXX can't these values be automatically computed by analyzing parameters?
+
+stack_sizes = {
+ 'Accum': 8,
+ 'AlphaFunc': 8,
+ 'AreTexturesResident': 12,
+ 'ArrayElement': 4,
+ 'Begin': 4,
+ 'BindTexture': 8,
+ 'Bitmap': 28,
+ 'BlendFunc': 8,
+ 'CallList': 4,
+ 'CallLists': 12,
+ 'Clear': 4,
+ 'ClearAccum': 16,
+ 'ClearColor': 16,
+ 'ClearDepth': 8,
+ 'ClearIndex': 4,
+ 'ClearStencil': 4,
+ 'ClipPlane': 8,
+ 'Color3b': 12,
+ 'Color3bv': 4,
+ 'Color3d': 24,
+ 'Color3dv': 4,
+ 'Color3f': 12,
+ 'Color3fv': 4,
+ 'Color3i': 12,
+ 'Color3iv': 4,
+ 'Color3s': 12,
+ 'Color3sv': 4,
+ 'Color3ub': 12,
+ 'Color3ubv': 4,
+ 'Color3ui': 12,
+ 'Color3uiv': 4,
+ 'Color3us': 12,
+ 'Color3usv': 4,
+ 'Color4b': 16,
+ 'Color4bv': 4,
+ 'Color4d': 32,
+ 'Color4dv': 4,
+ 'Color4f': 16,
+ 'Color4fv': 4,
+ 'Color4i': 16,
+ 'Color4iv': 4,
+ 'Color4s': 16,
+ 'Color4sv': 4,
+ 'Color4ub': 16,
+ 'Color4ubv': 4,
+ 'Color4ui': 16,
+ 'Color4uiv': 4,
+ 'Color4us': 16,
+ 'Color4usv': 4,
+ 'ColorMask': 16,
+ 'ColorMaterial': 8,
+ 'ColorPointer': 16,
+ 'CopyPixels': 20,
+ 'CopyTexImage1D': 28,
+ 'CopyTexImage2D': 32,
+ 'CopyTexSubImage1D': 24,
+ 'CopyTexSubImage2D': 32,
+ 'CullFace': 4,
+ 'DebugEntry': 8,
+ 'DeleteLists': 8,
+ 'DeleteTextures': 8,
+ 'DepthFunc': 4,
+ 'DepthMask': 4,
+ 'DepthRange': 16,
+ 'Disable': 4,
+ 'DisableClientState': 4,
+ 'DrawArrays': 12,
+ 'DrawBuffer': 4,
+ 'DrawElements': 16,
+ 'DrawPixels': 20,
+ 'EdgeFlag': 4,
+ 'EdgeFlagPointer': 8,
+ 'EdgeFlagv': 4,
+ 'Enable': 4,
+ 'EnableClientState': 4,
+ 'End': 0,
+ 'EndList': 0,
+ 'EvalCoord1d': 8,
+ 'EvalCoord1dv': 4,
+ 'EvalCoord1f': 4,
+ 'EvalCoord1fv': 4,
+ 'EvalCoord2d': 16,
+ 'EvalCoord2dv': 4,
+ 'EvalCoord2f': 8,
+ 'EvalCoord2fv': 4,
+ 'EvalMesh1': 12,
+ 'EvalMesh2': 20,
+ 'EvalPoint1': 4,
+ 'EvalPoint2': 8,
+ 'FeedbackBuffer': 12,
+ 'Finish': 0,
+ 'Flush': 0,
+ 'Fogf': 8,
+ 'Fogfv': 8,
+ 'Fogi': 8,
+ 'Fogiv': 8,
+ 'FrontFace': 4,
+ 'Frustum': 48,
+ 'GenLists': 4,
+ 'GenTextures': 8,
+ 'GetBooleanv': 8,
+ 'GetClipPlane': 8,
+ 'GetDoublev': 8,
+ 'GetError': 0,
+ 'GetFloatv': 8,
+ 'GetIntegerv': 8,
+ 'GetLightfv': 12,
+ 'GetLightiv': 12,
+ 'GetMapdv': 12,
+ 'GetMapfv': 12,
+ 'GetMapiv': 12,
+ 'GetMaterialfv': 12,
+ 'GetMaterialiv': 12,
+ 'GetPixelMapfv': 8,
+ 'GetPixelMapuiv': 8,
+ 'GetPixelMapusv': 8,
+ 'GetPointerv': 8,
+ 'GetPolygonStipple': 4,
+ 'GetString': 4,
+ 'GetTexEnvfv': 12,
+ 'GetTexEnviv': 12,
+ 'GetTexGendv': 12,
+ 'GetTexGenfv': 12,
+ 'GetTexGeniv': 12,
+ 'GetTexImage': 20,
+ 'GetTexLevelParameterfv': 16,
+ 'GetTexLevelParameteriv': 16,
+ 'GetTexParameterfv': 12,
+ 'GetTexParameteriv': 12,
+ 'Hint': 8,
+ 'IndexMask': 4,
+ 'IndexPointer': 12,
+ 'Indexd': 8,
+ 'Indexdv': 4,
+ 'Indexf': 4,
+ 'Indexfv': 4,
+ 'Indexi': 4,
+ 'Indexiv': 4,
+ 'Indexs': 4,
+ 'Indexsv': 4,
+ 'Indexub': 4,
+ 'Indexubv': 4,
+ 'InitNames': 0,
+ 'InterleavedArrays': 12,
+ 'IsEnabled': 4,
+ 'IsList': 4,
+ 'IsTexture': 4,
+ 'LightModelf': 8,
+ 'LightModelfv': 8,
+ 'LightModeli': 8,
+ 'LightModeliv': 8,
+ 'Lightf': 12,
+ 'Lightfv': 12,
+ 'Lighti': 12,
+ 'Lightiv': 12,
+ 'LineStipple': 8,
+ 'LineWidth': 4,
+ 'ListBase': 4,
+ 'LoadIdentity': 0,
+ 'LoadMatrixd': 4,
+ 'LoadMatrixf': 4,
+ 'LoadName': 4,
+ 'LogicOp': 4,
+ 'Map1d': 32,
+ 'Map1f': 24,
+ 'Map2d': 56,
+ 'Map2f': 40,
+ 'MapGrid1d': 20,
+ 'MapGrid1f': 12,
+ 'MapGrid2d': 40,
+ 'MapGrid2f': 24,
+ 'Materialf': 12,
+ 'Materialfv': 12,
+ 'Materiali': 12,
+ 'Materialiv': 12,
+ 'MatrixMode': 4,
+ 'MultMatrixd': 4,
+ 'MultMatrixf': 4,
+ 'NewList': 8,
+ 'Normal3b': 12,
+ 'Normal3bv': 4,
+ 'Normal3d': 24,
+ 'Normal3dv': 4,
+ 'Normal3f': 12,
+ 'Normal3fv': 4,
+ 'Normal3i': 12,
+ 'Normal3iv': 4,
+ 'Normal3s': 12,
+ 'Normal3sv': 4,
+ 'NormalPointer': 12,
+ 'Ortho': 48,
+ 'PassThrough': 4,
+ 'PixelMapfv': 12,
+ 'PixelMapuiv': 12,
+ 'PixelMapusv': 12,
+ 'PixelStoref': 8,
+ 'PixelStorei': 8,
+ 'PixelTransferf': 8,
+ 'PixelTransferi': 8,
+ 'PixelZoom': 8,
+ 'PointSize': 4,
+ 'PolygonMode': 8,
+ 'PolygonOffset': 8,
+ 'PolygonStipple': 4,
+ 'PopAttrib': 0,
+ 'PopClientAttrib': 0,
+ 'PopMatrix': 0,
+ 'PopName': 0,
+ 'PrioritizeTextures': 12,
+ 'PushAttrib': 4,
+ 'PushClientAttrib': 4,
+ 'PushMatrix': 0,
+ 'PushName': 4,
+ 'RasterPos2d': 16,
+ 'RasterPos2dv': 4,
+ 'RasterPos2f': 8,
+ 'RasterPos2fv': 4,
+ 'RasterPos2i': 8,
+ 'RasterPos2iv': 4,
+ 'RasterPos2s': 8,
+ 'RasterPos2sv': 4,
+ 'RasterPos3d': 24,
+ 'RasterPos3dv': 4,
+ 'RasterPos3f': 12,
+ 'RasterPos3fv': 4,
+ 'RasterPos3i': 12,
+ 'RasterPos3iv': 4,
+ 'RasterPos3s': 12,
+ 'RasterPos3sv': 4,
+ 'RasterPos4d': 32,
+ 'RasterPos4dv': 4,
+ 'RasterPos4f': 16,
+ 'RasterPos4fv': 4,
+ 'RasterPos4i': 16,
+ 'RasterPos4iv': 4,
+ 'RasterPos4s': 16,
+ 'RasterPos4sv': 4,
+ 'ReadBuffer': 4,
+ 'ReadPixels': 28,
+ 'Rectd': 32,
+ 'Rectdv': 8,
+ 'Rectf': 16,
+ 'Rectfv': 8,
+ 'Recti': 16,
+ 'Rectiv': 8,
+ 'Rects': 16,
+ 'Rectsv': 8,
+ 'RenderMode': 4,
+ 'Rotated': 32,
+ 'Rotatef': 16,
+ 'Scaled': 24,
+ 'Scalef': 12,
+ 'Scissor': 16,
+ 'SelectBuffer': 8,
+ 'ShadeModel': 4,
+ 'StencilFunc': 12,
+ 'StencilMask': 4,
+ 'StencilOp': 12,
+ 'TexCoord1d': 8,
+ 'TexCoord1dv': 4,
+ 'TexCoord1f': 4,
+ 'TexCoord1fv': 4,
+ 'TexCoord1i': 4,
+ 'TexCoord1iv': 4,
+ 'TexCoord1s': 4,
+ 'TexCoord1sv': 4,
+ 'TexCoord2d': 16,
+ 'TexCoord2dv': 4,
+ 'TexCoord2f': 8,
+ 'TexCoord2fv': 4,
+ 'TexCoord2i': 8,
+ 'TexCoord2iv': 4,
+ 'TexCoord2s': 8,
+ 'TexCoord2sv': 4,
+ 'TexCoord3d': 24,
+ 'TexCoord3dv': 4,
+ 'TexCoord3f': 12,
+ 'TexCoord3fv': 4,
+ 'TexCoord3i': 12,
+ 'TexCoord3iv': 4,
+ 'TexCoord3s': 12,
+ 'TexCoord3sv': 4,
+ 'TexCoord4d': 32,
+ 'TexCoord4dv': 4,
+ 'TexCoord4f': 16,
+ 'TexCoord4fv': 4,
+ 'TexCoord4i': 16,
+ 'TexCoord4iv': 4,
+ 'TexCoord4s': 16,
+ 'TexCoord4sv': 4,
+ 'TexCoordPointer': 16,
+ 'TexEnvf': 12,
+ 'TexEnvfv': 12,
+ 'TexEnvi': 12,
+ 'TexEnviv': 12,
+ 'TexGend': 16,
+ 'TexGendv': 12,
+ 'TexGenf': 12,
+ 'TexGenfv': 12,
+ 'TexGeni': 12,
+ 'TexGeniv': 12,
+ 'TexImage1D': 32,
+ 'TexImage2D': 36,
+ 'TexImage3D': 36,
+ 'TexParameterf': 12,
+ 'TexParameterfv': 12,
+ 'TexParameteri': 12,
+ 'TexParameteriv': 12,
+ 'TexSubImage1D': 28,
+ 'TexSubImage2D': 36,
+ 'Translated': 24,
+ 'Translatef': 12,
+ 'Vertex2d': 16,
+ 'Vertex2dv': 4,
+ 'Vertex2f': 8,
+ 'Vertex2fv': 4,
+ 'Vertex2i': 8,
+ 'Vertex2iv': 4,
+ 'Vertex2s': 8,
+ 'Vertex2sv': 4,
+ 'Vertex3d': 24,
+ 'Vertex3dv': 4,
+ 'Vertex3f': 12,
+ 'Vertex3fv': 4,
+ 'Vertex3i': 12,
+ 'Vertex3iv': 4,
+ 'Vertex3s': 12,
+ 'Vertex3sv': 4,
+ 'Vertex4d': 32,
+ 'Vertex4dv': 4,
+ 'Vertex4f': 16,
+ 'Vertex4fv': 4,
+ 'Vertex4i': 16,
+ 'Vertex4iv': 4,
+ 'Vertex4s': 16,
+ 'Vertex4sv': 4,
+ 'VertexPointer': 16,
+ 'Viewport': 16,
+ 'wglChoosePixelFormat': 8,
+ 'wglCopyContext': 12,
+ 'wglCreateContext': 4,
+ 'wglCreateLayerContext': 8,
+ 'wglDeleteContext': 4,
+ 'wglDescribeLayerPlane': 20,
+ 'wglDescribePixelFormat': 16,
+ 'wglGetCurrentContext': 0,
+ 'wglGetCurrentDC': 0,
+ 'wglGetDefaultProcAddress': 4,
+ 'wglGetLayerPaletteEntries': 20,
+ 'wglGetPixelFormat': 4,
+ 'wglGetProcAddress': 4,
+ 'wglMakeCurrent': 8,
+ 'wglRealizeLayerPalette': 12,
+ 'wglSetLayerPaletteEntries': 20,
+ 'wglSetPixelFormat': 12,
+ 'wglShareLists': 8,
+ 'wglSwapBuffers': 4,
+ 'wglSwapLayerBuffers': 8,
+ 'wglSwapMultipleBuffers': 8,
+ 'wglUseFontBitmapsA': 16,
+ 'wglUseFontBitmapsW': 16,
+ 'wglUseFontOutlinesA': 32,
+ 'wglUseFontOutlinesW': 32,
+ 'wglChoosePixelFormatEXT' : 24,
+ 'wglGetPixelFormatAttribivEXT' : 24,
+ 'wglGetPixelFormatAttribfvEXT' : 24,
+ 'wglGetExtensionsStringEXT' : 4,
+ 'CopyContext' : 12,
+ 'CreateContext' : 4,
+ 'CreateLayerContext' : 8,
+ 'DeleteContext' : 4,
+ 'DescribeLayerPlane' : 20,
+ 'DescribePixelFormat' : 16,
+ 'GetLayerPaletteEntries' : 20,
+ 'GetProcAddress' : 4,
+ 'RealizeLayerPalette' : 12,
+ 'ReleaseContext' : 4,
+ 'SetContext' : 12,
+ 'SetLayerPaletteEntries' : 20,
+ 'SetPixelFormat' : 12,
+ 'ShareLists' : 8,
+ 'SwapBuffers' : 4,
+ 'SwapLayerBuffers' : 8,
+ 'ValidateVersion' : 4,
+}
+
+noexport_special = [
+ "BoundsInfoCR",
+ "CreateContext",
+ "DestroyContext",
+ "MakeCurrent",
+ "WindowCreate",
+ "WindowDestroy",
+ "WindowSize",
+ "WindowPosition",
+ "WindowVisibleRegion",
+ "WindowShow",
+ "SwapBuffers"
+]
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if func_name in noexport_special:
+ continue
+ try:
+ print("gl%s@%d = cr_gl%s" % (func_name,stack_sizes[func_name],func_name))
+ except KeyError:
+ pass
+
+for func_name in ( "wglChoosePixelFormat",
+ "wglCopyContext",
+ "wglCreateContext",
+ "wglCreateLayerContext",
+ "wglDeleteContext",
+ "wglDescribeLayerPlane",
+ "wglDescribePixelFormat",
+ "wglGetCurrentContext",
+ "wglGetCurrentDC",
+ "wglGetLayerPaletteEntries",
+ "wglGetPixelFormat",
+ "wglGetProcAddress",
+ "wglMakeCurrent",
+ "wglRealizeLayerPalette",
+ "wglSetLayerPaletteEntries",
+ "wglSetPixelFormat",
+ "wglShareLists",
+ "wglSwapBuffers",
+ "wglSwapLayerBuffers",
+ "wglSwapMultipleBuffers",
+ "wglUseFontBitmapsA",
+ "wglUseFontBitmapsW",
+ "wglUseFontOutlinesA",
+ "wglUseFontOutlinesW",
+ "wglChoosePixelFormatEXT",
+ "wglGetPixelFormatAttribivEXT",
+ "wglGetPixelFormatAttribfvEXT",
+ "wglGetExtensionsStringEXT"):
+ print("%s = %s_prox" % (func_name,func_name))
+
+"""
+for func_name in ( "CopyContext",
+ "CreateContext",
+ "CreateLayerContext",
+ "DeleteContext",
+ "DescribeLayerPlane",
+ "DescribePixelFormat",
+ "GetLayerPaletteEntries",
+ "GetProcAddress",
+ "RealizeLayerPalette",
+ "SetLayerPaletteEntries",
+ "ShareLists",
+ "SwapBuffers",
+ "SwapLayerBuffers"):
+ print "Drv%s@%d = wgl%s_prox" % (func_name,stack_sizes[func_name],func_name)
+"""
+
+print("""DrvCopyContext
+DrvCreateContext
+DrvCreateLayerContext
+DrvDeleteContext
+DrvDescribeLayerPlane
+DrvDescribePixelFormat
+DrvGetLayerPaletteEntries
+DrvGetProcAddress = wglGetProcAddress_prox
+DrvRealizeLayerPalette
+DrvSetLayerPaletteEntries
+DrvShareLists
+DrvSwapBuffers
+DrvSwapLayerBuffers
+DrvReleaseContext@4 = DrvReleaseContext
+DrvSetContext@12 = DrvSetContext
+DrvValidateVersion@4 = DrvValidateVersion
+DrvSetPixelFormat@8 = DrvSetPixelFormat""")
+
+print("""crCreateContext
+crMakeCurrent
+crSwapBuffers
+crGetProcAddress
+VBoxCreateContext
+VBoxCtxChromiumParameteriCR
+VBoxGetWindowId
+VBoxGetContextId
+VBoxFlushToHost""")
+#print "DllMain"
diff --git a/src/VBox/Additions/common/crOpenGL/defs64.py b/src/VBox/Additions/common/crOpenGL/defs64.py
new file mode 100755
index 00000000..58971b9b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/defs64.py
@@ -0,0 +1,485 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightDef()
+
+#print "LIBRARY VBoxOGL"
+#print "DESCRIPTION \"\"" - warning LNK4017: DESCRIPTION statement not supported for the target platform; ignored
+print("EXPORTS")
+
+# XXX can't these values be automatically computed by analyzing parameters?
+
+exports_special = [
+ 'Accum',
+ 'AlphaFunc',
+ 'AreTexturesResident',
+ 'ArrayElement',
+ 'Begin',
+ 'BindTexture',
+ 'Bitmap',
+ 'BlendFunc',
+ 'CallList',
+ 'CallLists',
+ 'Clear',
+ 'ClearAccum',
+ 'ClearColor',
+ 'ClearDepth',
+ 'ClearIndex',
+ 'ClearStencil',
+ 'ClipPlane',
+ 'Color3b',
+ 'Color3bv',
+ 'Color3d',
+ 'Color3dv',
+ 'Color3f',
+ 'Color3fv',
+ 'Color3i',
+ 'Color3iv',
+ 'Color3s',
+ 'Color3sv',
+ 'Color3ub',
+ 'Color3ubv',
+ 'Color3ui',
+ 'Color3uiv',
+ 'Color3us',
+ 'Color3usv',
+ 'Color4b',
+ 'Color4bv',
+ 'Color4d',
+ 'Color4dv',
+ 'Color4f',
+ 'Color4fv',
+ 'Color4i',
+ 'Color4iv',
+ 'Color4s',
+ 'Color4sv',
+ 'Color4ub',
+ 'Color4ubv',
+ 'Color4ui',
+ 'Color4uiv',
+ 'Color4us',
+ 'Color4usv',
+ 'ColorMask',
+ 'ColorMaterial',
+ 'ColorPointer',
+ 'CopyPixels',
+ 'CopyTexImage1D',
+ 'CopyTexImage2D',
+ 'CopyTexSubImage1D',
+ 'CopyTexSubImage2D',
+ 'CullFace',
+ 'DebugEntry',
+ 'DeleteLists',
+ 'DeleteTextures',
+ 'DepthFunc',
+ 'DepthMask',
+ 'DepthRange',
+ 'Disable',
+ 'DisableClientState',
+ 'DrawArrays',
+ 'DrawBuffer',
+ 'DrawElements',
+ 'DrawPixels',
+ 'EdgeFlag',
+ 'EdgeFlagPointer',
+ 'EdgeFlagv',
+ 'Enable',
+ 'EnableClientState',
+ 'End',
+ 'EndList',
+ 'EvalCoord1d',
+ 'EvalCoord1dv',
+ 'EvalCoord1f',
+ 'EvalCoord1fv',
+ 'EvalCoord2d',
+ 'EvalCoord2dv',
+ 'EvalCoord2f',
+ 'EvalCoord2fv',
+ 'EvalMesh1',
+ 'EvalMesh2',
+ 'EvalPoint1',
+ 'EvalPoint2',
+ 'FeedbackBuffer',
+ 'Finish',
+ 'Flush',
+ 'Fogf',
+ 'Fogfv',
+ 'Fogi',
+ 'Fogiv',
+ 'FrontFace',
+ 'Frustum',
+ 'GenLists',
+ 'GenTextures',
+ 'GetBooleanv',
+ 'GetClipPlane',
+ 'GetDoublev',
+ 'GetError',
+ 'GetFloatv',
+ 'GetIntegerv',
+ 'GetLightfv',
+ 'GetLightiv',
+ 'GetMapdv',
+ 'GetMapfv',
+ 'GetMapiv',
+ 'GetMaterialfv',
+ 'GetMaterialiv',
+ 'GetPixelMapfv',
+ 'GetPixelMapuiv',
+ 'GetPixelMapusv',
+ 'GetPointerv',
+ 'GetPolygonStipple',
+ 'GetString',
+ 'GetTexEnvfv',
+ 'GetTexEnviv',
+ 'GetTexGendv',
+ 'GetTexGenfv',
+ 'GetTexGeniv',
+ 'GetTexImage',
+ 'GetTexLevelParameterfv',
+ 'GetTexLevelParameteriv',
+ 'GetTexParameterfv',
+ 'GetTexParameteriv',
+ 'Hint',
+ 'IndexMask',
+ 'IndexPointer',
+ 'Indexd',
+ 'Indexdv',
+ 'Indexf',
+ 'Indexfv',
+ 'Indexi',
+ 'Indexiv',
+ 'Indexs',
+ 'Indexsv',
+ 'Indexub',
+ 'Indexubv',
+ 'InitNames',
+ 'InterleavedArrays',
+ 'IsEnabled',
+ 'IsList',
+ 'IsTexture',
+ 'LightModelf',
+ 'LightModelfv',
+ 'LightModeli',
+ 'LightModeliv',
+ 'Lightf',
+ 'Lightfv',
+ 'Lighti',
+ 'Lightiv',
+ 'LineStipple',
+ 'LineWidth',
+ 'ListBase',
+ 'LoadIdentity',
+ 'LoadMatrixd',
+ 'LoadMatrixf',
+ 'LoadName',
+ 'LogicOp',
+ 'Map1d',
+ 'Map1f',
+ 'Map2d',
+ 'Map2f',
+ 'MapGrid1d',
+ 'MapGrid1f',
+ 'MapGrid2d',
+ 'MapGrid2f',
+ 'Materialf',
+ 'Materialfv',
+ 'Materiali',
+ 'Materialiv',
+ 'MatrixMode',
+ 'MultMatrixd',
+ 'MultMatrixf',
+ 'NewList',
+ 'Normal3b',
+ 'Normal3bv',
+ 'Normal3d',
+ 'Normal3dv',
+ 'Normal3f',
+ 'Normal3fv',
+ 'Normal3i',
+ 'Normal3iv',
+ 'Normal3s',
+ 'Normal3sv',
+ 'NormalPointer',
+ 'Ortho',
+ 'PassThrough',
+ 'PixelMapfv',
+ 'PixelMapuiv',
+ 'PixelMapusv',
+ 'PixelStoref',
+ 'PixelStorei',
+ 'PixelTransferf',
+ 'PixelTransferi',
+ 'PixelZoom',
+ 'PointSize',
+ 'PolygonMode',
+ 'PolygonOffset',
+ 'PolygonStipple',
+ 'PopAttrib',
+ 'PopClientAttrib',
+ 'PopMatrix',
+ 'PopName',
+ 'PrioritizeTextures',
+ 'PushAttrib',
+ 'PushClientAttrib',
+ 'PushMatrix',
+ 'PushName',
+ 'RasterPos2d',
+ 'RasterPos2dv',
+ 'RasterPos2f',
+ 'RasterPos2fv',
+ 'RasterPos2i',
+ 'RasterPos2iv',
+ 'RasterPos2s',
+ 'RasterPos2sv',
+ 'RasterPos3d',
+ 'RasterPos3dv',
+ 'RasterPos3f',
+ 'RasterPos3fv',
+ 'RasterPos3i',
+ 'RasterPos3iv',
+ 'RasterPos3s',
+ 'RasterPos3sv',
+ 'RasterPos4d',
+ 'RasterPos4dv',
+ 'RasterPos4f',
+ 'RasterPos4fv',
+ 'RasterPos4i',
+ 'RasterPos4iv',
+ 'RasterPos4s',
+ 'RasterPos4sv',
+ 'ReadBuffer',
+ 'ReadPixels',
+ 'Rectd',
+ 'Rectdv',
+ 'Rectf',
+ 'Rectfv',
+ 'Recti',
+ 'Rectiv',
+ 'Rects',
+ 'Rectsv',
+ 'RenderMode',
+ 'Rotated',
+ 'Rotatef',
+ 'Scaled',
+ 'Scalef',
+ 'Scissor',
+ 'SelectBuffer',
+ 'ShadeModel',
+ 'StencilFunc',
+ 'StencilMask',
+ 'StencilOp',
+ 'TexCoord1d',
+ 'TexCoord1dv',
+ 'TexCoord1f',
+ 'TexCoord1fv',
+ 'TexCoord1i',
+ 'TexCoord1iv',
+ 'TexCoord1s',
+ 'TexCoord1sv',
+ 'TexCoord2d',
+ 'TexCoord2dv',
+ 'TexCoord2f',
+ 'TexCoord2fv',
+ 'TexCoord2i',
+ 'TexCoord2iv',
+ 'TexCoord2s',
+ 'TexCoord2sv',
+ 'TexCoord3d',
+ 'TexCoord3dv',
+ 'TexCoord3f',
+ 'TexCoord3fv',
+ 'TexCoord3i',
+ 'TexCoord3iv',
+ 'TexCoord3s',
+ 'TexCoord3sv',
+ 'TexCoord4d',
+ 'TexCoord4dv',
+ 'TexCoord4f',
+ 'TexCoord4fv',
+ 'TexCoord4i',
+ 'TexCoord4iv',
+ 'TexCoord4s',
+ 'TexCoord4sv',
+ 'TexCoordPointer',
+ 'TexEnvf',
+ 'TexEnvfv',
+ 'TexEnvi',
+ 'TexEnviv',
+ 'TexGend',
+ 'TexGendv',
+ 'TexGenf',
+ 'TexGenfv',
+ 'TexGeni',
+ 'TexGeniv',
+ 'TexImage1D',
+ 'TexImage2D',
+ 'TexImage3D',
+ 'TexParameterf',
+ 'TexParameterfv',
+ 'TexParameteri',
+ 'TexParameteriv',
+ 'TexSubImage1D',
+ 'TexSubImage2D',
+ 'Translated',
+ 'Translatef',
+ 'Vertex2d',
+ 'Vertex2dv',
+ 'Vertex2f',
+ 'Vertex2fv',
+ 'Vertex2i',
+ 'Vertex2iv',
+ 'Vertex2s',
+ 'Vertex2sv',
+ 'Vertex3d',
+ 'Vertex3dv',
+ 'Vertex3f',
+ 'Vertex3fv',
+ 'Vertex3i',
+ 'Vertex3iv',
+ 'Vertex3s',
+ 'Vertex3sv',
+ 'Vertex4d',
+ 'Vertex4dv',
+ 'Vertex4f',
+ 'Vertex4fv',
+ 'Vertex4i',
+ 'Vertex4iv',
+ 'Vertex4s',
+ 'Vertex4sv',
+ 'VertexPointer',
+ 'Viewport',
+ 'wglChoosePixelFormat',
+ 'wglCopyContext',
+ 'wglCreateContext',
+ 'wglCreateLayerContext',
+ 'wglDeleteContext',
+ 'wglDescribeLayerPlane',
+ 'wglDescribePixelFormat',
+ 'wglGetCurrentContext',
+ 'wglGetCurrentDC',
+ 'wglGetDefaultProcAddress',
+ 'wglGetLayerPaletteEntries',
+ 'wglGetPixelFormat',
+ 'wglGetProcAddress',
+ 'wglMakeCurrent',
+ 'wglRealizeLayerPalette',
+ 'wglSetLayerPaletteEntries',
+ 'wglSetPixelFormat',
+ 'wglShareLists',
+ 'wglSwapBuffers',
+ 'wglSwapLayerBuffers',
+ 'wglSwapMultipleBuffers',
+ 'wglUseFontBitmapsA',
+ 'wglUseFontBitmapsW',
+ 'wglUseFontOutlinesA',
+ 'wglUseFontOutlinesW',
+ 'wglChoosePixelFormatEXT',
+ 'wglGetPixelFormatAttribivEXT',
+ 'wglGetPixelFormatAttribfvEXT',
+ 'wglGetExtensionsStringEXT',
+ 'CopyContext',
+ 'CreateContext',
+ 'CreateLayerContext',
+ 'DeleteContext',
+ 'DescribeLayerPlane',
+ 'DescribePixelFormat',
+ 'GetLayerPaletteEntries',
+ 'GetProcAddress',
+ 'RealizeLayerPalette',
+ 'ReleaseContext',
+ 'SetContext',
+ 'SetLayerPaletteEntries',
+ 'SetPixelFormat',
+ 'ShareLists',
+ 'SwapBuffers',
+ 'SwapLayerBuffers',
+ 'ValidateVersion',
+]
+
+noexport_special = [
+ "BoundsInfoCR",
+ "CreateContext",
+ "DestroyContext",
+ "MakeCurrent",
+ "WindowCreate",
+ "WindowDestroy",
+ "WindowSize",
+ "WindowPosition",
+ "WindowVisibleRegion",
+ "WindowShow",
+ "SwapBuffers"
+]
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if func_name in noexport_special:
+ continue
+ if func_name in exports_special:
+ print("gl%s = cr_gl%s" % (func_name,func_name))
+
+for func_name in ( "wglChoosePixelFormat",
+ "wglCopyContext",
+ "wglCreateContext",
+ "wglCreateLayerContext",
+ "wglDeleteContext",
+ "wglDescribeLayerPlane",
+ "wglDescribePixelFormat",
+ "wglGetCurrentContext",
+ "wglGetCurrentDC",
+ "wglGetLayerPaletteEntries",
+ "wglGetPixelFormat",
+ "wglGetProcAddress",
+ "wglMakeCurrent",
+ "wglRealizeLayerPalette",
+ "wglSetLayerPaletteEntries",
+ "wglSetPixelFormat",
+ "wglShareLists",
+ "wglSwapBuffers",
+ "wglSwapLayerBuffers",
+ "wglSwapMultipleBuffers",
+ "wglUseFontBitmapsA",
+ "wglUseFontBitmapsW",
+ "wglUseFontOutlinesA",
+ "wglUseFontOutlinesW",
+ "wglChoosePixelFormatEXT",
+ "wglGetPixelFormatAttribivEXT",
+ "wglGetPixelFormatAttribfvEXT",
+ "wglGetExtensionsStringEXT"):
+ print("%s = %s_prox" % (func_name,func_name))
+
+print("""DrvCopyContext
+DrvCreateContext
+DrvCreateLayerContext
+DrvDeleteContext
+DrvDescribeLayerPlane
+DrvDescribePixelFormat
+DrvGetLayerPaletteEntries
+DrvGetProcAddress = wglGetProcAddress_prox
+DrvRealizeLayerPalette
+DrvSetLayerPaletteEntries
+DrvShareLists
+DrvSwapBuffers
+DrvSwapLayerBuffers
+DrvReleaseContext = DrvReleaseContext
+DrvSetContext = DrvSetContext
+DrvValidateVersion = DrvValidateVersion
+DrvSetPixelFormat = DrvSetPixelFormat""")
+
+print("""crCreateContext
+crMakeCurrent
+crSwapBuffers
+crGetProcAddress
+VBoxCreateContext
+VBoxCtxChromiumParameteriCR
+VBoxGetWindowId
+VBoxGetContextId
+VBoxFlushToHost""")
+#print "DllMain"
diff --git a/src/VBox/Additions/common/crOpenGL/dri_drv.c b/src/VBox/Additions/common/crOpenGL/dri_drv.c
new file mode 100644
index 00000000..de206624
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/dri_drv.c
@@ -0,0 +1,1012 @@
+/* $Id: dri_drv.c $ */
+
+/** @file
+ * VBox OpenGL DRI driver functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This file is based in part on the tdfx driver from X.Org/Mesa, with the
+ * following copyright notice:
+ *
+ * Copyright 2000 VA Linux Systems Inc., Fremont, California.
+ *
+ * All Rights Reserved.
+ *
+ * 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 (including the next
+ * paragraph) 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
+ * VA LINUX SYSTEMS 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.
+ *
+ * Original rewrite:
+ * Gareth Hughes <gareth@valinux.com>, 29 Sep - 1 Oct 2000
+ *
+ * Authors:
+ * Gareth Hughes <gareth@valinux.com>
+ */
+
+#include "cr_error.h"
+#include "cr_gl.h"
+#include "stub.h"
+#include "dri_drv.h"
+#include "DD_gl.h"
+
+/** @todo some of those are or'ed with GL_VERSIONS and ain't needed here*/
+#define need_GL_ARB_occlusion_query
+#define need_GL_ARB_point_parameters
+#define need_GL_NV_point_sprite
+#define need_GL_ARB_texture_compression
+#define need_GL_ARB_transpose_matrix
+#define need_GL_ARB_vertex_buffer_object
+#define need_GL_ARB_vertex_program
+#define need_GL_ARB_window_pos
+#define need_GL_EXT_blend_color
+#define need_GL_EXT_blend_minmax
+#define need_GL_EXT_blend_func_separate
+#define need_GL_EXT_fog_coord
+#define need_GL_EXT_multi_draw_arrays
+#define need_GL_EXT_secondary_color
+#define need_GL_EXT_texture_object
+#define need_GL_EXT_texture3D
+#define need_GL_VERSION_1_3
+#define need_GL_VERSION_1_4
+#define need_GL_VERSION_1_5
+
+#include "drivers/dri/common/extension_helper.h"
+
+/** @todo add more which are supported by chromium like GL_NV_vertex_program etc.*/
+static const struct dri_extension vbox_extensions[] = {
+ { "GL_ARB_depth_texture", NULL },
+ { "GL_ARB_fragment_program", NULL },
+ { "GL_ARB_multitexture", NULL },
+ { "GL_ARB_occlusion_query", GL_ARB_occlusion_query_functions },
+ { "GL_ARB_point_parameters", GL_ARB_point_parameters_functions },
+ { "GL_NV_point_sprite", GL_NV_point_sprite_functions },
+ { "GL_ARB_shadow", NULL },
+ { "GL_ARB_shadow_ambient", NULL },
+ { "GL_ARB_texture_border_clamp", NULL },
+ { "GL_ARB_texture_compression", GL_ARB_texture_compression_functions },
+ { "GL_ARB_texture_cube_map", NULL },
+ { "GL_ARB_texture_env_add", NULL },
+ { "GL_ARB_texture_env_combine", NULL },
+ { "GL_EXT_texture_env_combine", NULL },
+ { "GL_ARB_texture_env_crossbar", NULL },
+ { "GL_ARB_texture_env_dot3", NULL },
+ { "GL_EXT_texture_env_dot3", NULL },
+ { "GL_ARB_texture_mirrored_repeat", NULL },
+ { "GL_ARB_texture_non_power_of_two", NULL },
+ { "GL_ARB_transpose_matrix", GL_ARB_transpose_matrix_functions },
+ { "GL_ARB_vertex_buffer_object", GL_ARB_vertex_buffer_object_functions },
+ { "GL_ARB_vertex_program", GL_ARB_vertex_program_functions },
+ { "GL_ARB_window_pos", GL_ARB_window_pos_functions },
+ { "GL_EXT_blend_color", GL_EXT_blend_color_functions },
+ { "GL_EXT_blend_minmax", GL_EXT_blend_minmax_functions },
+ { "GL_EXT_blend_func_separate", GL_EXT_blend_func_separate_functions },
+ { "GL_EXT_blend_subtract", NULL },
+ { "GL_EXT_texture_env_add", NULL }, /** @todo that's an alias to GL_ARB version, remove it?*/
+ { "GL_EXT_fog_coord", GL_EXT_fog_coord_functions },
+ { "GL_EXT_multi_draw_arrays", GL_EXT_multi_draw_arrays_functions },
+ { "GL_EXT_secondary_color", GL_EXT_secondary_color_functions },
+ { "GL_EXT_shadow_funcs", NULL },
+ { "GL_EXT_stencil_wrap", NULL },
+ { "GL_EXT_texture_cube_map", NULL }, /** @todo another alias*/
+ { "GL_EXT_texture_edge_clamp", NULL },
+ { "GL_EXT_texture_filter_anisotropic", NULL },
+ { "GL_EXT_texture_lod_bias", NULL },
+ { "GL_EXT_texture_object", GL_EXT_texture_object_functions },
+ { "GL_EXT_texture3D", GL_EXT_texture3D_functions },
+ { "GL_NV_texgen_reflection", NULL },
+ { "GL_ARB_texture_rectangle", NULL },
+ { "GL_SGIS_generate_mipmap", NULL },
+ { "GL_SGIS_texture_edge_clamp", NULL } /** @todo another alias*/
+};
+
+static void
+vboxdriInitExtensions(GLcontext * ctx)
+{
+ /** @todo have to check extensions supported by host here first */
+ driInitExtensions(ctx, vbox_extensions, GL_FALSE);
+}
+
+static GLvertexformat vboxdriTnlVtxFmt;
+
+
+/* This callback tells us that Mesa's internal state has changed. We probably
+ * don't need to handle this ourselves, so just pass it on to other parts of
+ * Mesa we may be using, as the swrast driver and others do */
+static void
+vboxDDUpdateState(GLcontext * ctx, GLuint new_state)
+{
+ _swrast_InvalidateState(ctx, new_state);
+ _swsetup_InvalidateState(ctx, new_state);
+ _vbo_InvalidateState(ctx, new_state);
+ _tnl_InvalidateState(ctx, new_state);
+}
+
+#if 0 /* See comment in vboxdriInitFuncs */
+static void
+vboxDDGetBufferSize(GLframebuffer *buffer, GLuint *width, GLuint *height)
+{
+ /*do something, note it's obsolete*/
+}
+
+static void
+vboxDDResizeBuffer( GLcontext *ctx, GLframebuffer *fb,
+ GLuint width, GLuint height)
+{
+ /*do something, note it's obsolete*/
+}
+
+static void
+vboxDDError(GLcontext *ctx)
+{
+ //__GLcontextRec::ErrorValue contains the error value.
+}
+#endif
+
+static void
+vboxDDDrawPixels(GLcontext *ctx,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ const struct gl_pixelstore_attrib *unpack,
+ const GLvoid *pixels)
+{
+}
+
+static void
+vboxDDReadPixels(GLcontext *ctx,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ const struct gl_pixelstore_attrib *unpack,
+ GLvoid *dest)
+{
+}
+
+static void
+vboxDDCopyPixels(GLcontext *ctx, GLint srcx, GLint srcy,
+ GLsizei width, GLsizei height,
+ GLint dstx, GLint dsty, GLenum type)
+{
+}
+
+static void
+vboxDDBitmap(GLcontext *ctx,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ const struct gl_pixelstore_attrib *unpack,
+ const GLubyte *bitmap)
+{
+}
+
+static void
+vboxDDTexImage1D(GLcontext *ctx, GLenum target, GLint level,
+ GLint internalFormat,
+ GLint width, GLint border,
+ GLenum format, GLenum type, const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+static void
+vboxDDTexImage2D(GLcontext *ctx, GLenum target, GLint level,
+ GLint internalFormat,
+ GLint width, GLint height, GLint border,
+ GLenum format, GLenum type, const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+static void
+vboxDDTexImage3D(GLcontext *ctx, GLenum target, GLint level,
+ GLint internalFormat,
+ GLint width, GLint height, GLint depth, GLint border,
+ GLenum format, GLenum type, const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+static void
+vboxDDTexSubImage1D(GLcontext *ctx, GLenum target, GLint level,
+ GLint xoffset, GLsizei width,
+ GLenum format, GLenum type,
+ const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+static void
+vboxDDTexSubImage2D(GLcontext *ctx, GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+
+static void
+vboxDDTexSubImage3D(GLcontext *ctx, GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint zoffset,
+ GLsizei width, GLsizei height, GLint depth,
+ GLenum format, GLenum type,
+ const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+
+static void
+vboxDDGetTexImage(GLcontext *ctx, GLenum target, GLint level,
+ GLenum format, GLenum type, GLvoid *pixels,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+}
+
+static void
+vboxDDBindTexture(GLcontext *ctx, GLenum target,
+ struct gl_texture_object *tObj)
+{
+}
+
+static GLboolean
+vboxDDIsTextureResident(GLcontext *ctx, struct gl_texture_object *t)
+{
+}
+
+static void
+vboxDDPrioritizeTexture(GLcontext *ctx, struct gl_texture_object *t,
+ GLclampf priority)
+{
+}
+
+static void
+vboxDDBlendColor(GLcontext *ctx, const GLfloat color[4])
+{
+}
+
+static void
+vboxDDClearColor(GLcontext *ctx, const GLfloat color[4])
+{
+}
+
+static void
+vboxDDClearIndex(GLcontext *ctx, GLuint index)
+{
+}
+
+static void
+vboxDDClipPlane(GLcontext *ctx, GLenum plane, const GLfloat *equation)
+{
+}
+
+/** @todo Enable or disable server-side gl capabilities, not related to glEnable? */
+static void
+vboxDDEnable(GLcontext *ctx, GLenum cap, GLboolean state)
+{
+ if (state)
+ cr_glEnable(cap);
+ else
+ cr_glDisable(cap);
+}
+
+static void
+vboxDDRenderMode(GLcontext *ctx, GLenum mode)
+{
+ cr_glRenderMode(mode);
+}
+
+static void
+vboxDDTexParameter(GLcontext *ctx, GLenum target,
+ struct gl_texture_object *texObj,
+ GLenum pname, const GLfloat *params)
+{
+}
+
+/*Note, checking glGetError before and after those calls is the only way
+ *to return if we succeeded to get value or not, but it will add 2 sync calls and
+ *will reset glGetError value returned in case application calls it explicitly
+ */
+static GLboolean
+vboxDDGetBooleanv(GLcontext *ctx, GLenum pname, GLboolean *result)
+{
+ cr_glGetBooleanv(pname, result);
+ return GL_TRUE;
+}
+
+static GLboolean
+vboxDDGetDoublev(GLcontext *ctx, GLenum pname, GLdouble *result)
+{
+ cr_glGetDoublev(pname, result);
+ return GL_TRUE;
+}
+
+static GLboolean
+vboxDDGetFloatv(GLcontext *ctx, GLenum pname, GLfloat *result)
+{
+ cr_glGetFloatv(pname, result);
+ return GL_TRUE;
+}
+
+static GLboolean
+vboxDDGetIntegerv(GLcontext *ctx, GLenum pname, GLint *result)
+{
+ cr_glGetIntegerv(pname, result);
+ return GL_TRUE;
+}
+
+static GLboolean
+vboxDDGetPointerv(GLcontext *ctx, GLenum pname, GLvoid **result)
+{
+ cr_glGetPointerv(pname, result);
+ return GL_TRUE;
+}
+
+/** @todo
+ * change stub's createcontext to reuse driver private part of mesa's ctx to store stub ctx info.
+ */
+#define VBOX_GL_FUNC(func) vboxDD_gl##func
+static void
+vboxdriInitFuncs(struct dd_function_table *driver)
+{
+ driver->GetString = VBOX_GL_FUNC(GetString);
+ driver->UpdateState = vboxDDUpdateState;
+#if 0
+ /* I assume that we don't need to change these. In that case, prefer the
+ * default implementation over a stub. */
+ driver->GetBufferSize = vboxDDGetBufferSize;
+ driver->ResizeBuffers = vboxDDResizeBuffer;
+ driver->Error = vboxDDError;
+#endif
+
+ driver->Finish = VBOX_GL_FUNC(Finish);
+ driver->Flush = VBOX_GL_FUNC(Flush);
+
+ /* framebuffer/image functions */
+ driver->Clear = VBOX_GL_FUNC(Clear);
+ driver->Accum = VBOX_GL_FUNC(Accum);
+ // driver->RasterPos = VBOX_GL_FUNC(RasterPos); /* No such element in *driver */
+ driver->DrawPixels = vboxDDDrawPixels;
+ driver->ReadPixels = vboxDDReadPixels;
+ driver->CopyPixels = vboxDDCopyPixels;
+ driver->Bitmap = vboxDDBitmap;
+
+ /* Texture functions */
+ /** @todo deal with texnames and gl_texture_object pointers which are passed here*/
+ driver->ChooseTextureFormat = NULL;
+ driver->TexImage1D = vboxDDTexImage1D;
+ driver->TexImage2D = vboxDDTexImage2D;
+ driver->TexImage3D = vboxDDTexImage3D;
+ driver->TexSubImage1D = vboxDDTexSubImage1D;
+ driver->TexSubImage2D = vboxDDTexSubImage2D;
+ driver->TexSubImage3D = vboxDDTexSubImage3D;
+ driver->GetTexImage = vboxDDGetTexImage;
+ driver->CopyTexImage1D = VBOX_GL_FUNC(CopyTexImage1D);
+ driver->CopyTexImage2D = VBOX_GL_FUNC(CopyTexImage2D);
+ driver->CopyTexSubImage1D = VBOX_GL_FUNC(CopyTexSubImage1D);
+ driver->CopyTexSubImage2D = VBOX_GL_FUNC(CopyTexSubImage2D);
+ driver->CopyTexSubImage3D = VBOX_GL_FUNC(CopyTexSubImage3D);
+ // driver->GenerateMipmap = VBOX_GL_FUNC(GenerateMipmap); /** @todo or NULL */
+ // driver->TestProxyTexImage = vboxDDTestProxyTexImage; /** @todo just pass to glTexImage as we take care or proxy textures there */
+ // driver->CompressedTexImage1D = VBOX_GL_FUNC(CompressedTexImage1D);
+ // driver->CompressedTexImage2D = VBOX_GL_FUNC(CompressedTexImage2D);
+ // driver->CompressedTexImage3D = VBOX_GL_FUNC(CompressedTexImage3D);
+ // driver->CompressedTexSubImage1D = VBOX_GL_FUNC(CompressedTexSubImage1D);
+ // driver->CompressedTexSubImage2D = VBOX_GL_FUNC(CompressedTexSubImage2D);
+ // driver->CompressedTexSubImage3D = VBOX_GL_FUNC(CompressedTexSubImage3D);
+ // driver->GetCompressedTexImage = VBOX_GL_FUNC(GetCompressedTexImage);
+ // driver->CompressedTextureSize = NULL; /** @todo */
+ driver->BindTexture = vboxDDBindTexture;
+ // driver->NewTextureObject = vboxDDNewTextureObject; /** @todo */
+ // driver->DeleteTexture = vboxDDDeleteTexture; /** @todo */
+ // driver->NewTextureImage = vboxDDNewTextureImage; /** @todo */
+ // driver->FreeTexImageData = vboxDDFreeTexImageData; /** @todo */
+ // driver->MapTexture = vboxDDMapTexture; /** @todo */
+ // driver->UnmapTexture = vboxDDUnmapTexture; /** @todo */
+ // driver->TextureMemCpy = vboxDDTextureMemCpy; /** @todo */
+ driver->IsTextureResident = vboxDDIsTextureResident;
+ driver->PrioritizeTexture = vboxDDPrioritizeTexture;
+ driver->ActiveTexture = VBOX_GL_FUNC(ActiveTextureARB);
+ // driver->UpdateTexturePalette = vboxDDUpdateTexturePalette; /** @todo */
+
+ /* imaging */
+ /*driver->CopyColorTable = _swrast_CopyColorTable;
+ driver->CopyColorSubTable = _swrast_CopyColorSubTable;
+ driver->CopyConvolutionFilter1D = _swrast_CopyConvolutionFilter1D;
+ driver->CopyConvolutionFilter2D = _swrast_CopyConvolutionFilter2D;*/
+
+ /* Vertex/fragment programs */
+ driver->BindProgram = NULL;
+ // driver->NewProgram = _mesa_new_program; /** @todo */
+ // driver->DeleteProgram = _mesa_delete_program; /** @todo */
+ driver->ProgramStringNotify = NULL;
+#if FEATURE_MESA_program_debug
+ // driver->GetProgramRegister = _mesa_get_program_register; /** @todo */
+#endif /* FEATURE_MESA_program_debug */
+ driver->IsProgramNative = NULL;
+
+ /* simple state commands */
+ driver->AlphaFunc = VBOX_GL_FUNC(AlphaFunc);
+ driver->BlendColor = vboxDDBlendColor;
+ // driver->BlendEquationSeparate = VBOX_GL_FUNC(BlendEquationSeparate); /** @todo */
+ driver->BlendFuncSeparate = VBOX_GL_FUNC(BlendFuncSeparateEXT);
+ driver->ClearColor = vboxDDClearColor;
+ driver->ClearDepth = VBOX_GL_FUNC(ClearDepth);
+ driver->ClearIndex = vboxDDClearIndex;
+ driver->ClearStencil = VBOX_GL_FUNC(ClearStencil);
+ driver->ClipPlane = vboxDDClipPlane;
+ driver->ColorMask = VBOX_GL_FUNC(ColorMask);
+ driver->ColorMaterial = VBOX_GL_FUNC(ColorMaterial);
+ driver->CullFace = VBOX_GL_FUNC(CullFace);
+ driver->DrawBuffer = VBOX_GL_FUNC(DrawBuffer); /** @todo */
+ // driver->DrawBuffers = VBOX_GL_FUNC(DrawBuffers); /** @todo */
+ driver->FrontFace = VBOX_GL_FUNC(FrontFace);
+ driver->DepthFunc = VBOX_GL_FUNC(DepthFunc);
+ driver->DepthMask = VBOX_GL_FUNC(DepthMask);
+ driver->DepthRange = VBOX_GL_FUNC(DepthRange);
+ driver->Enable = vboxDDEnable;
+ driver->Fogfv = VBOX_GL_FUNC(Fogfv);
+ driver->Hint = VBOX_GL_FUNC(Hint);
+ driver->IndexMask = VBOX_GL_FUNC(IndexMask);
+ driver->Lightfv = VBOX_GL_FUNC(Lightfv);
+ driver->LightModelfv = VBOX_GL_FUNC(LightModelfv);
+ driver->LineStipple = VBOX_GL_FUNC(LineStipple);
+ driver->LineWidth = VBOX_GL_FUNC(LineWidth);
+ // driver->LogicOpcode = VBOX_GL_FUNC(LogicOpcode); /** @todo */
+ driver->PointParameterfv = VBOX_GL_FUNC(PointParameterfvARB);
+ driver->PointSize = VBOX_GL_FUNC(PointSize);
+ driver->PolygonMode = VBOX_GL_FUNC(PolygonMode);
+ driver->PolygonOffset = VBOX_GL_FUNC(PolygonOffset);
+ driver->PolygonStipple = VBOX_GL_FUNC(PolygonStipple);
+ driver->ReadBuffer = VBOX_GL_FUNC(ReadBuffer);
+ driver->RenderMode = vboxDDRenderMode;
+ driver->Scissor = VBOX_GL_FUNC(Scissor);
+ driver->ShadeModel = VBOX_GL_FUNC(ShadeModel);
+ // driver->StencilFuncSeparate = VBOX_GL_FUNC(StencilFuncSeparate); /** @todo */
+ // driver->StencilOpSeparate = VBOX_GL_FUNC(StencilOpSeparate); /** @todo */
+ // driver->StencilMaskSeparate = VBOX_GL_FUNC(StencilMaskSeparate); /** @todo */
+ driver->TexGen = VBOX_GL_FUNC(TexGenfv);
+ driver->TexEnv = VBOX_GL_FUNC(TexEnvfv);
+ driver->TexParameter = vboxDDTexParameter;
+ // driver->TextureMatrix = VBOX_GL_FUNC(TextureMatrix); /** @todo */
+ driver->Viewport = VBOX_GL_FUNC(Viewport);
+
+ /* vertex arrays */
+ driver->VertexPointer = VBOX_GL_FUNC(VertexPointer);
+ driver->NormalPointer = VBOX_GL_FUNC(NormalPointer);
+ driver->ColorPointer = VBOX_GL_FUNC(ColorPointer);
+ driver->FogCoordPointer = VBOX_GL_FUNC(FogCoordPointerEXT);
+ driver->IndexPointer = VBOX_GL_FUNC(IndexPointer);
+ driver->SecondaryColorPointer = VBOX_GL_FUNC(SecondaryColorPointerEXT);
+ driver->TexCoordPointer = VBOX_GL_FUNC(TexCoordPointer);
+ driver->EdgeFlagPointer = VBOX_GL_FUNC(EdgeFlagPointer);
+ // driver->VertexAttribPointer = VBOX_GL_FUNC(VertexAttribPointer); /** @todo */
+ // driver->LockArraysEXT = VBOX_GL_FUNC(LockArraysEXT); /** @todo */
+ // driver->UnlockArraysEXT = VBOX_GL_FUNC(UnlockArraysEXT); /** @todo */
+
+ /* state queries */
+ driver->GetBooleanv = vboxDDGetBooleanv;
+ driver->GetDoublev = vboxDDGetDoublev;
+ driver->GetFloatv = vboxDDGetFloatv;
+ driver->GetIntegerv = vboxDDGetIntegerv;
+ driver->GetPointerv = vboxDDGetPointerv;
+
+/** @todo */
+#if FEATURE_ARB_vertex_buffer_object
+ // driver->NewBufferObject = _mesa_new_buffer_object;
+ // driver->DeleteBuffer = _mesa_delete_buffer_object;
+ // driver->BindBuffer = NULL;
+ // driver->BufferData = _mesa_buffer_data;
+ // driver->BufferSubData = _mesa_buffer_subdata;
+ // driver->GetBufferSubData = _mesa_buffer_get_subdata;
+ // driver->MapBuffer = _mesa_buffer_map;
+ // driver->UnmapBuffer = _mesa_buffer_unmap;
+#endif
+
+/** @todo */
+#if FEATURE_EXT_framebuffer_object
+ // driver->NewFramebuffer = _mesa_new_framebuffer;
+ // driver->NewRenderbuffer = _mesa_new_soft_renderbuffer;
+ // driver->RenderTexture = _mesa_render_texture;
+ // driver->FinishRenderTexture = _mesa_finish_render_texture;
+ // driver->FramebufferRenderbuffer = _mesa_framebuffer_renderbuffer;
+#endif
+
+/** @todo */
+#if FEATURE_EXT_framebuffer_blit
+ // driver->BlitFramebuffer = _swrast_BlitFramebuffer;
+#endif
+
+ /* query objects */
+ // driver->NewQueryObject = VBOX_GL_FUNC(NewQueryObject); /** @todo */
+ // driver->DeleteQuery = VBOX_GL_FUNC(DeleteQuery); /** @todo */
+ // driver->BeginQuery = VBOX_GL_FUNC(BeginQuery); /** @todo */
+ // driver->EndQuery = VBOX_GL_FUNC(EndQuery); /** @todo */
+ // driver->WaitQuery = VBOX_GL_FUNC(WaitQuery); /** @todo */
+
+ /* APPLE_vertex_array_object */
+/*
+ driver->NewArrayObject = _mesa_new_array_object;
+ driver->DeleteArrayObject = _mesa_delete_array_object;
+ driver->BindArrayObject = NULL;
+*/
+
+ /* T&L stuff */
+ driver->NeedValidate = GL_FALSE;
+ driver->ValidateTnlModule = NULL;
+ driver->CurrentExecPrimitive = 0;
+ driver->CurrentSavePrimitive = 0;
+ driver->NeedFlush = 0;
+ driver->SaveNeedFlush = 0;
+
+ // driver->ProgramStringNotify = _tnl_program_string; /** @todo */
+ driver->FlushVertices = NULL;
+ driver->SaveFlushVertices = NULL;
+ driver->NotifySaveBegin = NULL;
+ driver->LightingSpaceChange = NULL;
+
+ /* display list */
+ driver->NewList = VBOX_GL_FUNC(NewList);
+ driver->EndList = VBOX_GL_FUNC(EndList);
+ // driver->BeginCallList = VBOX_GL_FUNC(BeginCallList); /** @todo */
+ // driver->EndCallList = VBOX_GL_FUNC(EndCallList); /** @todo */
+
+
+ /* shaders */
+ /*
+ driver->AttachShader = _mesa_attach_shader;
+ driver->BindAttribLocation = _mesa_bind_attrib_location;
+ driver->CompileShader = _mesa_compile_shader;
+ driver->CreateProgram = _mesa_create_program;
+ driver->CreateShader = _mesa_create_shader;
+ driver->DeleteProgram2 = _mesa_delete_program2;
+ driver->DeleteShader = _mesa_delete_shader;
+ driver->DetachShader = _mesa_detach_shader;
+ driver->GetActiveAttrib = _mesa_get_active_attrib;
+ driver->GetActiveUniform = _mesa_get_active_uniform;
+ driver->GetAttachedShaders = _mesa_get_attached_shaders;
+ driver->GetAttribLocation = _mesa_get_attrib_location;
+ driver->GetHandle = _mesa_get_handle;
+ driver->GetProgramiv = _mesa_get_programiv;
+ driver->GetProgramInfoLog = _mesa_get_program_info_log;
+ driver->GetShaderiv = _mesa_get_shaderiv;
+ driver->GetShaderInfoLog = _mesa_get_shader_info_log;
+ driver->GetShaderSource = _mesa_get_shader_source;
+ driver->GetUniformfv = _mesa_get_uniformfv;
+ driver->GetUniformiv = _mesa_get_uniformiv;
+ driver->GetUniformLocation = _mesa_get_uniform_location;
+ driver->IsProgram = _mesa_is_program;
+ driver->IsShader = _mesa_is_shader;
+ driver->LinkProgram = _mesa_link_program;
+ driver->ShaderSource = _mesa_shader_source;
+ driver->Uniform = _mesa_uniform;
+ driver->UniformMatrix = _mesa_uniform_matrix;
+ driver->UseProgram = _mesa_use_program;
+ driver->ValidateProgram = _mesa_validate_program;
+ */
+}
+
+static void
+vboxdriInitTnlVtxFmt(GLvertexformat *pVtxFmt)
+{
+ pVtxFmt->ArrayElement = VBOX_GL_FUNC(ArrayElement);
+ pVtxFmt->Begin = VBOX_GL_FUNC(Begin);
+ pVtxFmt->CallList = VBOX_GL_FUNC(CallList);
+ pVtxFmt->CallLists = VBOX_GL_FUNC(CallLists);
+ pVtxFmt->Color3f = VBOX_GL_FUNC(Color3f);
+ pVtxFmt->Color3fv = VBOX_GL_FUNC(Color3fv);
+ pVtxFmt->Color4f = VBOX_GL_FUNC(Color4f);
+ pVtxFmt->Color4fv = VBOX_GL_FUNC(Color4fv);
+ pVtxFmt->EdgeFlag = VBOX_GL_FUNC(EdgeFlag);
+ pVtxFmt->End = VBOX_GL_FUNC(End);
+ pVtxFmt->EvalCoord1f = VBOX_GL_FUNC(EvalCoord1f);
+ pVtxFmt->EvalCoord1fv = VBOX_GL_FUNC(EvalCoord1fv);
+ pVtxFmt->EvalCoord2f = VBOX_GL_FUNC(EvalCoord2f);
+ pVtxFmt->EvalCoord2fv = VBOX_GL_FUNC(EvalCoord2fv);
+ pVtxFmt->EvalPoint1 = VBOX_GL_FUNC(EvalPoint1);
+ pVtxFmt->EvalPoint2 = VBOX_GL_FUNC(EvalPoint2);
+ pVtxFmt->FogCoordfEXT = VBOX_GL_FUNC(FogCoordfEXT);
+ pVtxFmt->FogCoordfvEXT = VBOX_GL_FUNC(FogCoordfvEXT);
+ pVtxFmt->Indexf = VBOX_GL_FUNC(Indexf);
+ pVtxFmt->Indexfv = VBOX_GL_FUNC(Indexfv);
+ pVtxFmt->Materialfv = VBOX_GL_FUNC(Materialfv);
+ pVtxFmt->MultiTexCoord1fARB = VBOX_GL_FUNC(MultiTexCoord1fARB);
+ pVtxFmt->MultiTexCoord1fvARB = VBOX_GL_FUNC(MultiTexCoord1fvARB);
+ pVtxFmt->MultiTexCoord2fARB = VBOX_GL_FUNC(MultiTexCoord2fARB);
+ pVtxFmt->MultiTexCoord2fvARB = VBOX_GL_FUNC(MultiTexCoord2fvARB);
+ pVtxFmt->MultiTexCoord3fARB = VBOX_GL_FUNC(MultiTexCoord3fARB);
+ pVtxFmt->MultiTexCoord3fvARB = VBOX_GL_FUNC(MultiTexCoord3fvARB);
+ pVtxFmt->MultiTexCoord4fARB = VBOX_GL_FUNC(MultiTexCoord4fARB);
+ pVtxFmt->MultiTexCoord4fvARB = VBOX_GL_FUNC(MultiTexCoord4fvARB);
+ pVtxFmt->Normal3f = VBOX_GL_FUNC(Normal3f);
+ pVtxFmt->Normal3fv = VBOX_GL_FUNC(Normal3fv);
+ pVtxFmt->SecondaryColor3fEXT = VBOX_GL_FUNC(SecondaryColor3fEXT);
+ pVtxFmt->SecondaryColor3fvEXT = VBOX_GL_FUNC(SecondaryColor3fvEXT);
+ pVtxFmt->TexCoord1f = VBOX_GL_FUNC(TexCoord1f);
+ pVtxFmt->TexCoord1fv = VBOX_GL_FUNC(TexCoord1fv);
+ pVtxFmt->TexCoord2f = VBOX_GL_FUNC(TexCoord2f);
+ pVtxFmt->TexCoord2fv = VBOX_GL_FUNC(TexCoord2fv);
+ pVtxFmt->TexCoord3f = VBOX_GL_FUNC(TexCoord3f);
+ pVtxFmt->TexCoord3fv = VBOX_GL_FUNC(TexCoord3fv);
+ pVtxFmt->TexCoord4f = VBOX_GL_FUNC(TexCoord4f);
+ pVtxFmt->TexCoord4fv = VBOX_GL_FUNC(TexCoord4fv);
+ pVtxFmt->Vertex2f = VBOX_GL_FUNC(Vertex2f);
+ pVtxFmt->Vertex2fv = VBOX_GL_FUNC(Vertex2fv);
+ pVtxFmt->Vertex3f = VBOX_GL_FUNC(Vertex3f);
+ pVtxFmt->Vertex3fv = VBOX_GL_FUNC(Vertex3fv);
+ pVtxFmt->Vertex4f = VBOX_GL_FUNC(Vertex4f);
+ pVtxFmt->Vertex4fv = VBOX_GL_FUNC(Vertex4fv);
+ pVtxFmt->VertexAttrib1fNV = VBOX_GL_FUNC(VertexAttrib1fARB);
+ pVtxFmt->VertexAttrib1fvNV = VBOX_GL_FUNC(VertexAttrib1fvARB);
+ pVtxFmt->VertexAttrib2fNV = VBOX_GL_FUNC(VertexAttrib2fARB);
+ pVtxFmt->VertexAttrib2fvNV = VBOX_GL_FUNC(VertexAttrib2fvARB);
+ pVtxFmt->VertexAttrib3fNV = VBOX_GL_FUNC(VertexAttrib3fARB);
+ pVtxFmt->VertexAttrib3fvNV = VBOX_GL_FUNC(VertexAttrib3fvARB);
+ pVtxFmt->VertexAttrib4fNV = VBOX_GL_FUNC(VertexAttrib4fARB);
+ pVtxFmt->VertexAttrib4fvNV = VBOX_GL_FUNC(VertexAttrib4fvARB);
+ pVtxFmt->VertexAttrib1fARB = VBOX_GL_FUNC(VertexAttrib1fARB);
+ pVtxFmt->VertexAttrib1fvARB = VBOX_GL_FUNC(VertexAttrib1fvARB);
+ pVtxFmt->VertexAttrib2fARB = VBOX_GL_FUNC(VertexAttrib2fARB);
+ pVtxFmt->VertexAttrib2fvARB = VBOX_GL_FUNC(VertexAttrib2fvARB);
+ pVtxFmt->VertexAttrib3fARB = VBOX_GL_FUNC(VertexAttrib3fARB);
+ pVtxFmt->VertexAttrib3fvARB = VBOX_GL_FUNC(VertexAttrib3fvARB);
+ pVtxFmt->VertexAttrib4fARB = VBOX_GL_FUNC(VertexAttrib4fARB);
+ pVtxFmt->VertexAttrib4fvARB = VBOX_GL_FUNC(VertexAttrib4fvARB);
+
+ pVtxFmt->EvalMesh1 = VBOX_GL_FUNC(EvalMesh1);
+ pVtxFmt->EvalMesh2 = VBOX_GL_FUNC(EvalMesh2);
+ pVtxFmt->Rectf = VBOX_GL_FUNC(Rectf);
+
+ pVtxFmt->DrawArrays = VBOX_GL_FUNC(DrawArrays);
+ pVtxFmt->DrawElements = VBOX_GL_FUNC(DrawElements);
+ pVtxFmt->DrawRangeElements = VBOX_GL_FUNC(DrawRangeElements);
+}
+#undef VBOX_GL_FUNC
+
+static void
+vboxdriExtSetTexOffset(__DRIcontext *pDRICtx, GLint texname,
+ unsigned long long offset, GLint depth, GLuint pitch)
+{
+}
+
+
+static void
+vboxdriExtSetTexBuffer(__DRIcontext *pDRICtx, GLint target, __DRIdrawable *dPriv)
+{
+}
+
+/** @todo not sure we need it from start*/
+static const __DRItexOffsetExtension vboxdriTexOffsetExtension = {
+ { __DRI_TEX_OFFSET },
+ vboxdriExtSetTexOffset,
+};
+
+/* This DRI extension is required to support EXT_texture_from_pixmap,
+ * which in turn is required by compiz.
+ */
+static const __DRItexBufferExtension vboxdriTexBufferExtension = {
+ { __DRI_TEX_BUFFER, __DRI_TEX_BUFFER_VERSION },
+ vboxdriExtSetTexBuffer,
+};
+
+/* List of DRI extensions supported by VBox DRI driver */
+static const __DRIextension *vboxdriExtensions[] = {
+ &vboxdriTexOffsetExtension.base,
+ &vboxdriTexBufferExtension.base,
+ NULL
+};
+
+static __GLcontextModes *vboxdriFillInModes(unsigned pixel_bits,
+ unsigned depth_bits,
+ unsigned stencil_bits,
+ GLboolean have_back_buffer)
+{
+ unsigned deep = (depth_bits > 17);
+
+ /* Right now GLX_SWAP_COPY_OML isn't supported, but it would be easy
+ * enough to add support. Basically, if a context is created with an
+ * fbconfig where the swap method is GLX_SWAP_COPY_OML, pageflipping
+ * will never be used.
+ */
+
+ static const GLenum db_modes[2] = { GLX_NONE, GLX_SWAP_UNDEFINED_OML };
+ uint8_t depth_bits_array[4];
+ uint8_t stencil_bits_array[4];
+ if(deep) {
+ depth_bits_array[0] = 0;
+ depth_bits_array[1] = 24;
+ stencil_bits_array[0] = 0;
+ stencil_bits_array[1] = 8;
+ } else {
+ depth_bits_array[0] = depth_bits;
+ depth_bits_array[1] = 0;
+ depth_bits_array[2] = depth_bits;
+ depth_bits_array[3] = 0;
+ stencil_bits_array[0] = 0;
+ stencil_bits_array[1] = 0;
+ stencil_bits_array[2] = 8;
+ stencil_bits_array[3] = 8;
+ }
+
+ return driCreateConfigs(
+ deep ? GL_RGBA : GL_RGB,
+ deep ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT_5_6_5,
+ depth_bits_array,
+ stencil_bits_array,
+ deep ? 2 : 4,
+ db_modes, 2);
+}
+
+/**
+ * This is the driver specific part of the createNewScreen entry point.
+ * Called when using legacy DRI.
+ *
+ * return the __GLcontextModes supported by this driver
+ */
+static const __DRIconfig **vboxdriInitScreen(__DRIscreenPrivate *psp)
+{
+ static const __DRIversion ddx_expected = { 1, 1, 0 };
+ static const __DRIversion dri_expected = { 4, 0, 0 };
+ static const __DRIversion drm_expected = { 1, 0, 0 };
+ //PVBoxDRI = (PVBoxDRI) psp->pDevPrivate;
+
+ /* Initialise our call table in chromium. */
+ if (!stubInit())
+ {
+ crDebug("vboxdriInitScreen: stubInit failed");
+ return NULL;
+ }
+
+ if ( ! driCheckDriDdxDrmVersions2( "tdfx",
+ &psp->dri_version, & dri_expected,
+ &psp->ddx_version, & ddx_expected,
+ &psp->drm_version, & drm_expected ) )
+ return NULL;
+
+ /* Calling driInitExtensions here, with a NULL context pointer,
+ * does not actually enable the extensions. It just makes sure
+ * that all the dispatch offsets for all the extensions that
+ * *might* be enables are known. This is needed because the
+ * dispatch offsets need to be known when _mesa_context_create is
+ * called, but we can't enable the extensions until we have a
+ * context pointer.
+ *
+ * Hello chicken. Hello egg. How are you two today?
+ */
+ vboxdriInitExtensions(NULL);
+
+ /** @todo check size of DRIRec (passed from X.Org driver), allocate private
+ * structure if necessary, parse options if necessary, map VRAM if
+ * necessary. */
+ psp->extensions = vboxdriExtensions;
+
+ /* Initialise VtxFmt call table. */
+ vboxdriInitTnlVtxFmt(&vboxdriTnlVtxFmt);
+
+ /*return (const __DRIconfig **)
+ vboxdriFillInModes(psp, dri_priv->cpp * 8,
+ (dri_priv->cpp == 2) ? 16 : 24,
+ (dri_priv->cpp == 2) ? 0 : 8, 1);*/
+
+ /** @todo This should depend on what the host can do, not the guest.
+ * However, we will probably want to discover that in the X.Org driver,
+ * not here. */
+ return (const __DRIconfig **) vboxdriFillInModes(psp, 24, 24, 8, 1);
+}
+
+static void
+vboxdriDestroyScreen(__DRIscreenPrivate * sPriv)
+{
+ crDebug("vboxdriDestroyScreen");
+#if 0 /* From the tdfx driver */
+ /* free all option information */
+ driDestroyOptionInfo (&sPriv->private->optionCache);
+ FREE(sPriv->private);
+ sPriv->private = NULL;
+#endif
+}
+
+static GLboolean
+vboxdriCreateContext(const __GLcontextModes * mesaVis,
+ __DRIcontextPrivate * driContextPriv,
+ void *sharedContextPrivate)
+{
+ //__DRIscreenPrivate *sPriv = driContextPriv->driScreenPriv;
+ struct dd_function_table functions;
+ GLcontext *ctx, *shareCtx;
+
+#if 0 /* We shouldn't need this sort of thing. */
+ XVisualInfo *vis;
+ vis->visual->visualid;
+ context->dpy = dpy;
+ context->visual = vis;
+
+ GLXContext vboxctx = glXCreateContext(dpy, mesaVis->visualID, GL_TRUE);
+#endif
+ /* We should be allocating a private context structure, where we will
+ * remember the Mesa context (ctx) among other things. The TDFX driver
+ * also saves importand information in driContextPriv in there - is this
+ * not always available to us? */
+ //driContextPriv->driverPrivate = vboxctx;
+
+ /* Initialise the default driver functions then plug in our vbox ones,
+ * which will actually replace most of the defaults. */
+ /** @todo we should also pass some information from the visual back to the
+ * host. */
+ _mesa_init_driver_functions(&functions);
+ vboxdriInitFuncs(&functions);
+
+ /* Allocate context information for Mesa. */
+ if (sharedContextPrivate)
+ shareCtx = ((tdfxContextPtr) sharedContextPrivate)->glCtx;
+ else
+ shareCtx = NULL;
+ /** @todo save ctx, or be more confident that we can don't need to. */
+ ctx = _mesa_create_context(mesaVis, shareCtx, &functions,
+ driContextPriv->driverPrivate);
+ if (!ctx)
+ {
+ crDebug("vboxdriCreateContext: _mesa_create_context failed");
+ return GL_FALSE;
+ }
+
+ /* The TDFX driver parses its configuration files here, via
+ * driParseConfigFiles. We will probably get any information via guest
+ * properties. */
+
+ /* Set various context configuration. We take these values from the
+ * TDFX driver. */
+ /** @r=Leonid, stub.spu->dispatch_table.GetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&value) etc.
+ * Those would be cached where possible, see include/state/cr_limits.h, VBoxOGLgen/packspu_get.c
+ * Note, that ctx->Const.MaxTextureImageUnits is *not* related to GL_MAX_TEXTURE_UNITS_ARB,
+ * use GL_MAX_TEXTURE_IMAGE_UNITS_ARB instead.
+ * Also, those could fail if we haven't made ctx in our stub yet.
+ */
+ ctx->Const.MaxTextureLevels = 9;
+ ctx->Const.MaxTextureUnits = 1;
+ ctx->Const.MaxTextureImageUnits = ctx->Const.MaxTextureUnits;
+ ctx->Const.MaxTextureCoordUnits = ctx->Const.MaxTextureUnits;
+
+ /* No wide points.
+ */
+ ctx->Const.MinPointSize = 1.0;
+ ctx->Const.MinPointSizeAA = 1.0;
+ ctx->Const.MaxPointSize = 1.0;
+ ctx->Const.MaxPointSizeAA = 1.0;
+
+ /* Disable wide lines as we can't antialias them correctly in
+ * hardware.
+ */
+ /** @note That applies to the TDFX, not to us, but as I don't yet know
+ * what to use instead I am leaving the values for now. */
+ ctx->Const.MinLineWidth = 1.0;
+ ctx->Const.MinLineWidthAA = 1.0;
+ ctx->Const.MaxLineWidth = 1.0;
+ ctx->Const.MaxLineWidthAA = 1.0;
+ ctx->Const.LineWidthGranularity = 1.0;
+
+ /* Initialize the software rasterizer and helper modules - again, TDFX */
+ _swrast_CreateContext( ctx );
+ _vbo_CreateContext( ctx );
+ _tnl_CreateContext( ctx );
+ _swsetup_CreateContext( ctx );
+
+ /* Install the customized pipeline, TDFX */
+ _tnl_destroy_pipeline( ctx );
+ _tnl_install_pipeline( ctx, tdfx_pipeline );
+
+ /* Configure swrast and T&L to match hardware characteristics, TDFX */
+ _swrast_allow_pixel_fog( ctx, GL_TRUE );
+ _swrast_allow_vertex_fog( ctx, GL_FALSE );
+ _tnl_allow_pixel_fog( ctx, GL_TRUE );
+ _tnl_allow_vertex_fog( ctx, GL_FALSE );
+ /*ctx->DriverCtx = ;*/
+
+ /* This was *not* in the TDFX driver. */
+ _mesa_install_exec_vtxfmt(ctx, &vboxdriTnlVtxFmt);
+
+ vboxdriInitExtensions(ctx);
+
+ return GL_TRUE;
+}
+
+
+static void
+vboxdriDestroyContext(__DRIcontextPrivate *driContextPriv)
+{
+ // glXDestroyContext(driContextPriv->driverPrivate);
+ //_mesa_destroy_context ?
+}
+
+/**
+ * This is called when we need to set up GL rendering to a new X window.
+ */
+static GLboolean
+vboxdriCreateBuffer(__DRIscreenPrivate * driScrnPriv,
+ __DRIdrawablePrivate * driDrawPriv,
+ const __GLcontextModes * mesaVis, GLboolean isPixmap)
+{
+ return GL_FALSE;
+}
+
+static void
+vboxdriDestroyBuffer(__DRIdrawablePrivate * driDrawPriv)
+{
+}
+
+static void
+vboxdriSwapBuffers(__DRIdrawablePrivate * dPriv)
+{
+}
+
+
+GLboolean
+vboxdriMakeCurrent(__DRIcontextPrivate * driContextPriv,
+ __DRIdrawablePrivate * driDrawPriv,
+ __DRIdrawablePrivate * driReadPriv)
+{
+ return GL_FALSE;
+}
+
+GLboolean
+vboxdriUnbindContext(__DRIcontextPrivate * driContextPriv)
+{
+ return GL_TRUE;
+}
+
+
+/* This structure is used by dri_util from mesa, don't rename it! */
+DECLEXPORT(const struct __DriverAPIRec) driDriverAPI = {
+ .InitScreen = vboxdriInitScreen,
+ .DestroyScreen = vboxdriDestroyScreen,
+ .CreateContext = vboxdriCreateContext,
+ .DestroyContext = vboxdriDestroyContext,
+ .CreateBuffer = vboxdriCreateBuffer,
+ .DestroyBuffer = vboxdriDestroyBuffer,
+ .SwapBuffers = vboxdriSwapBuffers,
+ .MakeCurrent = vboxdriMakeCurrent,
+ .UnbindContext = vboxdriUnbindContext,
+ .GetSwapInfo = NULL,
+ .WaitForMSC = NULL,
+ .WaitForSBC = NULL,
+ .SwapBuffersMSC = NULL,
+ .CopySubBuffer = NULL,
+ .GetDrawableMSC = NULL,
+ .InitScreen2 = NULL
+};
diff --git a/src/VBox/Additions/common/crOpenGL/dri_drv.h b/src/VBox/Additions/common/crOpenGL/dri_drv.h
new file mode 100644
index 00000000..7c11c3ba
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/dri_drv.h
@@ -0,0 +1,32 @@
+/* $Id: dri_drv.h $ */
+
+/** @file
+ *
+ * VirtualBox guest OpenGL DRI header
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_dri_drv_h
+#define GA_INCLUDED_SRC_common_crOpenGL_dri_drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "src/mesa/main/mtypes.h"
+#include "src/mesa/drivers/dri/common/dri_util.h"
+#include "src/mesa/glapi/dispatch.h"
+#include "src/mesa/main/dd.h"
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_dri_drv_h */
+
diff --git a/src/VBox/Additions/common/crOpenGL/dri_glx.h b/src/VBox/Additions/common/crOpenGL/dri_glx.h
new file mode 100644
index 00000000..237c93a3
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/dri_glx.h
@@ -0,0 +1,165 @@
+/* $Id: dri_glx.h $ */
+
+/** @file
+ *
+ * VirtualBox guest OpenGL DRI GLX header
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_dri_glx_h
+#define GA_INCLUDED_SRC_common_crOpenGL_dri_glx_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "chromium.h"
+#include "stub.h"
+
+#if defined(VBOXOGL_FAKEDRI) || defined(VBOXOGL_DRI)
+ #define VBOXGLXTAG(Func) vboxstub_##Func
+ #define VBOXGLXENTRYTAG(Func) vbox_##Func
+ #define VBOXGLTAG(Func) cr_##Func
+#else
+ #define VBOXGLXTAG(Func) Func
+ #define VBOXGLXENTRYTAG(Func) Func
+ #define VBOXGLTAG(Func) Func
+#endif
+
+#ifdef VBOXOGL_FAKEDRI
+extern DECLEXPORT(const char *) VBOXGLXTAG(glXGetDriverConfig)(const char *driverName);
+extern DECLEXPORT(void) VBOXGLXTAG(glXFreeMemoryMESA)(Display *dpy, int scrn, void *pointer);
+extern DECLEXPORT(GLXContext) VBOXGLXTAG(glXImportContextEXT)(Display *dpy, GLXContextID contextID);
+extern DECLEXPORT(GLXContextID) VBOXGLXTAG(glXGetContextIDEXT)(const GLXContext ctx);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXMakeCurrentReadSGI)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+extern DECLEXPORT(const char *) VBOXGLXTAG(glXGetScreenDriver)(Display *dpy, int scrNum);
+extern DECLEXPORT(Display *) VBOXGLXTAG(glXGetCurrentDisplayEXT)(void);
+extern DECLEXPORT(void) VBOXGLXTAG(glXFreeContextEXT)(Display *dpy, GLXContext ctx);
+/*Mesa internal*/
+extern DECLEXPORT(int) VBOXGLXTAG(glXQueryContextInfoEXT)(Display *dpy, GLXContext ctx);
+extern DECLEXPORT(void *) VBOXGLXTAG(glXAllocateMemoryMESA)(Display *dpy, int scrn,
+ size_t size, float readFreq,
+ float writeFreq, float priority);
+extern DECLEXPORT(GLuint) VBOXGLXTAG(glXGetMemoryOffsetMESA)(Display *dpy, int scrn, const void *pointer );
+extern DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreateGLXPixmapMESA)(Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
+#endif
+
+/*Common glX functions*/
+extern DECLEXPORT(void) VBOXGLXTAG(glXCopyContext)( Display *dpy, GLXContext src, GLXContext dst,
+#if defined(SunOS)
+unsigned long mask);
+#else
+unsigned long mask);
+#endif
+extern DECLEXPORT(void) VBOXGLXTAG(glXUseXFont)(Font font, int first, int count, int listBase);
+extern DECLEXPORT(CR_GLXFuncPtr) VBOXGLXTAG(glXGetProcAddress)(const GLubyte *name);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXQueryExtension)(Display *dpy, int *errorBase, int *eventBase);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXIsDirect)(Display *dpy, GLXContext ctx);
+extern DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreateGLXPixmap)(Display *dpy, XVisualInfo *vis, Pixmap pixmap);
+extern DECLEXPORT(void) VBOXGLXTAG(glXSwapBuffers)(Display *dpy, GLXDrawable drawable);
+extern DECLEXPORT(GLXDrawable) VBOXGLXTAG(glXGetCurrentDrawable)(void);
+extern DECLEXPORT(void) VBOXGLXTAG(glXWaitGL)(void);
+extern DECLEXPORT(Display *) VBOXGLXTAG(glXGetCurrentDisplay)(void);
+extern DECLEXPORT(const char *) VBOXGLXTAG(glXQueryServerString)(Display *dpy, int screen, int name);
+extern DECLEXPORT(GLXContext) VBOXGLXTAG(glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, Bool direct);
+extern DECLEXPORT(int) VBOXGLXTAG(glXGetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int *value);
+extern DECLEXPORT(void) VBOXGLXTAG(glXWaitX)(void);
+extern DECLEXPORT(GLXContext) VBOXGLXTAG(glXGetCurrentContext)(void);
+extern DECLEXPORT(const char *) VBOXGLXTAG(glXGetClientString)(Display *dpy, int name);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyContext)(Display *dpy, GLXContext ctx);
+extern DECLEXPORT(CR_GLXFuncPtr) VBOXGLXTAG(glXGetProcAddressARB)(const GLubyte *name);
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyGLXPixmap)(Display *dpy, GLXPixmap pix);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXQueryVersion)(Display *dpy, int *major, int *minor);
+extern DECLEXPORT(XVisualInfo *) VBOXGLXTAG(glXChooseVisual)(Display *dpy, int screen, int *attribList);
+extern DECLEXPORT(const char *) VBOXGLXTAG(glXQueryExtensionsString)(Display *dpy, int screen);
+
+/**
+ * Set this to 1 if you want to build stub functions for the
+ * GL_SGIX_pbuffer and GLX_SGIX_fbconfig extensions.
+ * This used to be disabled, due to "messy compilation issues",
+ * according to the earlier comment; but they're needed just
+ * to resolve symbols for OpenInventor applications, and I
+ * haven't found any reference to exactly what the "messy compilation
+ * issues" are, so I'm re-enabling the code by default.
+ */
+#define GLX_EXTRAS 1
+
+#define GLX_SGIX_video_resize 1
+
+/**
+ * Prototypes, in case they're not in glx.h or glxext.h
+ * Unfortunately, there's some inconsistency between the extension
+ * specs, and the SGI, NVIDIA, XFree86 and common glxext.h header
+ * files.
+ */
+#if defined(GLX_GLXEXT_VERSION)
+/* match glxext.h, XFree86, Mesa */
+#define ATTRIB_TYPE const int
+#else
+#define ATTRIB_TYPE int
+#endif
+
+#if GLX_EXTRAS
+extern DECLEXPORT(GLXPbufferSGIX) VBOXGLXTAG(glXCreateGLXPbufferSGIX)
+(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
+
+extern DECLEXPORT(int) VBOXGLXTAG(glXQueryGLXPbufferSGIX)
+(Display *dpy, GLXPbuffer pbuf, int attribute, unsigned int *value);
+
+extern DECLEXPORT(GLXFBConfigSGIX *) VBOXGLXTAG(glXChooseFBConfigSGIX)
+(Display *dpy, int screen, int *attrib_list, int *nelements);
+
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf);
+extern DECLEXPORT(void) VBOXGLXTAG(glXSelectEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long mask);
+extern DECLEXPORT(void) VBOXGLXTAG(glXGetSelectedEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long *mask);
+
+extern DECLEXPORT(GLXFBConfigSGIX) VBOXGLXTAG(glXGetFBConfigFromVisualSGIX)(Display *dpy, XVisualInfo *vis);
+extern DECLEXPORT(XVisualInfo *) VBOXGLXTAG(glXGetVisualFromFBConfigSGIX)(Display *dpy, GLXFBConfig config);
+extern DECLEXPORT(GLXContext) VBOXGLXTAG(glXCreateContextWithConfigSGIX)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+
+extern DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreateGLXPixmapWithConfigSGIX)(Display *dpy, GLXFBConfig config, Pixmap pixmap);
+extern DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttribSGIX)(Display *dpy, GLXFBConfig config, int attribute, int *value);
+
+/*
+ * GLX 1.3 functions
+ */
+extern DECLEXPORT(GLXFBConfig *) VBOXGLXTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list, int *nelements);
+extern DECLEXPORT(GLXPbuffer) VBOXGLXTAG(glXCreatePbuffer)(Display *dpy, GLXFBConfig config, ATTRIB_TYPE *attrib_list);
+extern DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreatePixmap)(Display *dpy, GLXFBConfig config, Pixmap pixmap, ATTRIB_TYPE *attrib_list);
+extern DECLEXPORT(GLXWindow) VBOXGLXTAG(glXCreateWindow)(Display *dpy, GLXFBConfig config, Window win, ATTRIB_TYPE *attrib_list);
+extern DECLEXPORT(GLXContext) VBOXGLXTAG(glXCreateNewContext)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyPbuffer)(Display *dpy, GLXPbuffer pbuf);
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyPixmap)(Display *dpy, GLXPixmap pixmap);
+extern DECLEXPORT(void) VBOXGLXTAG(glXDestroyWindow)(Display *dpy, GLXWindow win);
+extern DECLEXPORT(GLXDrawable) VBOXGLXTAG(glXGetCurrentReadDrawable)(void);
+extern DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int *value);
+extern DECLEXPORT(GLXFBConfig *) VBOXGLXTAG(glXGetFBConfigs)(Display *dpy, int screen, int *nelements);
+extern DECLEXPORT(void) VBOXGLXTAG(glXGetSelectedEvent)(Display *dpy, GLXDrawable draw, unsigned long *event_mask);
+extern DECLEXPORT(XVisualInfo *) VBOXGLXTAG(glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config);
+extern DECLEXPORT(Bool) VBOXGLXTAG(glXMakeContextCurrent)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+extern DECLEXPORT(int) VBOXGLXTAG(glXQueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value);
+extern DECLEXPORT(void) VBOXGLXTAG(glXQueryDrawable)(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
+extern DECLEXPORT(void) VBOXGLXTAG(glXSelectEvent)(Display *dpy, GLXDrawable draw, unsigned long event_mask);
+
+#ifdef CR_EXT_texture_from_pixmap
+extern DECLEXPORT(void) VBOXGLXTAG(glXBindTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer, const int *attrib_list);
+extern DECLEXPORT(void) VBOXGLXTAG(glXReleaseTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer);
+#endif
+
+#endif /* GLX_EXTRAS */
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_dri_glx_h */
diff --git a/src/VBox/Additions/common/crOpenGL/dri_util.c b/src/VBox/Additions/common/crOpenGL/dri_util.c
new file mode 100644
index 00000000..5041e07f
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/dri_util.c
@@ -0,0 +1,1117 @@
+/* $XFree86: xc/lib/GL/dri/dri_util.c,v 1.7 2003/04/28 17:01:25 dawes Exp $ */
+/**
+ * \file dri_util.c
+ * DRI utility functions.
+ *
+ * This module acts as glue between GLX and the actual hardware driver. A DRI
+ * driver doesn't really \e have to use any of this - it's optional. But, some
+ * useful stuff is done here that otherwise would have to be duplicated in most
+ * drivers.
+ *
+ * Basically, these utility functions take care of some of the dirty details of
+ * screen initialization, context creation, context binding, DRM setup, etc.
+ *
+ * These functions are compiled into each DRI driver so libGL.so knows nothing
+ * about them.
+ */
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdio.h>
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+#include "imports.h"
+#define None 0
+
+#include "dri_util.h"
+#include "drm_sarea.h"
+#include "utils.h"
+
+#ifndef GLX_OML_sync_control
+typedef GLboolean ( * PFNGLXGETMSCRATEOMLPROC) (__DRIdrawable *drawable, int32_t *numerator, int32_t *denominator);
+#endif
+
+/**
+ * This is just a token extension used to signal that the driver
+ * supports setting a read drawable.
+ */
+const __DRIextension driReadDrawableExtension = {
+ __DRI_READ_DRAWABLE, __DRI_READ_DRAWABLE_VERSION
+};
+
+/**
+ * Print message to \c stderr if the \c LIBGL_DEBUG environment variable
+ * is set.
+ *
+ * Is called from the drivers.
+ *
+ * \param f \c printf like format string.
+ */
+void
+__driUtilMessage(const char *f, ...)
+{
+ va_list args;
+
+ if (getenv("LIBGL_DEBUG")) {
+ fprintf(stderr, "libGL error: \n");
+ va_start(args, f);
+ vfprintf(stderr, f, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ }
+}
+
+GLint
+driIntersectArea( drm_clip_rect_t rect1, drm_clip_rect_t rect2 )
+{
+ if (rect2.x1 > rect1.x1) rect1.x1 = rect2.x1;
+ if (rect2.x2 < rect1.x2) rect1.x2 = rect2.x2;
+ if (rect2.y1 > rect1.y1) rect1.y1 = rect2.y1;
+ if (rect2.y2 < rect1.y2) rect1.y2 = rect2.y2;
+
+ if (rect1.x1 > rect1.x2 || rect1.y1 > rect1.y2) return 0;
+
+ return (rect1.x2 - rect1.x1) * (rect1.y2 - rect1.y1);
+}
+
+/*****************************************************************/
+/** \name Context (un)binding functions */
+/*****************************************************************/
+/*@{*/
+
+/**
+ * Unbind context.
+ *
+ * \param scrn the screen.
+ * \param gc context.
+ *
+ * \return \c GL_TRUE on success, or \c GL_FALSE on failure.
+ *
+ * \internal
+ * This function calls __DriverAPIRec::UnbindContext, and then decrements
+ * __DRIdrawablePrivateRec::refcount which must be non-zero for a successful
+ * return.
+ *
+ * While casting the opaque private pointers associated with the parameters
+ * into their respective real types it also assures they are not \c NULL.
+ */
+static int driUnbindContext(__DRIcontext *pcp)
+{
+ __DRIscreen *psp;
+ __DRIdrawable *pdp;
+ __DRIdrawable *prp;
+
+ /*
+ ** Assume error checking is done properly in glXMakeCurrent before
+ ** calling driUnbindContext.
+ */
+
+ if (pcp == NULL)
+ return GL_FALSE;
+
+ psp = pcp->driScreenPriv;
+ pdp = pcp->driDrawablePriv;
+ prp = pcp->driReadablePriv;
+
+ /* Let driver unbind drawable from context */
+ (*psp->DriverAPI.UnbindContext)(pcp);
+
+ if (pdp->refcount == 0) {
+ /* ERROR!!! */
+ return GL_FALSE;
+ }
+
+ pdp->refcount--;
+
+ if (prp != pdp) {
+ if (prp->refcount == 0) {
+ /* ERROR!!! */
+ return GL_FALSE;
+ }
+
+ prp->refcount--;
+ }
+
+
+ /* XXX this is disabled so that if we call SwapBuffers on an unbound
+ * window we can determine the last context bound to the window and
+ * use that context's lock. (BrianP, 2-Dec-2000)
+ */
+#if 0
+ /* Unbind the drawable */
+ pcp->driDrawablePriv = NULL;
+ pdp->driContextPriv = &psp->dummyContextPriv;
+#endif
+
+ return GL_TRUE;
+}
+
+
+/**
+ * This function takes both a read buffer and a draw buffer. This is needed
+ * for \c glXMakeCurrentReadSGI or GLX 1.3's \c glXMakeContextCurrent
+ * function.
+ */
+static int driBindContext(__DRIcontext *pcp,
+ __DRIdrawable *pdp,
+ __DRIdrawable *prp)
+{
+ __DRIscreenPrivate *psp;
+
+ /*
+ ** Assume error checking is done properly in glXMakeCurrent before
+ ** calling driBindContext.
+ */
+
+ if (pcp == NULL || pdp == None || prp == None)
+ return GL_FALSE;
+
+ /* Bind the drawable to the context */
+ pcp->driDrawablePriv = pdp;
+ pcp->driReadablePriv = prp;
+ pdp->driContextPriv = pcp;
+ pdp->refcount++;
+ if ( pdp != prp ) {
+ prp->refcount++;
+ }
+
+ /*
+ ** Now that we have a context associated with this drawable, we can
+ ** initialize the drawable information if has not been done before.
+ */
+
+ psp = pcp->driScreenPriv;
+ if (psp->dri2.enabled) {
+ __driParseEvents(pcp, pdp);
+ __driParseEvents(pcp, prp);
+ } else {
+ if (!pdp->pStamp || *pdp->pStamp != pdp->lastStamp) {
+ DRM_SPINLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+ __driUtilUpdateDrawableInfo(pdp);
+ DRM_SPINUNLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+ }
+
+ if ((pdp != prp) && (!prp->pStamp || *prp->pStamp != prp->lastStamp)) {
+ DRM_SPINLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+ __driUtilUpdateDrawableInfo(prp);
+ DRM_SPINUNLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+ }
+ }
+
+ /* Call device-specific MakeCurrent */
+ (*psp->DriverAPI.MakeCurrent)(pcp, pdp, prp);
+
+ return GL_TRUE;
+}
+
+/*@}*/
+
+
+/*****************************************************************/
+/** \name Drawable handling functions */
+/*****************************************************************/
+/*@{*/
+
+/**
+ * Update private drawable information.
+ *
+ * \param pdp pointer to the private drawable information to update.
+ *
+ * This function basically updates the __DRIdrawablePrivate struct's
+ * cliprect information by calling \c __DRIinterfaceMethods::getDrawableInfo.
+ * This is usually called by the DRI_VALIDATE_DRAWABLE_INFO macro which
+ * compares the __DRIdrwablePrivate pStamp and lastStamp values. If
+ * the values are different that means we have to update the clipping
+ * info.
+ */
+void
+__driUtilUpdateDrawableInfo(__DRIdrawablePrivate *pdp)
+{
+ __DRIscreenPrivate *psp = pdp->driScreenPriv;
+ __DRIcontextPrivate *pcp = pdp->driContextPriv;
+
+ if (!pcp
+ || ((pdp != pcp->driDrawablePriv) && (pdp != pcp->driReadablePriv))) {
+ /* ERROR!!!
+ * ...but we must ignore it. There can be many contexts bound to a
+ * drawable.
+ */
+ }
+
+ if (pdp->pClipRects) {
+ _mesa_free(pdp->pClipRects);
+ pdp->pClipRects = NULL;
+ }
+
+ if (pdp->pBackClipRects) {
+ _mesa_free(pdp->pBackClipRects);
+ pdp->pBackClipRects = NULL;
+ }
+
+ DRM_SPINUNLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+
+ if (! (*psp->getDrawableInfo->getDrawableInfo)(pdp,
+ &pdp->index, &pdp->lastStamp,
+ &pdp->x, &pdp->y, &pdp->w, &pdp->h,
+ &pdp->numClipRects, &pdp->pClipRects,
+ &pdp->backX,
+ &pdp->backY,
+ &pdp->numBackClipRects,
+ &pdp->pBackClipRects,
+ pdp->loaderPrivate)) {
+ /* Error -- eg the window may have been destroyed. Keep going
+ * with no cliprects.
+ */
+ pdp->pStamp = &pdp->lastStamp; /* prevent endless loop */
+ pdp->numClipRects = 0;
+ pdp->pClipRects = NULL;
+ pdp->numBackClipRects = 0;
+ pdp->pBackClipRects = NULL;
+ }
+ else
+ pdp->pStamp = &(psp->pSAREA->drawableTable[pdp->index].stamp);
+
+ DRM_SPINLOCK(&psp->pSAREA->drawable_lock, psp->drawLockID);
+}
+
+
+int
+__driParseEvents(__DRIcontextPrivate *pcp, __DRIdrawablePrivate *pdp)
+{
+ __DRIscreenPrivate *psp = pdp->driScreenPriv;
+ __DRIDrawableConfigEvent *dc, *last_dc;
+ __DRIBufferAttachEvent *ba, *last_ba;
+ unsigned int tail, mask, *p, end, total, size, changed;
+ unsigned char *data;
+ size_t rect_size;
+
+ /* Check for wraparound. */
+ if (pcp && psp->dri2.buffer->prealloc - pdp->dri2.tail > psp->dri2.buffer->size) {
+ /* If prealloc overlaps into what we just parsed, the
+ * server overwrote it and we have to reset our tail
+ * pointer. */
+ DRM_UNLOCK(psp->fd, psp->lock, pcp->hHWContext);
+ (*psp->dri2.loader->reemitDrawableInfo)(pdp, &pdp->dri2.tail,
+ pdp->loaderPrivate);
+ DRM_LIGHT_LOCK(psp->fd, psp->lock, pcp->hHWContext);
+ }
+
+ total = psp->dri2.buffer->head - pdp->dri2.tail;
+ mask = psp->dri2.buffer->size - 1;
+ end = psp->dri2.buffer->head;
+ data = psp->dri2.buffer->data;
+
+ changed = 0;
+ last_dc = NULL;
+ last_ba = NULL;
+
+ for (tail = pdp->dri2.tail; tail != end; tail += size) {
+ p = (unsigned int *) (data + (tail & mask));
+ size = DRI2_EVENT_SIZE(*p);
+ if (size > total || (tail & mask) + size > psp->dri2.buffer->size) {
+ /* illegal data, bail out. */
+ fprintf(stderr, "illegal event size\n");
+ break;
+ }
+
+ switch (DRI2_EVENT_TYPE(*p)) {
+ case DRI2_EVENT_DRAWABLE_CONFIG:
+ dc = (__DRIDrawableConfigEvent *) p;
+ if (dc->drawable == pdp->dri2.drawable_id)
+ last_dc = dc;
+ break;
+
+ case DRI2_EVENT_BUFFER_ATTACH:
+ ba = (__DRIBufferAttachEvent *) p;
+ if (ba->drawable == pdp->dri2.drawable_id &&
+ ba->buffer.attachment == DRI_DRAWABLE_BUFFER_FRONT_LEFT)
+ last_ba = ba;
+ break;
+ }
+ }
+
+ if (last_dc) {
+ if (pdp->w != last_dc->width || pdp->h != last_dc->height)
+ changed = 1;
+
+ pdp->x = last_dc->x;
+ pdp->y = last_dc->y;
+ pdp->w = last_dc->width;
+ pdp->h = last_dc->height;
+
+ pdp->backX = 0;
+ pdp->backY = 0;
+ pdp->numBackClipRects = 1;
+ pdp->pBackClipRects[0].x1 = 0;
+ pdp->pBackClipRects[0].y1 = 0;
+ pdp->pBackClipRects[0].x2 = pdp->w;
+ pdp->pBackClipRects[0].y2 = pdp->h;
+
+ pdp->numClipRects = last_dc->num_rects;
+ _mesa_free(pdp->pClipRects);
+ rect_size = last_dc->num_rects * sizeof last_dc->rects[0];
+ pdp->pClipRects = _mesa_malloc(rect_size);
+ memcpy(pdp->pClipRects, last_dc->rects, rect_size);
+ }
+
+ /* We only care about the most recent drawable config. */
+ if (last_dc && changed)
+ (*psp->DriverAPI.HandleDrawableConfig)(pdp, pcp, last_dc);
+
+ /* Front buffer attachments are special, they typically mean that
+ * we're rendering to a redirected window (or a child window of a
+ * redirected window) and that it got resized. Resizing the root
+ * window on randr events is a special case of this. Other causes
+ * may be a window transitioning between redirected and
+ * non-redirected, or a window getting reparented between parents
+ * with different window pixmaps (eg two redirected windows).
+ * These events are special in that the X server allocates the
+ * buffer and that the buffer may be shared by other child
+ * windows. When our window share the window pixmap with its
+ * parent, drawable config events doesn't affect the front buffer.
+ * We only care about the last such event in the buffer; in fact,
+ * older events will refer to invalid buffer objects.*/
+ if (last_ba)
+ (*psp->DriverAPI.HandleBufferAttach)(pdp, pcp, last_ba);
+
+ /* If there was a drawable config event in the buffer and it
+ * changed the size of the window, all buffer auxiliary buffer
+ * attachments prior to that are invalid (as opposed to the front
+ * buffer case discussed above). In that case we can start
+ * looking for buffer attachment after the last drawable config
+ * event. If there is no drawable config event in this batch of
+ * events, we have to assume that the last batch might have had
+ * one and process all buffer attach events.*/
+ if (last_dc && changed)
+ tail = (unsigned char *) last_dc - data;
+ else
+ tail = pdp->dri2.tail;
+
+ for ( ; tail != end; tail += size) {
+ ba = (__DRIBufferAttachEvent *) (data + (tail & mask));
+ size = DRI2_EVENT_SIZE(ba->event_header);
+
+ if (DRI2_EVENT_TYPE(ba->event_header) != DRI2_EVENT_BUFFER_ATTACH)
+ continue;
+ if (ba->drawable != pdp->dri2.drawable_id)
+ continue;
+ if (last_ba == ba)
+ continue;
+
+ (*psp->DriverAPI.HandleBufferAttach)(pdp, pcp, ba);
+ changed = 1;
+ }
+
+ pdp->dri2.tail = tail;
+
+ return changed || last_ba;
+}
+
+/*@}*/
+
+/*****************************************************************/
+/** \name GLX callbacks */
+/*****************************************************************/
+/*@{*/
+
+static void driReportDamage(__DRIdrawable *pdp,
+ struct drm_clip_rect *pClipRects, int numClipRects)
+{
+ __DRIscreen *psp = pdp->driScreenPriv;
+
+ /* Check that we actually have the new damage report method */
+ if (psp->dri2.enabled) {
+ (*psp->dri2.loader->postDamage)(pdp,
+ pClipRects,
+ numClipRects,
+ pdp->loaderPrivate);
+ } else if (psp->damage) {
+ /* Report the damage. Currently, all our drivers draw
+ * directly to the front buffer, so we report the damage there
+ * rather than to the backing storein (if any).
+ */
+ (*psp->damage->reportDamage)(pdp,
+ pdp->x, pdp->y,
+ pClipRects, numClipRects,
+ GL_TRUE, pdp->loaderPrivate);
+ }
+}
+
+
+/**
+ * Swap buffers.
+ *
+ * \param drawablePrivate opaque pointer to the per-drawable private info.
+ *
+ * \internal
+ * This function calls __DRIdrawablePrivate::swapBuffers.
+ *
+ * Is called directly from glXSwapBuffers().
+ */
+static void driSwapBuffers(__DRIdrawable *dPriv)
+{
+ __DRIscreen *psp = dPriv->driScreenPriv;
+
+ if (!dPriv->numClipRects)
+ return;
+
+ if (psp->dri2.enabled)
+ __driParseEvents(NULL, dPriv);
+
+ psp->DriverAPI.SwapBuffers(dPriv);
+
+ driReportDamage(dPriv, dPriv->pClipRects, dPriv->numClipRects);
+}
+
+static int driDrawableGetMSC( __DRIscreen *sPriv, __DRIdrawable *dPriv,
+ int64_t *msc )
+{
+ return sPriv->DriverAPI.GetDrawableMSC(sPriv, dPriv, msc);
+}
+
+
+static int driWaitForMSC(__DRIdrawable *dPriv, int64_t target_msc,
+ int64_t divisor, int64_t remainder,
+ int64_t * msc, int64_t * sbc)
+{
+ __DRIswapInfo sInfo;
+ int status;
+
+ status = dPriv->driScreenPriv->DriverAPI.WaitForMSC( dPriv, target_msc,
+ divisor, remainder,
+ msc );
+
+ /* GetSwapInfo() may not be provided by the driver if GLX_SGI_video_sync
+ * is supported but GLX_OML_sync_control is not. Therefore, don't return
+ * an error value if GetSwapInfo() is not implemented.
+ */
+ if ( status == 0
+ && dPriv->driScreenPriv->DriverAPI.GetSwapInfo ) {
+ status = dPriv->driScreenPriv->DriverAPI.GetSwapInfo( dPriv, & sInfo );
+ *sbc = sInfo.swap_count;
+ }
+
+ return status;
+}
+
+
+const __DRImediaStreamCounterExtension driMediaStreamCounterExtension = {
+ { __DRI_MEDIA_STREAM_COUNTER, __DRI_MEDIA_STREAM_COUNTER_VERSION },
+ driWaitForMSC,
+ driDrawableGetMSC,
+};
+
+
+static void driCopySubBuffer(__DRIdrawable *dPriv,
+ int x, int y, int w, int h)
+{
+ drm_clip_rect_t rect;
+
+ rect.x1 = x;
+ rect.y1 = dPriv->h - y - h;
+ rect.x2 = x + w;
+ rect.y2 = rect.y1 + h;
+ driReportDamage(dPriv, &rect, 1);
+
+ dPriv->driScreenPriv->DriverAPI.CopySubBuffer(dPriv, x, y, w, h);
+}
+
+const __DRIcopySubBufferExtension driCopySubBufferExtension = {
+ { __DRI_COPY_SUB_BUFFER, __DRI_COPY_SUB_BUFFER_VERSION },
+ driCopySubBuffer
+};
+
+static void driSetSwapInterval(__DRIdrawable *dPriv, unsigned int interval)
+{
+ dPriv->swap_interval = interval;
+}
+
+static unsigned int driGetSwapInterval(__DRIdrawable *dPriv)
+{
+ return dPriv->swap_interval;
+}
+
+const __DRIswapControlExtension driSwapControlExtension = {
+ { __DRI_SWAP_CONTROL, __DRI_SWAP_CONTROL_VERSION },
+ driSetSwapInterval,
+ driGetSwapInterval
+};
+
+
+/**
+ * This is called via __DRIscreenRec's createNewDrawable pointer.
+ */
+static __DRIdrawable *
+driCreateNewDrawable(__DRIscreen *psp, const __DRIconfig *config,
+ drm_drawable_t hwDrawable, int renderType,
+ const int *attrs, void *data)
+{
+ __DRIdrawable *pdp;
+
+ /* Since pbuffers are not yet supported, no drawable attributes are
+ * supported either.
+ */
+ (void) attrs;
+
+ pdp = _mesa_malloc(sizeof *pdp);
+ if (!pdp) {
+ return NULL;
+ }
+
+ pdp->loaderPrivate = data;
+ pdp->hHWDrawable = hwDrawable;
+ pdp->refcount = 0;
+ pdp->pStamp = NULL;
+ pdp->lastStamp = 0;
+ pdp->index = 0;
+ pdp->x = 0;
+ pdp->y = 0;
+ pdp->w = 0;
+ pdp->h = 0;
+ pdp->numClipRects = 0;
+ pdp->numBackClipRects = 0;
+ pdp->pClipRects = NULL;
+ pdp->pBackClipRects = NULL;
+ pdp->vblSeq = 0;
+ pdp->vblFlags = 0;
+
+ pdp->driScreenPriv = psp;
+ pdp->driContextPriv = &psp->dummyContextPriv;
+
+ if (!(*psp->DriverAPI.CreateBuffer)(psp, pdp, &config->modes,
+ renderType == GLX_PIXMAP_BIT)) {
+ _mesa_free(pdp);
+ return NULL;
+ }
+
+ pdp->msc_base = 0;
+
+ /* This special default value is replaced with the configured
+ * default value when the drawable is first bound to a direct
+ * rendering context.
+ */
+ pdp->swap_interval = (unsigned)-1;
+
+ return pdp;
+}
+
+
+static __DRIdrawable *
+dri2CreateNewDrawable(__DRIscreen *screen, const __DRIconfig *config,
+ unsigned int drawable_id, unsigned int head, void *data)
+{
+ __DRIdrawable *pdraw;
+
+ pdraw = driCreateNewDrawable(screen, config, 0, 0, NULL, data);
+ if (!pdraw)
+ return NULL;
+
+ pdraw->dri2.drawable_id = drawable_id;
+ pdraw->dri2.tail = head;
+ pdraw->pBackClipRects = _mesa_malloc(sizeof *pdraw->pBackClipRects);
+
+ return pdraw;
+}
+
+
+static void
+driDestroyDrawable(__DRIdrawable *pdp)
+{
+ __DRIscreenPrivate *psp;
+
+ if (pdp) {
+ psp = pdp->driScreenPriv;
+ (*psp->DriverAPI.DestroyBuffer)(pdp);
+ if (pdp->pClipRects) {
+ _mesa_free(pdp->pClipRects);
+ pdp->pClipRects = NULL;
+ }
+ if (pdp->pBackClipRects) {
+ _mesa_free(pdp->pBackClipRects);
+ pdp->pBackClipRects = NULL;
+ }
+ _mesa_free(pdp);
+ }
+}
+
+/*@}*/
+
+
+/*****************************************************************/
+/** \name Context handling functions */
+/*****************************************************************/
+/*@{*/
+
+/**
+ * Destroy the per-context private information.
+ *
+ * \internal
+ * This function calls __DriverAPIRec::DestroyContext on \p contextPrivate, calls
+ * drmDestroyContext(), and finally frees \p contextPrivate.
+ */
+static void
+driDestroyContext(__DRIcontext *pcp)
+{
+ if (pcp) {
+ (*pcp->driScreenPriv->DriverAPI.DestroyContext)(pcp);
+ _mesa_free(pcp);
+ }
+}
+
+
+/**
+ * Create the per-drawable private driver information.
+ *
+ * \param render_type Type of rendering target. \c GLX_RGBA is the only
+ * type likely to ever be supported for direct-rendering.
+ * \param shared Context with which to share textures, etc. or NULL
+ *
+ * \returns An opaque pointer to the per-context private information on
+ * success, or \c NULL on failure.
+ *
+ * \internal
+ * This function allocates and fills a __DRIcontextPrivateRec structure. It
+ * performs some device independent initialization and passes all the
+ * relevant information to __DriverAPIRec::CreateContext to create the
+ * context.
+ *
+ */
+static __DRIcontext *
+driCreateNewContext(__DRIscreen *psp, const __DRIconfig *config,
+ int render_type, __DRIcontext *shared,
+ drm_context_t hwContext, void *data)
+{
+ __DRIcontext *pcp;
+ void * const shareCtx = (shared != NULL) ? shared->driverPrivate : NULL;
+
+ pcp = _mesa_malloc(sizeof *pcp);
+ if (!pcp)
+ return NULL;
+
+ pcp->driScreenPriv = psp;
+ pcp->driDrawablePriv = NULL;
+
+ /* When the first context is created for a screen, initialize a "dummy"
+ * context.
+ */
+
+ if (!psp->dri2.enabled && !psp->dummyContextPriv.driScreenPriv) {
+ psp->dummyContextPriv.hHWContext = psp->pSAREA->dummy_context;
+ psp->dummyContextPriv.driScreenPriv = psp;
+ psp->dummyContextPriv.driDrawablePriv = NULL;
+ psp->dummyContextPriv.driverPrivate = NULL;
+ /* No other fields should be used! */
+ }
+
+ pcp->hHWContext = hwContext;
+
+ if ( !(*psp->DriverAPI.CreateContext)(&config->modes, pcp, shareCtx) ) {
+ _mesa_free(pcp);
+ return NULL;
+ }
+
+ return pcp;
+}
+
+
+static __DRIcontext *
+dri2CreateNewContext(__DRIscreen *screen, const __DRIconfig *config,
+ __DRIcontext *shared, void *data)
+{
+ drm_context_t hwContext;
+ DRM_CAS_RESULT(ret);
+
+ /* DRI2 doesn't use kernel with context IDs, we just need an ID that's
+ * different from the kernel context ID to make drmLock() happy. */
+
+ do {
+ hwContext = screen->dri2.lock->next_id;
+ DRM_CAS(&screen->dri2.lock->next_id, hwContext, hwContext + 1, ret);
+ } while (ret);
+
+ return driCreateNewContext(screen, config, 0, shared, hwContext, data);
+}
+
+
+static int
+driCopyContext(__DRIcontext *dest, __DRIcontext *src, unsigned long mask)
+{
+ return GL_FALSE;
+}
+
+/*@}*/
+
+
+/*****************************************************************/
+/** \name Screen handling functions */
+/*****************************************************************/
+/*@{*/
+
+/**
+ * Destroy the per-screen private information.
+ *
+ * \internal
+ * This function calls __DriverAPIRec::DestroyScreen on \p screenPrivate, calls
+ * drmClose(), and finally frees \p screenPrivate.
+ */
+static void driDestroyScreen(__DRIscreen *psp)
+{
+ if (psp) {
+ /* No interaction with the X-server is possible at this point. This
+ * routine is called after XCloseDisplay, so there is no protocol
+ * stream open to the X-server anymore.
+ */
+
+ if (psp->DriverAPI.DestroyScreen)
+ (*psp->DriverAPI.DestroyScreen)(psp);
+
+ if (psp->dri2.enabled) {
+#ifdef TTM_API
+ drmBOUnmap(psp->fd, &psp->dri2.sareaBO);
+ drmBOUnreference(psp->fd, &psp->dri2.sareaBO);
+#endif
+ } else {
+ (void)drmUnmap((drmAddress)psp->pSAREA, SAREA_MAX);
+ (void)drmUnmap((drmAddress)psp->pFB, psp->fbSize);
+ (void)drmCloseOnce(psp->fd);
+ }
+
+ _mesa_free(psp);
+ }
+}
+
+static void
+setupLoaderExtensions(__DRIscreen *psp,
+ const __DRIextension **extensions)
+{
+ int i;
+
+ for (i = 0; extensions[i]; i++) {
+ if (strcmp(extensions[i]->name, __DRI_GET_DRAWABLE_INFO) == 0)
+ psp->getDrawableInfo = (__DRIgetDrawableInfoExtension *) extensions[i];
+ if (strcmp(extensions[i]->name, __DRI_DAMAGE) == 0)
+ psp->damage = (__DRIdamageExtension *) extensions[i];
+ if (strcmp(extensions[i]->name, __DRI_SYSTEM_TIME) == 0)
+ psp->systemTime = (__DRIsystemTimeExtension *) extensions[i];
+ if (strcmp(extensions[i]->name, __DRI_LOADER) == 0)
+ psp->dri2.loader = (__DRIloaderExtension *) extensions[i];
+ }
+}
+
+/**
+ * This is the bootstrap function for the driver. libGL supplies all of the
+ * requisite information about the system, and the driver initializes itself.
+ * This routine also fills in the linked list pointed to by \c driver_modes
+ * with the \c __GLcontextModes that the driver can support for windows or
+ * pbuffers.
+ *
+ * For legacy DRI.
+ *
+ * \param scrn Index of the screen
+ * \param ddx_version Version of the 2D DDX. This may not be meaningful for
+ * all drivers.
+ * \param dri_version Version of the "server-side" DRI.
+ * \param drm_version Version of the kernel DRM.
+ * \param frame_buffer Data describing the location and layout of the
+ * framebuffer.
+ * \param pSAREA Pointer to the SAREA.
+ * \param fd Device handle for the DRM.
+ * \param extensions ??
+ * \param driver_modes Returns modes supported by the driver
+ * \param loaderPrivate ??
+ *
+ * \note There is no need to check the minimum API version in this
+ * function. Since the name of this function is versioned, it is
+ * impossible for a loader that is too old to even load this driver.
+ */
+static __DRIscreen *
+driCreateNewScreen(int scrn,
+ const __DRIversion *ddx_version,
+ const __DRIversion *dri_version,
+ const __DRIversion *drm_version,
+ const __DRIframebuffer *frame_buffer,
+ drmAddress pSAREA, int fd,
+ const __DRIextension **extensions,
+ const __DRIconfig ***driver_modes,
+ void *loaderPrivate)
+{
+ static const __DRIextension *emptyExtensionList[] = { NULL };
+ __DRIscreen *psp;
+
+ psp = _mesa_malloc(sizeof *psp);
+ if (!psp)
+ return NULL;
+
+ setupLoaderExtensions(psp, extensions);
+
+ /*
+ ** NOT_DONE: This is used by the X server to detect when the client
+ ** has died while holding the drawable lock. The client sets the
+ ** drawable lock to this value.
+ */
+ psp->drawLockID = 1;
+
+ psp->drm_version = *drm_version;
+ psp->ddx_version = *ddx_version;
+ psp->dri_version = *dri_version;
+
+ psp->pSAREA = pSAREA;
+ psp->lock = (drmLock *) &psp->pSAREA->lock;
+
+ psp->pFB = frame_buffer->base;
+ psp->fbSize = frame_buffer->size;
+ psp->fbStride = frame_buffer->stride;
+ psp->fbWidth = frame_buffer->width;
+ psp->fbHeight = frame_buffer->height;
+ psp->devPrivSize = frame_buffer->dev_priv_size;
+ psp->pDevPriv = frame_buffer->dev_priv;
+ psp->fbBPP = psp->fbStride * 8 / frame_buffer->width;
+
+ psp->extensions = emptyExtensionList;
+ psp->fd = fd;
+ psp->myNum = scrn;
+ psp->dri2.enabled = GL_FALSE;
+
+ /*
+ ** Do not init dummy context here; actual initialization will be
+ ** done when the first DRI context is created. Init screen priv ptr
+ ** to NULL to let CreateContext routine that it needs to be inited.
+ */
+ psp->dummyContextPriv.driScreenPriv = NULL;
+
+ psp->DriverAPI = driDriverAPI;
+
+ *driver_modes = driDriverAPI.InitScreen(psp);
+ if (*driver_modes == NULL) {
+ _mesa_free(psp);
+ return NULL;
+ }
+
+ return psp;
+}
+
+
+/**
+ * DRI2
+ */
+static __DRIscreen *
+dri2CreateNewScreen(int scrn, int fd, unsigned int sarea_handle,
+ const __DRIextension **extensions,
+ const __DRIconfig ***driver_configs, void *data)
+{
+#ifdef TTM_API
+ static const __DRIextension *emptyExtensionList[] = { NULL };
+ __DRIscreen *psp;
+ unsigned int *p;
+ drmVersionPtr version;
+
+ if (driDriverAPI.InitScreen2 == NULL)
+ return NULL;
+
+ psp = _mesa_malloc(sizeof(*psp));
+ if (!psp)
+ return NULL;
+
+ setupLoaderExtensions(psp, extensions);
+
+ version = drmGetVersion(fd);
+ if (version) {
+ psp->drm_version.major = version->version_major;
+ psp->drm_version.minor = version->version_minor;
+ psp->drm_version.patch = version->version_patchlevel;
+ drmFreeVersion(version);
+ }
+
+ psp->extensions = emptyExtensionList;
+ psp->fd = fd;
+ psp->myNum = scrn;
+ psp->dri2.enabled = GL_TRUE;
+
+ if (drmBOReference(psp->fd, sarea_handle, &psp->dri2.sareaBO)) {
+ fprintf(stderr, "Failed to reference DRI2 sarea BO\n");
+ _mesa_free(psp);
+ return NULL;
+ }
+
+ if (drmBOMap(psp->fd, &psp->dri2.sareaBO,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE, 0, &psp->dri2.sarea)) {
+ drmBOUnreference(psp->fd, &psp->dri2.sareaBO);
+ _mesa_free(psp);
+ return NULL;
+ }
+
+ p = psp->dri2.sarea;
+ while (DRI2_SAREA_BLOCK_TYPE(*p)) {
+ switch (DRI2_SAREA_BLOCK_TYPE(*p)) {
+ case DRI2_SAREA_BLOCK_LOCK:
+ psp->dri2.lock = (__DRILock *) p;
+ break;
+ case DRI2_SAREA_BLOCK_EVENT_BUFFER:
+ psp->dri2.buffer = (__DRIEventBuffer *) p;
+ break;
+ }
+ p = DRI2_SAREA_BLOCK_NEXT(p);
+ }
+
+ psp->lock = (drmLock *) &psp->dri2.lock->lock;
+
+ psp->DriverAPI = driDriverAPI;
+ *driver_configs = driDriverAPI.InitScreen2(psp);
+ if (*driver_configs == NULL) {
+ drmBOUnmap(psp->fd, &psp->dri2.sareaBO);
+ drmBOUnreference(psp->fd, &psp->dri2.sareaBO);
+ _mesa_free(psp);
+ return NULL;
+ }
+
+ psp->DriverAPI = driDriverAPI;
+
+ return psp;
+#else
+ return NULL;
+#endif
+}
+
+static const __DRIextension **driGetExtensions(__DRIscreen *psp)
+{
+ return psp->extensions;
+}
+
+/** Legacy DRI interface */
+const __DRIlegacyExtension driLegacyExtension = {
+ { __DRI_LEGACY, __DRI_LEGACY_VERSION },
+ driCreateNewScreen,
+ driCreateNewDrawable,
+ driCreateNewContext
+};
+
+/** DRI2 interface */
+const __DRIcoreExtension driCoreExtension = {
+ { __DRI_CORE, __DRI_CORE_VERSION },
+ dri2CreateNewScreen,
+ driDestroyScreen,
+ driGetExtensions,
+ driGetConfigAttrib,
+ driIndexConfigAttrib,
+ dri2CreateNewDrawable,
+ driDestroyDrawable,
+ driSwapBuffers,
+ dri2CreateNewContext,
+ driCopyContext,
+ driDestroyContext,
+ driBindContext,
+ driUnbindContext
+};
+
+/* This is the table of extensions that the loader will dlsym() for. */
+PUBLIC const __DRIextension *__driDriverExtensions[] = {
+ &driCoreExtension.base,
+ &driLegacyExtension.base,
+ NULL
+};
+
+static int
+driFrameTracking(__DRIdrawable *drawable, GLboolean enable)
+{
+ return GLX_BAD_CONTEXT;
+}
+
+static int
+driQueryFrameTracking(__DRIdrawable *dpriv,
+ int64_t * sbc, int64_t * missedFrames,
+ float * lastMissedUsage, float * usage)
+{
+ __DRIswapInfo sInfo;
+ int status;
+ int64_t ust;
+ __DRIscreenPrivate *psp = dpriv->driScreenPriv;
+
+ status = dpriv->driScreenPriv->DriverAPI.GetSwapInfo( dpriv, & sInfo );
+ if ( status == 0 ) {
+ *sbc = sInfo.swap_count;
+ *missedFrames = sInfo.swap_missed_count;
+ *lastMissedUsage = sInfo.swap_missed_usage;
+
+ (*psp->systemTime->getUST)( & ust );
+ *usage = driCalculateSwapUsage( dpriv, sInfo.swap_ust, ust );
+ }
+
+ return status;
+}
+
+const __DRIframeTrackingExtension driFrameTrackingExtension = {
+ { __DRI_FRAME_TRACKING, __DRI_FRAME_TRACKING_VERSION },
+ driFrameTracking,
+ driQueryFrameTracking
+};
+
+/**
+ * Calculate amount of swap interval used between GLX buffer swaps.
+ *
+ * The usage value, on the range [0,max], is the fraction of total swap
+ * interval time used between GLX buffer swaps is calculated.
+ *
+ * \f$p = t_d / (i * t_r)\f$
+ *
+ * Where \f$t_d\f$ is the time since the last GLX buffer swap, \f$i\f$ is the
+ * swap interval (as set by \c glXSwapIntervalSGI), and \f$t_r\f$ time
+ * required for a single vertical refresh period (as returned by \c
+ * glXGetMscRateOML).
+ *
+ * See the documentation for the GLX_MESA_swap_frame_usage extension for more
+ * details.
+ *
+ * \param dPriv Pointer to the private drawable structure.
+ * \return If less than a single swap interval time period was required
+ * between GLX buffer swaps, a number greater than 0 and less than
+ * 1.0 is returned. If exactly one swap interval time period is
+ * required, 1.0 is returned, and if more than one is required then
+ * a number greater than 1.0 will be returned.
+ *
+ * \sa glXSwapIntervalSGI glXGetMscRateOML
+ *
+ * \todo Instead of caching the \c glXGetMscRateOML function pointer, would it
+ * be possible to cache the sync rate?
+ */
+float
+driCalculateSwapUsage( __DRIdrawablePrivate *dPriv, int64_t last_swap_ust,
+ int64_t current_ust )
+{
+ int32_t n;
+ int32_t d;
+ int interval;
+ float usage = 1.0;
+ __DRIscreenPrivate *psp = dPriv->driScreenPriv;
+
+ if ( (*psp->systemTime->getMSCRate)(dPriv, &n, &d, dPriv->loaderPrivate) ) {
+ interval = (dPriv->swap_interval != 0) ? dPriv->swap_interval : 1;
+
+
+ /* We want to calculate
+ * (current_UST - last_swap_UST) / (interval * us_per_refresh). We get
+ * current_UST by calling __glXGetUST. last_swap_UST is stored in
+ * dPriv->swap_ust. interval has already been calculated.
+ *
+ * The only tricky part is us_per_refresh. us_per_refresh is
+ * 1000000 / MSC_rate. We know the MSC_rate is n / d. We can flip it
+ * around and say us_per_refresh = 1000000 * d / n. Since this goes in
+ * the denominator of the final calculation, we calculate
+ * (interval * 1000000 * d) and move n into the numerator.
+ */
+
+ usage = (current_ust - last_swap_ust);
+ usage *= n;
+ usage /= (interval * d);
+ usage /= 1000000.0;
+ }
+
+ return usage;
+}
+
+/*@}*/
diff --git a/src/VBox/Additions/common/crOpenGL/drirenderbuffer.c b/src/VBox/Additions/common/crOpenGL/drirenderbuffer.c
new file mode 100644
index 00000000..d36af3e5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/drirenderbuffer.c
@@ -0,0 +1,215 @@
+
+#include "mtypes.h"
+#include "drirenderbuffer.h"
+#include "framebuffer.h"
+#include "renderbuffer.h"
+#include "imports.h"
+
+
+/**
+ * This will get called when a window (gl_framebuffer) is resized (probably
+ * via driUpdateFramebufferSize(), below).
+ * Just update width, height and internal format fields for now.
+ * There's usually no memory allocation above because the present
+ * DRI drivers use statically-allocated full-screen buffers. If that's not
+ * the case for a DRI driver, a different AllocStorage method should
+ * be used.
+ */
+static GLboolean
+driRenderbufferStorage(GLcontext *ctx, struct gl_renderbuffer *rb,
+ GLenum internalFormat, GLuint width, GLuint height)
+{
+ rb->Width = width;
+ rb->Height = height;
+ rb->InternalFormat = internalFormat;
+ return GL_TRUE;
+}
+
+
+static void
+driDeleteRenderbuffer(struct gl_renderbuffer *rb)
+{
+ /* don't free rb->Data Chances are it's a memory mapped region for
+ * the dri drivers.
+ */
+ _mesa_free(rb);
+}
+
+
+/**
+ * Allocate a new driRenderbuffer object.
+ * Individual drivers are free to implement different versions of
+ * this function.
+ *
+ * At this time, this function can only be used for window-system
+ * renderbuffers, not user-created RBOs.
+ *
+ * \param format Either GL_RGBA, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24,
+ * GL_DEPTH_COMPONENT32, or GL_STENCIL_INDEX8_EXT (for now).
+ * \param addr address in main memory of the buffer. Probably a memory
+ * mapped region.
+ * \param cpp chars or bytes per pixel
+ * \param offset start of renderbuffer with respect to start of framebuffer
+ * \param pitch pixels per row
+ */
+driRenderbuffer *
+driNewRenderbuffer(GLenum format, GLvoid *addr,
+ GLint cpp, GLint offset, GLint pitch,
+ __DRIdrawablePrivate *dPriv)
+{
+ driRenderbuffer *drb;
+
+ assert(format == GL_RGBA ||
+ format == GL_RGB5 ||
+ format == GL_RGBA8 ||
+ format == GL_DEPTH_COMPONENT16 ||
+ format == GL_DEPTH_COMPONENT24 ||
+ format == GL_DEPTH_COMPONENT32 ||
+ format == GL_STENCIL_INDEX8_EXT);
+
+ assert(cpp > 0);
+ assert(pitch > 0);
+
+ drb = _mesa_calloc(sizeof(driRenderbuffer));
+ if (drb) {
+ const GLuint name = 0;
+
+ _mesa_init_renderbuffer(&drb->Base, name);
+
+ /* Make sure we're using a null-valued GetPointer routine */
+ assert(drb->Base.GetPointer(NULL, &drb->Base, 0, 0) == NULL);
+
+ drb->Base.InternalFormat = format;
+
+ if (format == GL_RGBA || format == GL_RGB5 || format == GL_RGBA8) {
+ /* Color */
+ drb->Base._BaseFormat = GL_RGBA;
+ drb->Base.DataType = GL_UNSIGNED_BYTE;
+ if (format == GL_RGB5) {
+ drb->Base.RedBits = 5;
+ drb->Base.GreenBits = 6;
+ drb->Base.BlueBits = 5;
+ }
+ else {
+ drb->Base.RedBits =
+ drb->Base.GreenBits =
+ drb->Base.BlueBits =
+ drb->Base.AlphaBits = 8;
+ }
+ }
+ else if (format == GL_DEPTH_COMPONENT16) {
+ /* Depth */
+ drb->Base._BaseFormat = GL_DEPTH_COMPONENT;
+ /* we always Get/Put 32-bit Z values */
+ drb->Base.DataType = GL_UNSIGNED_INT;
+ drb->Base.DepthBits = 16;
+ }
+ else if (format == GL_DEPTH_COMPONENT24) {
+ /* Depth */
+ drb->Base._BaseFormat = GL_DEPTH_COMPONENT;
+ /* we always Get/Put 32-bit Z values */
+ drb->Base.DataType = GL_UNSIGNED_INT;
+ drb->Base.DepthBits = 24;
+ }
+ else if (format == GL_DEPTH_COMPONENT32) {
+ /* Depth */
+ drb->Base._BaseFormat = GL_DEPTH_COMPONENT;
+ /* we always Get/Put 32-bit Z values */
+ drb->Base.DataType = GL_UNSIGNED_INT;
+ drb->Base.DepthBits = 32;
+ }
+ else {
+ /* Stencil */
+ ASSERT(format == GL_STENCIL_INDEX8_EXT);
+ drb->Base._BaseFormat = GL_STENCIL_INDEX;
+ drb->Base.DataType = GL_UNSIGNED_BYTE;
+ drb->Base.StencilBits = 8;
+ }
+
+ /* XXX if we were allocating a user-created renderbuffer, we'd have
+ * to fill in the Red/Green/Blue/.../Bits values too.
+ */
+
+ drb->Base.AllocStorage = driRenderbufferStorage;
+ drb->Base.Delete = driDeleteRenderbuffer;
+
+ drb->Base.Data = addr;
+
+ /* DRI renderbuffer-specific fields: */
+ drb->dPriv = dPriv;
+ drb->offset = offset;
+ drb->pitch = pitch;
+ drb->cpp = cpp;
+
+ /* may be changed if page flipping is active: */
+ drb->flippedOffset = offset;
+ drb->flippedPitch = pitch;
+ drb->flippedData = addr;
+ }
+ return drb;
+}
+
+
+/**
+ * Update the front and back renderbuffers' flippedPitch/Offset/Data fields.
+ * If stereo, flip both the left and right pairs.
+ * This is used when we do double buffering via page flipping.
+ * \param fb the framebuffer we're page flipping
+ * \param flipped if true, set flipped values, else set non-flipped values
+ */
+void
+driFlipRenderbuffers(struct gl_framebuffer *fb, GLboolean flipped)
+{
+ const GLuint count = fb->Visual.stereoMode ? 2 : 1;
+ GLuint lr; /* left or right */
+
+ /* we shouldn't really call this function if single-buffered, but
+ * play it safe.
+ */
+ if (!fb->Visual.doubleBufferMode)
+ return;
+
+ for (lr = 0; lr < count; lr++) {
+ GLuint frontBuf = (lr == 0) ? BUFFER_FRONT_LEFT : BUFFER_FRONT_RIGHT;
+ GLuint backBuf = (lr == 0) ? BUFFER_BACK_LEFT : BUFFER_BACK_RIGHT;
+ driRenderbuffer *front_drb
+ = (driRenderbuffer *) fb->Attachment[frontBuf].Renderbuffer;
+ driRenderbuffer *back_drb
+ = (driRenderbuffer *) fb->Attachment[backBuf].Renderbuffer;
+
+ if (flipped) {
+ front_drb->flippedOffset = back_drb->offset;
+ front_drb->flippedPitch = back_drb->pitch;
+ front_drb->flippedData = back_drb->Base.Data;
+ back_drb->flippedOffset = front_drb->offset;
+ back_drb->flippedPitch = front_drb->pitch;
+ back_drb->flippedData = front_drb->Base.Data;
+ }
+ else {
+ front_drb->flippedOffset = front_drb->offset;
+ front_drb->flippedPitch = front_drb->pitch;
+ front_drb->flippedData = front_drb->Base.Data;
+ back_drb->flippedOffset = back_drb->offset;
+ back_drb->flippedPitch = back_drb->pitch;
+ back_drb->flippedData = back_drb->Base.Data;
+ }
+ }
+}
+
+
+/**
+ * Check that the gl_framebuffer associated with dPriv is the right size.
+ * Resize the gl_framebuffer if needed.
+ * It's expected that the dPriv->driverPrivate member points to a
+ * gl_framebuffer object.
+ */
+void
+driUpdateFramebufferSize(GLcontext *ctx, const __DRIdrawablePrivate *dPriv)
+{
+ struct gl_framebuffer *fb = (struct gl_framebuffer *) dPriv->driverPrivate;
+ if (fb && (dPriv->w != fb->Width || dPriv->h != fb->Height)) {
+ ctx->Driver.ResizeBuffers(ctx, fb, dPriv->w, dPriv->h);
+ assert(fb->Width == dPriv->w);
+ assert(fb->Height == dPriv->h);
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/egl.c b/src/VBox/Additions/common/crOpenGL/egl.c
new file mode 100644
index 00000000..3d8a636d
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/egl.c
@@ -0,0 +1,967 @@
+/* $Id: egl.c $ */
+
+/** @file
+ * VBox OpenGL EGL implentation.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#include <EGL/egl.h>
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define EGL_ASSERT(expr) \
+ if (!(expr)) { printf("Assertion failed: %s\n", #expr); exit(1); }
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+struct VBEGLTLS
+{
+ /** The last EGL error. */
+ EGLint cErr;
+ /** The EGL API currently bound to this thread. */
+ EGLenum enmAPI;
+ /** The current context. */
+ EGLContext hCurrent;
+ /** The display bound to the current context. */
+ EGLDisplay hCurrentDisplay;
+ /** The draw surface bound to the current context. */
+ EGLSurface hCurrentDraw;
+ /** The read surface bound to the current context. */
+ EGLSurface hCurrentRead;
+};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @note IDs returned for surfaces should always be lower than these constants.
+ */
+/** This is OR-ed with a surface ID to mark it as a window, as GLX needs to
+ * know. */
+#define VBEGL_WINDOW_SURFACE 0x20000000
+/** This is OR-ed with a surface ID to mark it as a pbuffer, as GLX needs to
+ * know. */
+#define VBEGL_PBUFFER_SURFACE 0x40000000
+/** This is OR-ed with a surface ID to mark it as a pixmap, as GLX needs to
+ * know. */
+#define VBEGL_PIXMAP_SURFACE 0x80000000
+#define VBEGL_ANY_SURFACE (VBEGL_WINDOW_SURFACE | VBEGL_PBUFFER_SURFACE | VBEGL_PIXMAP_SURFACE)
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+static pthread_key_t g_tls;
+static pthread_once_t g_tlsOnce = PTHREAD_ONCE_INIT;
+static Display *g_pDefaultDisplay = NULL;
+static pthread_once_t g_defaultDisplayOnce = PTHREAD_ONCE_INIT;
+
+static void tlsInitOnce(void)
+{
+ pthread_key_create(&g_tls, NULL);
+}
+
+static struct VBEGLTLS *getTls(void)
+{
+ struct VBEGLTLS *pTls;
+
+ pthread_once(&g_tlsOnce, tlsInitOnce);
+ pTls = (struct VBEGLTLS *)pthread_getspecific(g_tls);
+ if (RT_LIKELY(pTls))
+ return pTls;
+ pTls = (struct VBEGLTLS *)malloc(sizeof(*pTls));
+ if (!pTls)
+ return NULL;
+ pTls->cErr = EGL_SUCCESS;
+ pTls->enmAPI = EGL_NONE;
+ pTls->hCurrent = EGL_NO_CONTEXT;
+ pTls->hCurrentDisplay = EGL_NO_DISPLAY;
+ pTls->hCurrentDraw = EGL_NO_SURFACE;
+ pTls->hCurrentRead = EGL_NO_SURFACE;
+ if (pthread_setspecific(g_tls, pTls) == 0)
+ return pTls;
+ free(pTls);
+ return NULL;
+}
+
+static void defaultDisplayInitOnce(void)
+{
+ g_pDefaultDisplay = XOpenDisplay(NULL);
+}
+
+static EGLBoolean clearEGLError(void)
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (!VALID_PTR(pTls))
+ return EGL_FALSE;
+ pTls->cErr = EGL_SUCCESS;
+ return EGL_TRUE;
+}
+
+static EGLBoolean setEGLError(EGLint cErr)
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (pTls)
+ pTls->cErr = cErr;
+ return EGL_FALSE;
+}
+
+static EGLBoolean testValidDisplay(EGLNativeDisplayType hDisplay)
+{
+ void *pSymbol = dlsym(NULL, "gbm_create_device");
+
+ if (hDisplay == EGL_DEFAULT_DISPLAY)
+ return EGL_TRUE;
+ if ((void *)hDisplay == NULL)
+ return EGL_FALSE;
+ /* This is the test that Mesa uses to see if this is a GBM "display". Not
+ * very pretty, but since no one can afford to break Mesa it should be
+ * safe. We need this to detect when the X server tries to load us. */
+ if (pSymbol != NULL && *(void **)hDisplay == pSymbol)
+ return EGL_FALSE;
+ return EGL_TRUE;
+}
+
+DECLEXPORT(EGLDisplay) eglGetDisplay(EGLNativeDisplayType hDisplay)
+{
+ Display *pDisplay;
+
+ if (!testValidDisplay(hDisplay))
+ return EGL_NO_DISPLAY;
+ if (!clearEGLError()) /* Set up our tls. */
+ return EGL_NO_DISPLAY;
+ if (hDisplay != EGL_DEFAULT_DISPLAY)
+ pDisplay = hDisplay;
+ else
+ {
+ pthread_once(&g_defaultDisplayOnce, defaultDisplayInitOnce);
+ pDisplay = g_pDefaultDisplay;
+ }
+ if (pDisplay && !strcmp(glXGetClientString(pDisplay, GLX_VENDOR), "Chromium"))
+ return (EGLDisplay) pDisplay;
+ return EGL_NO_DISPLAY;
+}
+
+DECLEXPORT(EGLint) eglGetError(void)
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (pTls)
+ return pTls->cErr;
+ return EGL_NOT_INITIALIZED;
+}
+
+DECLEXPORT(EGLBoolean) eglInitialize (EGLDisplay hDisplay, EGLint *pcMajor, EGLint *pcMinor)
+{
+ if (hDisplay == EGL_NO_DISPLAY)
+ return EGL_FALSE;
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_BAD_DISPLAY);
+ if (pcMajor)
+ *pcMajor = 1;
+ if (pcMinor)
+ *pcMinor = 4;
+ return clearEGLError();
+}
+
+/** @todo This function should terminate all allocated resources. */
+DECLEXPORT(EGLBoolean) eglTerminate(EGLDisplay hDisplay)
+{
+ if (!VALID_PTR(hDisplay))
+ return EGL_FALSE;
+ return EGL_TRUE;
+}
+
+DECLEXPORT(const char *) eglQueryString(EGLDisplay hDisplay, EGLint name)
+{
+ RT_NOREF(hDisplay);
+ switch (name)
+ {
+ case EGL_CLIENT_APIS:
+ return "OpenGL";
+ case EGL_VENDOR:
+ return "Chromium";
+ case EGL_VERSION:
+ return "1.4 Chromium";
+ case EGL_EXTENSIONS:
+ return "";
+ default:
+ return NULL;
+ }
+}
+
+DECLEXPORT(EGLBoolean) eglGetConfigs (EGLDisplay hDisplay, EGLConfig *paConfigs, EGLint caConfigs, EGLint *pcaConfigs)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ GLXFBConfig *paFBConfigs;
+ int caFBConfigs, i;
+
+ if (!VALID_PTR(pDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ if (!VALID_PTR(pcaConfigs))
+ return setEGLError(EGL_BAD_PARAMETER);
+ if (caConfigs > 0 && !VALID_PTR(paConfigs))
+ return setEGLError(EGL_BAD_PARAMETER);
+ paFBConfigs = glXGetFBConfigs(pDisplay, DefaultScreen(pDisplay), &caFBConfigs);
+ if (!VALID_PTR(paFBConfigs))
+ return setEGLError(EGL_BAD_PARAMETER);
+ if (caFBConfigs > caConfigs)
+ caFBConfigs = caConfigs;
+ *pcaConfigs = caFBConfigs;
+ for (i = 0; i < caFBConfigs; ++i)
+ paConfigs[i] = (EGLConfig)paFBConfigs[i];
+ XFree(paFBConfigs);
+ return clearEGLError();
+}
+
+static int convertEGLAttribToGLX(EGLint a_EGLAttrib)
+{
+ switch (a_EGLAttrib)
+ {
+ case EGL_BUFFER_SIZE:
+ return GLX_BUFFER_SIZE;
+ case EGL_RED_SIZE:
+ return GLX_RED_SIZE;
+ case EGL_GREEN_SIZE:
+ return GLX_GREEN_SIZE;
+ case EGL_BLUE_SIZE:
+ return GLX_BLUE_SIZE;
+ case EGL_LUMINANCE_SIZE:
+ return GLX_RED_SIZE;
+ case EGL_ALPHA_SIZE:
+ return GLX_ALPHA_SIZE;
+ /* case EGL_ALPHA_MASK_SIZE: */
+ /* case EGL_BIND_TO_TEXTURE_RGB: */
+ /* case EGL_BIND_TO_TEXTURE_RGBA: */
+ /* case EGL_COLOR_BUFFER_TYPE: */
+ /* case EGL_CONFIG_CAVEAT: */
+ case EGL_CONFIG_ID:
+ return GLX_FBCONFIG_ID;
+ /* case EGL_CONFORMANT: */
+ case EGL_DEPTH_SIZE:
+ return GLX_DEPTH_SIZE;
+ case EGL_LEVEL:
+ return GLX_LEVEL;
+ case EGL_MAX_PBUFFER_WIDTH:
+ return GLX_MAX_PBUFFER_WIDTH;
+ case EGL_MAX_PBUFFER_HEIGHT:
+ return GLX_MAX_PBUFFER_HEIGHT;
+ case EGL_MAX_PBUFFER_PIXELS:
+ return GLX_MAX_PBUFFER_PIXELS;
+ /* case EGL_MATCH_NATIVE_PIXMAP: */
+ /* case EGL_MAX_SWAP_INTERVAL: */
+ /* case EGL_MIN_SWAP_INTERVAL: */
+ case EGL_NATIVE_RENDERABLE:
+ return GLX_X_RENDERABLE;
+ case EGL_NATIVE_VISUAL_ID:
+ return GLX_VISUAL_ID;
+ /* case EGL_NATIVE_VISUAL_TYPE: */
+ /* case EGL_RENDERABLE_TYPE: */
+ case EGL_SAMPLE_BUFFERS:
+ return GLX_SAMPLE_BUFFERS;
+ case EGL_SAMPLES:
+ return GLX_SAMPLES;
+ case EGL_STENCIL_SIZE:
+ return GLX_STENCIL_SIZE;
+ /* case EGL_SURFACE_TYPE: */
+ /* case EGL_TRANSPARENT_TYPE: */
+ case EGL_TRANSPARENT_RED_VALUE:
+ return GLX_TRANSPARENT_RED_VALUE;
+ case EGL_TRANSPARENT_GREEN_VALUE:
+ return GLX_TRANSPARENT_GREEN_VALUE;
+ case EGL_TRANSPARENT_BLUE_VALUE:
+ return GLX_TRANSPARENT_BLUE_VALUE;
+ default:
+ return None;
+ }
+}
+
+DECLEXPORT(EGLBoolean) eglChooseConfig (EGLDisplay hDisplay, const EGLint *paAttribs, EGLConfig *paConfigs, EGLint caConfigs,
+ EGLint *pcConfigs)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ int aAttribList[256]; /* The list cannot be this long. */
+ unsigned cAttribs = 0, i;
+ const EGLint *pAttrib, *pAttrib2;
+ EGLint cRenderableType = EGL_OPENGL_ES_BIT;
+ unsigned cConfigCaveat = GLX_DONT_CARE, cConformant = GLX_DONT_CARE;
+ GLXFBConfig *paFBConfigs;
+ int caFBConfigs;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ if (!VALID_PTR(pcConfigs))
+ return setEGLError(EGL_BAD_PARAMETER);
+ if (caConfigs > 0 && !VALID_PTR(paConfigs))
+ return setEGLError(EGL_BAD_PARAMETER);
+ for (pAttrib = paAttribs; pAttrib != NULL && *pAttrib != EGL_NONE; pAttrib += 2)
+ {
+ bool fSkip = false;
+ int cGLXAttrib;
+
+ /* Check for illegal values. */
+ if ((*pAttrib == EGL_LEVEL || *pAttrib == EGL_MATCH_NATIVE_PIXMAP) && pAttrib[1] == EGL_DONT_CARE)
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ /* Check for values we can't handle. */
+ if ( (*pAttrib == EGL_ALPHA_MASK_SIZE)
+ && pAttrib[1] != EGL_DONT_CARE && pAttrib[1] != 0)
+ return setEGLError(EGL_BAD_ACCESS);
+ /** @todo try creating a pixmap from a native one with the configurations returned. */
+ if (*pAttrib == EGL_MATCH_NATIVE_PIXMAP)
+ return setEGLError(EGL_BAD_ACCESS);
+ if ( ( *pAttrib == EGL_MIN_SWAP_INTERVAL || *pAttrib == EGL_MAX_SWAP_INTERVAL
+ || *pAttrib == EGL_BIND_TO_TEXTURE_RGB || *pAttrib == EGL_BIND_TO_TEXTURE_RGBA)
+ && pAttrib[1] != EGL_DONT_CARE)
+ return setEGLError(EGL_BAD_ACCESS);
+ /* Ignore attributes which are repeated later. */
+ for (pAttrib2 = pAttrib + 2; *pAttrib2 != EGL_NONE; pAttrib2 += 2)
+ if (*pAttrib2 == *pAttrib)
+ {
+ fSkip = true;
+ break;
+ }
+
+ if (fSkip)
+ continue;
+ cGLXAttrib = convertEGLAttribToGLX(*pAttrib);
+ if (cGLXAttrib != None)
+ {
+ aAttribList[cAttribs] = cGLXAttrib;
+ if (pAttrib[1] == EGL_DONT_CARE)
+ aAttribList[cAttribs + 1] = GLX_DONT_CARE;
+ else
+ aAttribList[cAttribs + 1] = pAttrib[1];
+ cAttribs += 2;
+ }
+ else
+ {
+ switch (*pAttrib)
+ {
+ case EGL_COLOR_BUFFER_TYPE:
+ aAttribList[cAttribs] = GLX_X_VISUAL_TYPE;
+ aAttribList[cAttribs + 1] = pAttrib[1] == EGL_DONT_CARE ? GLX_DONT_CARE
+ : pAttrib[1] == EGL_RGB_BUFFER ? GLX_TRUE_COLOR
+ : pAttrib[1] == EGL_LUMINANCE_BUFFER ? GLX_GRAY_SCALE
+ : GL_FALSE;
+ if ( *pAttrib == EGL_COLOR_BUFFER_TYPE
+ && pAttrib[1] != EGL_DONT_CARE && pAttrib[1] != EGL_RGB_BUFFER)
+ return setEGLError(EGL_BAD_ACCESS);
+ break;
+ case EGL_CONFIG_CAVEAT:
+ cConfigCaveat = pAttrib[1] == EGL_DONT_CARE ? GLX_DONT_CARE
+ : pAttrib[1] == EGL_NONE ? GLX_NONE
+ : pAttrib[1] == EGL_SLOW_CONFIG ? GLX_SLOW_CONFIG
+ : pAttrib[1] == EGL_NON_CONFORMANT_CONFIG ? GLX_NON_CONFORMANT_CONFIG
+ : GL_FALSE;
+ if (!cConfigCaveat)
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ cAttribs -= 2;
+ break;
+ case EGL_CONFORMANT:
+ if (pAttrib[1] != EGL_OPENGL_BIT && pAttrib[1] != 0)
+ return setEGLError(EGL_BAD_ACCESS);
+ cConformant = pAttrib[1] == EGL_OPENGL_BIT ? GL_TRUE : GL_FALSE;
+ cAttribs -= 2;
+ break;
+ case EGL_NATIVE_VISUAL_TYPE:
+ aAttribList[cAttribs] = GLX_X_VISUAL_TYPE;
+ aAttribList[cAttribs + 1] = pAttrib[1] == EGL_DONT_CARE ? GLX_DONT_CARE
+ : pAttrib[1] == StaticGray ? GLX_STATIC_GRAY
+ : pAttrib[1] == StaticColor ? GLX_STATIC_COLOR
+ : pAttrib[1] == TrueColor ? GLX_TRUE_COLOR
+ : pAttrib[1] == GrayScale ? GLX_GRAY_SCALE
+ : pAttrib[1] == PseudoColor ? GLX_PSEUDO_COLOR
+ : pAttrib[1] == DirectColor ? GLX_DIRECT_COLOR
+ : GL_FALSE;
+ break;
+ case EGL_RENDERABLE_TYPE:
+ cRenderableType = pAttrib[1];
+ cAttribs -= 2; /* We did not add anything to the list. */
+ break;
+ case EGL_SURFACE_TYPE:
+ if (pAttrib[1] & ~(EGL_PBUFFER_BIT | EGL_PIXMAP_BIT | EGL_WINDOW_BIT))
+ return setEGLError(EGL_BAD_ACCESS);
+ aAttribList[cAttribs] = GLX_DRAWABLE_TYPE;
+ aAttribList[cAttribs + 1] = (pAttrib[1] & EGL_PBUFFER_BIT ? GLX_PBUFFER_BIT : 0)
+ | (pAttrib[1] & EGL_PIXMAP_BIT ? GLX_PIXMAP_BIT : 0)
+ | (pAttrib[1] & EGL_WINDOW_BIT ? GLX_WINDOW_BIT : 0);
+ break;
+ case EGL_TRANSPARENT_TYPE:
+ aAttribList[cAttribs] = GLX_TRANSPARENT_TYPE;
+ aAttribList[cAttribs + 1] = pAttrib[1] == EGL_DONT_CARE ? GLX_DONT_CARE
+ : pAttrib[1] == EGL_NONE ? GLX_NONE
+ : pAttrib[1] == EGL_TRANSPARENT_RGB ? GLX_TRANSPARENT_RGB
+ : GL_FALSE;
+ break;
+ default:
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ }
+ cAttribs += 2;
+ }
+ }
+ if (cConfigCaveat != GLX_DONT_CARE || cConformant != GLX_DONT_CARE)
+ {
+ aAttribList[cAttribs] = GLX_CONFIG_CAVEAT;
+ aAttribList[cAttribs + 1] = cConformant == GL_FALSE ? GLX_NON_CONFORMANT_CONFIG
+ : cConfigCaveat == EGL_SLOW_CONFIG ? GLX_SLOW_CONFIG
+ : GLX_NONE;
+ cAttribs += 2;
+ }
+ aAttribList[cAttribs] = GLX_RENDER_TYPE;
+ aAttribList[cAttribs + 1] = GLX_RGBA_BIT;
+ cAttribs += 2;
+ if (paAttribs != NULL)
+ {
+ aAttribList[cAttribs] = None;
+ EGL_ASSERT(cAttribs < RT_ELEMENTS(aAttribList));
+ if (!(cRenderableType & EGL_OPENGL_BIT))
+ return setEGLError(EGL_BAD_ACCESS);
+ }
+ paFBConfigs = glXChooseFBConfig(pDisplay, DefaultScreen(pDisplay), paAttribs != NULL ? aAttribList : NULL, &caFBConfigs);
+ if (paFBConfigs == NULL)
+ return setEGLError(EGL_BAD_ACCESS);
+ *pcConfigs = caFBConfigs;
+ for (i = 0; (GLint)i < caConfigs && (GLint)i < caFBConfigs; ++i)
+ paConfigs[i] = (EGLConfig)paFBConfigs[i];
+ XFree(paFBConfigs);
+ return clearEGLError();
+}
+
+DECLEXPORT(EGLBoolean) eglGetConfigAttrib (EGLDisplay hDisplay, EGLConfig cConfig, EGLint cAttribute, EGLint *pValue)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ int cGLXAttribute = convertEGLAttribToGLX(cAttribute);
+ int cValue;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ if (!VALID_PTR(pValue))
+ return setEGLError(EGL_BAD_PARAMETER);
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_FBCONFIG_ID, &cValue))
+ return setEGLError(EGL_BAD_CONFIG);
+ if (cGLXAttribute != None)
+ {
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, cGLXAttribute, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue;
+ return clearEGLError();
+ }
+ switch (cAttribute)
+ {
+ case EGL_ALPHA_MASK_SIZE:
+ *pValue = 0;
+ return clearEGLError();
+ case EGL_LUMINANCE_SIZE:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_X_VISUAL_TYPE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ if (cValue == GLX_STATIC_GRAY || cValue == GLX_GRAY_SCALE)
+ {
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_RED_SIZE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue;
+ }
+ else
+ *pValue = 0;
+ return clearEGLError();
+ case EGL_COLOR_BUFFER_TYPE:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_X_VISUAL_TYPE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ if (cValue == GLX_STATIC_GRAY || cValue == GLX_GRAY_SCALE)
+ *pValue = EGL_LUMINANCE_BUFFER;
+ else
+ *pValue = EGL_RGB_BUFFER;
+ return clearEGLError();
+ case EGL_CONFIG_CAVEAT:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_CONFIG_CAVEAT, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue == GLX_NONE ? EGL_NONE : cValue == GLX_SLOW_CONFIG ? EGL_SLOW_CONFIG : GLX_NON_CONFORMANT_CONFIG;
+ return clearEGLError();
+ case EGL_CONFORMANT:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_CONFIG_CAVEAT, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue == GLX_NON_CONFORMANT_CONFIG ? 0 : EGL_OPENGL_BIT;
+ return clearEGLError();
+ case EGL_MATCH_NATIVE_PIXMAP:
+ case EGL_MIN_SWAP_INTERVAL:
+ case EGL_MAX_SWAP_INTERVAL:
+ return setEGLError(EGL_BAD_ACCESS);
+ case EGL_NATIVE_VISUAL_TYPE:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_X_VISUAL_TYPE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue == GLX_STATIC_GRAY ? StaticGray
+ : cValue == GLX_STATIC_COLOR ? StaticColor
+ : cValue == GLX_TRUE_COLOR ? TrueColor
+ : cValue == GLX_GRAY_SCALE ? GrayScale
+ : cValue == GLX_PSEUDO_COLOR ? PseudoColor
+ : cValue == GLX_DIRECT_COLOR ? DirectColor
+ : -1;
+ return clearEGLError();
+ case EGL_RENDERABLE_TYPE:
+ *pValue = EGL_OPENGL_BIT;
+ return clearEGLError();
+ case EGL_SURFACE_TYPE:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_DRAWABLE_TYPE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = (cValue & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0)
+ | (cValue & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0)
+ | (cValue & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0);
+ return clearEGLError();
+ case EGL_TRANSPARENT_TYPE:
+ if (glXGetFBConfigAttrib(pDisplay, (GLXFBConfig)cConfig, GLX_TRANSPARENT_TYPE, &cValue))
+ return setEGLError(EGL_BAD_ACCESS);
+ *pValue = cValue == GLX_NONE ? EGL_NONE
+ : cValue == GLX_TRANSPARENT_RGB ? EGL_TRANSPARENT_RGB
+ : EGL_FALSE;
+ return *pValue != EGL_FALSE ? clearEGLError() : setEGLError(EGL_BAD_ACCESS);
+ default:
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ }
+ return clearEGLError();
+}
+
+DECLEXPORT(EGLSurface) eglCreateWindowSurface(EGLDisplay hDisplay, EGLConfig config, EGLNativeWindowType hWindow,
+ const EGLint *paAttributes)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ GLXWindow hGLXWindow;
+
+ if (!VALID_PTR(hDisplay))
+ {
+ setEGLError(EGL_NOT_INITIALIZED);
+ return EGL_NO_SURFACE;
+ }
+ if (paAttributes != NULL) /* Sanity test only. */
+ while (*paAttributes != EGL_NONE)
+ {
+ if (*paAttributes != EGL_RENDER_BUFFER)
+ {
+ setEGLError(EGL_BAD_MATCH);
+ return EGL_NO_SURFACE;
+ }
+ paAttributes += 2;
+ }
+ hGLXWindow = glXCreateWindow(pDisplay, (GLXFBConfig)config, (Window)hWindow, NULL);
+ if (hGLXWindow == None)
+ {
+ setEGLError(EGL_BAD_ALLOC);
+ return EGL_NO_SURFACE;
+ }
+ EGL_ASSERT(hGLXWindow < VBEGL_WINDOW_SURFACE); /* Greater than the maximum XID. */
+ clearEGLError();
+ return (EGLSurface)(hGLXWindow | VBEGL_WINDOW_SURFACE);
+}
+
+static void setAttribute(int *pcStoreIndex, int *pcCurIndex, int *paGLXAttributes, int cAttribute, int cValue)
+{
+ if (*pcStoreIndex < 0)
+ {
+ *pcStoreIndex = *pcCurIndex;
+ *pcCurIndex += 2;
+ paGLXAttributes[*pcStoreIndex] = cAttribute;
+ }
+ paGLXAttributes[*pcStoreIndex + 1] = cValue;
+}
+
+DECLEXPORT(EGLSurface) eglCreatePbufferSurface(EGLDisplay hDisplay, EGLConfig config, EGLint const *paAttributes)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ enum { CPS_WIDTH = 0, CPS_HEIGHT, CPS_LARGEST, CPS_PRESERVED, CPS_TEX_FORMAT, CPS_TEX_TARGET, CPS_MIPMAP_TEX, CPS_END };
+ int acIndices[CPS_END];
+ int aAttributes[CPS_END * 2];
+ int cIndex = 0;
+ unsigned i;
+ GLXPbuffer hPbuffer;
+
+ if (!VALID_PTR(hDisplay))
+ {
+ setEGLError(EGL_NOT_INITIALIZED);
+ return EGL_NO_SURFACE;
+ }
+ for (i = 0; i < RT_ELEMENTS(acIndices); ++i)
+ acIndices[i] = -1;
+ if (paAttributes != NULL)
+ while (*paAttributes != EGL_NONE)
+ {
+ switch (*paAttributes)
+ {
+ case EGL_WIDTH:
+ setAttribute(&acIndices[CPS_WIDTH], &cIndex, aAttributes, GLX_PBUFFER_WIDTH, paAttributes[1]);
+ break;
+ case EGL_HEIGHT:
+ setAttribute(&acIndices[CPS_HEIGHT], &cIndex, aAttributes, GLX_LARGEST_PBUFFER, paAttributes[1]);
+ break;
+ case EGL_LARGEST_PBUFFER:
+ setAttribute(&acIndices[CPS_LARGEST], &cIndex, aAttributes, GLX_PBUFFER_HEIGHT, paAttributes[1]);
+ break;
+ case EGL_BUFFER_PRESERVED:
+ setAttribute(&acIndices[CPS_PRESERVED], &cIndex, aAttributes, GLX_PRESERVED_CONTENTS, paAttributes[1]);
+ break;
+ case EGL_TEXTURE_FORMAT:
+ setAttribute(&acIndices[CPS_TEX_FORMAT], &cIndex, aAttributes, GLX_TEXTURE_FORMAT_EXT, paAttributes[1]);
+ break;
+ case EGL_TEXTURE_TARGET:
+ setAttribute(&acIndices[CPS_TEX_TARGET], &cIndex, aAttributes, GLX_TEXTURE_TARGET_EXT, paAttributes[1]);
+ break;
+ case EGL_MIPMAP_TEXTURE:
+ setAttribute(&acIndices[CPS_MIPMAP_TEX], &cIndex, aAttributes, GLX_MIPMAP_TEXTURE_EXT, paAttributes[1]);
+ break;
+ case EGL_VG_ALPHA_FORMAT:
+ case EGL_VG_COLORSPACE:
+ {
+ setEGLError(EGL_BAD_MATCH);
+ return EGL_NO_SURFACE;
+ }
+ }
+ paAttributes += 2;
+ }
+ EGL_ASSERT((unsigned)cIndex < RT_ELEMENTS(aAttributes) - 1U);
+ aAttributes[cIndex + 1] = None;
+ hPbuffer = glXCreatePbuffer(pDisplay, (GLXFBConfig)config, aAttributes);
+ if (hPbuffer == None)
+ {
+ setEGLError(EGL_BAD_ALLOC);
+ return EGL_NO_SURFACE;
+ }
+ EGL_ASSERT(hPbuffer < VBEGL_WINDOW_SURFACE); /* Greater than the maximum XID. */
+ clearEGLError();
+ return (EGLSurface)(hPbuffer | VBEGL_PBUFFER_SURFACE);
+}
+
+DECLEXPORT(EGLSurface) eglCreatePixmapSurface(EGLDisplay hDisplay, EGLConfig config, EGLNativePixmapType hPixmap,
+ const EGLint *paAttributes)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ GLXPixmap hGLXPixmap;
+
+ if (!VALID_PTR(hDisplay))
+ {
+ setEGLError(EGL_NOT_INITIALIZED);
+ return EGL_NO_SURFACE;
+ }
+ if (paAttributes != NULL) /* Sanity test only. */
+ if (*paAttributes != EGL_NONE)
+ {
+ if (*paAttributes == EGL_VG_COLORSPACE || *paAttributes == EGL_VG_ALPHA_FORMAT)
+ {
+ setEGLError(EGL_BAD_MATCH);
+ return EGL_NO_SURFACE;
+ }
+ else
+ {
+ setEGLError(EGL_BAD_ATTRIBUTE);
+ return EGL_NO_SURFACE;
+ }
+ }
+ hGLXPixmap = glXCreatePixmap(pDisplay, (GLXFBConfig)config, (Pixmap)hPixmap, NULL);
+ if (hGLXPixmap == None)
+ {
+ setEGLError(EGL_BAD_MATCH);
+ return EGL_NO_SURFACE;
+ }
+ EGL_ASSERT(hGLXPixmap < VBEGL_WINDOW_SURFACE); /* Greater than the maximum XID. */
+ clearEGLError();
+ return (EGLSurface)(hGLXPixmap | VBEGL_PIXMAP_SURFACE);
+}
+
+DECLEXPORT(EGLBoolean) eglDestroySurface(EGLDisplay hDisplay, EGLSurface hSurface)
+{
+ Display *pDisplay = (Display *)hDisplay;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ switch ((GLXDrawable)hSurface & VBEGL_ANY_SURFACE)
+ {
+ case VBEGL_WINDOW_SURFACE:
+ glXDestroyWindow(pDisplay, (GLXWindow)hSurface & ~VBEGL_WINDOW_SURFACE);
+ return clearEGLError();
+ case VBEGL_PBUFFER_SURFACE:
+ glXDestroyPbuffer(pDisplay, (GLXPbuffer)hSurface & ~VBEGL_PBUFFER_SURFACE);
+ return clearEGLError();
+ case VBEGL_PIXMAP_SURFACE:
+ glXDestroyPixmap(pDisplay, (GLXPixmap)hSurface & ~VBEGL_PIXMAP_SURFACE);
+ return clearEGLError();
+ default:
+ return setEGLError(EGL_BAD_SURFACE);
+ }
+}
+
+DECLEXPORT(EGLBoolean) eglSurfaceAttrib(EGLDisplay hDisplay, EGLSurface hSurface, EGLint cAttribute, EGLint cValue)
+{
+ NOREF(hDisplay);
+ NOREF(hSurface);
+ NOREF(cValue);
+ switch (cAttribute)
+ {
+ case EGL_MIPMAP_LEVEL:
+ case EGL_MULTISAMPLE_RESOLVE:
+ case EGL_SWAP_BEHAVIOR:
+ return setEGLError(EGL_BAD_MATCH);
+ default:
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ }
+}
+
+DECLEXPORT(EGLBoolean) eglQuerySurface(EGLDisplay hDisplay, EGLSurface hSurface, EGLint cAttribute, EGLint *cValue)
+{
+ NOREF(hDisplay);
+ NOREF(hSurface);
+ NOREF(cAttribute);
+ NOREF(cValue);
+ return setEGLError(EGL_BAD_MATCH);
+}
+
+DECLEXPORT(EGLBoolean) eglBindTexImage(EGLDisplay hDisplay, EGLSurface hSurface, EGLint cBuffer)
+{
+ NOREF(hDisplay);
+ NOREF(hSurface);
+ NOREF(cBuffer);
+ return setEGLError(EGL_BAD_MATCH);
+}
+
+DECLEXPORT(EGLBoolean) eglReleaseTexImage(EGLDisplay hDisplay, EGLSurface hSurface, EGLint cBuffer)
+{
+ NOREF(hDisplay);
+ NOREF(hSurface);
+ NOREF(cBuffer);
+ return setEGLError(EGL_BAD_MATCH);
+}
+
+DECLEXPORT(EGLBoolean) eglBindAPI(EGLenum enmApi)
+{
+ return enmApi == EGL_OPENGL_API ? clearEGLError() : setEGLError(EGL_BAD_PARAMETER);
+}
+
+DECLEXPORT(EGLenum) eglQueryAPI(void)
+{
+ return EGL_OPENGL_API;
+}
+
+DECLEXPORT(EGLContext) eglCreateContext(EGLDisplay hDisplay, EGLConfig hConfig, EGLContext hSharedContext,
+ const EGLint *paAttribs)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ GLXContext hNewContext;
+
+ if (!VALID_PTR(hDisplay))
+ {
+ setEGLError(EGL_NOT_INITIALIZED);
+ return EGL_NO_CONTEXT;
+ }
+ if (paAttribs != NULL && *paAttribs != EGL_NONE)
+ {
+ setEGLError(EGL_BAD_ATTRIBUTE);
+ return EGL_NO_CONTEXT;
+ }
+ hNewContext = glXCreateNewContext(pDisplay, (GLXFBConfig)hConfig, GLX_RGBA_TYPE, (GLXContext)hSharedContext, true);
+ if (hNewContext)
+ {
+ clearEGLError();
+ return (EGLContext)hNewContext;
+ }
+ setEGLError(EGL_BAD_MATCH);
+ return EGL_NO_CONTEXT;
+}
+
+DECLEXPORT(EGLBoolean) eglDestroyContext(EGLDisplay hDisplay, EGLContext hContext)
+{
+ Display *pDisplay = (Display *)hDisplay;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ glXDestroyContext(pDisplay, (GLXContext) hContext);
+ return clearEGLError();
+}
+
+DECLEXPORT(EGLBoolean) eglMakeCurrent(EGLDisplay hDisplay, EGLSurface hDraw, EGLSurface hRead, EGLContext hContext)
+{
+ Display *pDisplay = (Display *)hDisplay;
+ GLXDrawable hGLXDraw = hDraw == EGL_NO_SURFACE ? None : (GLXDrawable)hDraw & ~VBEGL_ANY_SURFACE;
+ GLXDrawable hGLXRead = hRead == EGL_NO_SURFACE ? None : (GLXDrawable)hRead & ~VBEGL_ANY_SURFACE;
+ GLXContext hGLXContext = hContext == EGL_NO_CONTEXT ? None : (GLXContext)hContext;
+ struct VBEGLTLS *pTls = getTls();
+
+ if (!VALID_PTR(hDisplay) || !VALID_PTR(pTls))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ if (glXMakeContextCurrent(pDisplay, hGLXDraw, hGLXRead, hGLXContext))
+ {
+ pTls->hCurrent = hContext;
+ pTls->hCurrentDraw = hDraw;
+ pTls->hCurrentRead = hRead;
+ return clearEGLError();
+ }
+ else
+ return setEGLError(EGL_BAD_MATCH);
+}
+
+DECLEXPORT(EGLContext) eglGetCurrentContext(void)
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (!VALID_PTR(pTls))
+ return EGL_NO_CONTEXT;
+ clearEGLError();
+ return pTls->hCurrent;
+}
+
+DECLEXPORT(EGLSurface) eglGetCurrentSurface(EGLint cOp)
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (!VALID_PTR(pTls))
+ return EGL_NO_SURFACE;
+ clearEGLError();
+ switch (cOp)
+ {
+ case EGL_DRAW:
+ return pTls->hCurrentDraw;
+ case EGL_READ:
+ return pTls->hCurrentRead;
+ default:
+ setEGLError(EGL_BAD_PARAMETER);
+ return EGL_NO_SURFACE;
+ }
+}
+
+DECLEXPORT(EGLDisplay) eglGetCurrentDisplay(void)
+{
+ struct VBEGLTLS *pTls;
+
+ pTls = getTls();
+ if (!VALID_PTR(pTls))
+ return EGL_NO_DISPLAY;
+ clearEGLError();
+ return pTls->hCurrentDisplay;
+}
+
+DECLEXPORT(EGLBoolean) eglQueryContext(EGLDisplay hDisplay, EGLContext hContext, EGLint cAttribute, EGLint *pcValue)
+{
+ Display *pDisplay = (Display *)hDisplay;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ if (!VALID_PTR(pcValue))
+ return setEGLError(EGL_BAD_PARAMETER);
+ switch (cAttribute)
+ {
+ case EGL_CONFIG_ID:
+ {
+ int cValue = 0;
+
+ if (glXQueryContext(pDisplay, (GLXContext)hContext, GLX_FBCONFIG_ID, &cValue) == Success)
+ {
+ *pcValue = cValue;
+ return clearEGLError();
+ }
+ return setEGLError(EGL_BAD_MATCH);
+ }
+ case EGL_CONTEXT_CLIENT_TYPE:
+ *pcValue = EGL_OPENGL_API;
+ return clearEGLError();
+ case EGL_CONTEXT_CLIENT_VERSION:
+ *pcValue = 0;
+ return clearEGLError();
+ case EGL_RENDER_BUFFER:
+ *pcValue = EGL_BACK_BUFFER;
+ return clearEGLError();
+ default:
+ return setEGLError(EGL_BAD_ATTRIBUTE);
+ }
+}
+
+DECLEXPORT(EGLBoolean) eglWaitClient(void)
+{
+ glXWaitGL();
+ return clearEGLError();
+}
+
+DECLEXPORT(EGLBoolean) eglWaitGL(void)
+{
+ return setEGLError(EGL_BAD_PARAMETER); /* OpenGL ES only. */
+}
+
+DECLEXPORT(EGLBoolean) eglWaitNative(EGLint cEngine)
+{
+ if (cEngine != EGL_CORE_NATIVE_ENGINE)
+ return setEGLError(EGL_BAD_PARAMETER);
+ glXWaitX();
+ return clearEGLError();
+}
+
+DECLEXPORT(EGLBoolean) eglSwapBuffers(EGLDisplay hDisplay, EGLSurface hSurface)
+{
+ Display *pDisplay = (Display *)hDisplay;
+
+ if (!VALID_PTR(hDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+ glXSwapBuffers(pDisplay, (GLXDrawable)hSurface & ~VBEGL_ANY_SURFACE);
+ return clearEGLError();
+}
+
+/** @todo Work out how this fits over what Chromium has to offer. */
+DECLEXPORT(EGLBoolean) eglCopyBuffers(EGLDisplay hDisplay, EGLSurface hSurface, EGLNativePixmapType hPixmap)
+{
+ Display *pDisplay = (Display *)hDisplay;
+
+ if (!VALID_PTR(pDisplay))
+ return setEGLError(EGL_NOT_INITIALIZED);
+
+ NOREF(hSurface);
+ NOREF(hPixmap);
+ return setEGLError(EGL_BAD_MATCH);
+}
+
+DECLEXPORT(EGLBoolean) eglSwapInterval (EGLDisplay dpy, EGLint interval)
+{
+ NOREF(dpy);
+ NOREF(interval);
+ return EGL_TRUE;
+}
+
+typedef void (*VBEGLFuncPtr)(void);
+DECLEXPORT(VBEGLFuncPtr)eglGetProcAddress(const char *pszName)
+{
+ clearEGLError();
+ return glXGetProcAddress((const GLubyte *)pszName);
+}
+
+DECLEXPORT(EGLBoolean) eglReleaseThread()
+{
+ struct VBEGLTLS *pTls = getTls();
+
+ if (!(pTls))
+ return EGL_TRUE;
+ free(pTls);
+ /* Can this fail with ENOMEM? */
+ pthread_setspecific(g_tls, NULL);
+ return EGL_TRUE;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/entrypoints.py b/src/VBox/Additions/common/crOpenGL/entrypoints.py
new file mode 100755
index 00000000..bdd93e75
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/entrypoints.py
@@ -0,0 +1,180 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+"""
+This module generates C entrypoints for all the OpenGL functions
+and the special Chromium meta/glue functions.
+"""
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints(hacks = []):
+ """Emit code for all the OpenGL/Chromium entrypoints.
+ hacks is an optional list of functions which are special cased.
+ """
+
+ apiutil.CopyrightC()
+
+ print('#define GL_GLEXT_PROTOTYPES')
+ print('#include <stdio.h>')
+ print('#include <stdlib.h>')
+ print('#include <GL/gl.h>')
+ print('#include "chromium.h"')
+ print('#include "stub.h"')
+ print('#include "dri_glx.h"')
+ print('')
+ print('#ifdef __GNUC__')
+ print('# if (__GNUC__ << 16) + __GNUC_MINOR__ >= 0x40002')
+ print('# pragma GCC diagnostic ignored "-Wunused-parameter"')
+ print('# endif')
+ print('#endif')
+
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ # this function is defined in stub.c
+ continue
+
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ if func_name in hacks:
+ print("/* hacked entrypoint: %s */" % func_name)
+ if func_name == "TexImage3D":
+ # Pretty common: internalformat is GLenum, not GLint
+ print("void glTexImage3D( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels )")
+ print("{")
+ print("\tglim.TexImage3D( target, level, (GLint) internalformat, width, height, depth, border, format, type, pixels );")
+ print("}")
+ elif func_name == "TexImage2D":
+ # Pretty common: internalformat is GLenum, not GLint
+ print("void glTexImage2D( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels )")
+ print("{")
+ print("\tglim.TexImage2D( target, level, (GLint) internalformat, width, height, border, format, type, pixels );")
+ print("}")
+ elif func_name == "TexImage1D":
+ # Pretty common: internalformat is GLenum, not GLint
+ print("void glTexImage1D( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels )")
+ print("{")
+ print("\tglim.TexImage1D( target, level, (GLint) internalformat, width, border, format, type, pixels );")
+ print("}")
+ elif func_name == "EdgeFlagPointer":
+ # second arg is GLboolean instead of GLvoid
+ print("void glEdgeFlagPointer( GLsizei stride, const GLboolean *pointer )")
+ print("{")
+ print("\tglim.EdgeFlagPointer( stride, pointer );")
+ print("}")
+ elif func_name == "ProgramParameters4fvNV":
+ print("void glProgramParameters4fvNV( GLenum target, GLuint index, GLuint num, const GLfloat *params )")
+ print("{")
+ print("\tglim.ProgramParameters4fvNV( target, index, num, params );")
+ print("}")
+ elif func_name == "MultiDrawElementsEXT":
+ print("void glMultiDrawElementsEXT(GLenum mode, GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount)")
+ print("{")
+ print("\tglim.MultiDrawElementsEXT(mode, count,type, indices, primcount);")
+ print("}")
+ elif func_name == "ProgramParameters4dvNV":
+ print("void glProgramParameters4dvNV( GLenum target, GLuint index, GLuint num, const GLdouble *params )")
+ print("{")
+ print("\tglim.ProgramParameters4dvNV( target, index, num, params );")
+ print("}")
+ else:
+ # the usual path
+ print("%s VBOXGLTAG(gl%s)(%s);" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("")
+ print("%s VBOXGLTAG(gl%s)(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("{")
+ print("\t", end="")
+ if return_type != "void":
+ print("return ", end=" ")
+ print("glim.%s(%s);" % (func_name, apiutil.MakeCallString(params)))
+ print("}")
+ print("")
+
+ print('/*')
+ print('* Aliases')
+ print('*/')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ if func_name in hacks:
+ print("/* hacked entrypoint: %s */" % func_name)
+ if func_name == "MultiDrawArrays":
+ print("void glMultiDrawArrays( GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount )")
+ print("{")
+ print("\tglim.MultiDrawArraysEXT( mode, (GLint*)first, (GLsizei*)count, primcount );")
+ print("}")
+ elif func_name == "BufferData":
+ print("void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)")
+ print("{")
+ print("\tglim.BufferDataARB(target, size, data, usage);")
+ print("}")
+ elif func_name == "BufferSubData":
+ print("void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)")
+ print("{")
+ print("\tglim.BufferSubDataARB(target, offset, size, data);")
+ print("}")
+ elif func_name == "GetBufferSubData":
+ print("void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data)")
+ print("{")
+ print("\tglim.GetBufferSubDataARB(target, offset, size, data);")
+ print("}")
+ else:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print("%s VBOXGLTAG(gl%s)(%s);" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("")
+ print("%s VBOXGLTAG(gl%s)(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("{")
+ print("\t", end="")
+ if return_type != "void":
+ print("return ", end=" ")
+ print("glim.%s(%s);" % (alias, apiutil.MakeCallString(params)))
+ print("}")
+ print("")
+
+ print('/*')
+ print('* No-op stubs')
+ print('*/')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ print("%s VBOXGLTAG(gl%s)(%s);" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("")
+ print("%s VBOXGLTAG(gl%s)(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("{")
+ if return_type != "void":
+ print("return (%s) 0" % return_type)
+ print("}")
+ print("")
+
diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_drv.c b/src/VBox/Additions/common/crOpenGL/fakedri_drv.c
new file mode 100644
index 00000000..1fab45b0
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/fakedri_drv.c
@@ -0,0 +1,878 @@
+/* $Id: fakedri_drv.c $ */
+/** @file
+ * VBox OpenGL DRI driver functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#define _GNU_SOURCE 1
+
+#include "cr_error.h"
+#include "cr_gl.h"
+#include "cr_mem.h"
+#include "stub.h"
+#include "fakedri_drv.h"
+#include "dri_glx.h"
+#include "iprt/mem.h"
+#include <iprt/errcore.h>
+#include <dlfcn.h>
+#include <elf.h>
+#include <unistd.h>
+
+#if defined(RT_OS_FREEBSD)
+#include <sys/param.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <string.h>
+#endif
+
+/** X server message type definitions. */
+typedef enum {
+ X_PROBED, /* Value was probed */
+ X_CONFIG, /* Value was given in the config file */
+ X_DEFAULT, /* Value is a default */
+ X_CMDLINE, /* Value was given on the command line */
+ X_NOTICE, /* Notice */
+ X_ERROR, /* Error message */
+ X_WARNING, /* Warning message */
+ X_INFO, /* Informational message */
+ X_NONE, /* No prefix */
+ X_NOT_IMPLEMENTED, /* Not implemented */
+ X_UNKNOWN = -1 /* unknown -- this must always be last */
+} MessageType;
+
+#define VBOX_NO_MESA_PATCH_REPORTS
+
+//#define DEBUG_DRI_CALLS
+
+/// @todo this could be different...
+#ifdef RT_ARCH_AMD64
+# ifdef RT_OS_FREEBSD
+# define DRI_DEFAULT_DRIVER_DIR "/usr/local/lib/dri"
+# define DRI_XORG_DRV_DIR "/usr/local/lib/xorg/modules/drivers/"
+# else
+# define DRI_DEFAULT_DRIVER_DIR "/usr/lib64/dri:/usr/lib/dri:/usr/lib/x86_64-linux-gnu/dri:/usr/lib/xorg/modules/dri"
+# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/"
+# endif
+#else
+# ifdef RT_OS_FREEBSD
+# define DRI_DEFAULT_DRIVER_DIR "/usr/local/lib/dri"
+# define DRI_XORG_DRV_DIR "/usr/local/lib/xorg/modules/drivers/"
+# else
+# define DRI_DEFAULT_DRIVER_DIR "/usr/lib/dri:/usr/lib/i386-linux-gnu/dri:/usr/lib/xorg/modules/dri"
+# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/"
+# endif
+#endif
+
+#ifdef DEBUG_DRI_CALLS
+ #define SWDRI_SHOWNAME(pext, func) \
+ crDebug("SWDRI: sc %s->%s", #pext, #func)
+#else
+ #define SWDRI_SHOWNAME(pext, func)
+#endif
+
+#define SWDRI_SAFECALL(pext, func, ...) \
+ SWDRI_SHOWNAME(pext, func); \
+ if (pext && pext->func){ \
+ (*pext->func)(__VA_ARGS__); \
+ } else { \
+ crDebug("swcore_call NULL for "#func); \
+ }
+
+#define SWDRI_SAFERET(pext, func, ...) \
+ SWDRI_SHOWNAME(pext, func); \
+ if (pext && pext->func){ \
+ return (*pext->func)(__VA_ARGS__); \
+ } else { \
+ crDebug("swcore_call NULL for "#func); \
+ return 0; \
+ }
+
+#define SWDRI_SAFERET_CORE(func, ...) SWDRI_SAFERET(gpSwDriCoreExternsion, func, __VA_ARGS__)
+#define SWDRI_SAFECALL_CORE(func, ...) SWDRI_SAFECALL(gpSwDriCoreExternsion, func, __VA_ARGS__)
+#define SWDRI_SAFERET_SWRAST(func, ...) SWDRI_SAFERET(gpSwDriSwrastExtension, func, __VA_ARGS__)
+#define SWDRI_SAFECALL_SWRAST(func, ...) SWDRI_SAFECALL(gpSwDriSwrastExtension, func, __VA_ARGS__)
+
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+
+#ifdef RT_ARCH_AMD64
+# define DRI_ELFSYM Elf64_Sym
+#else
+# define DRI_ELFSYM Elf32_Sym
+#endif
+
+#ifdef RT_ARCH_AMD64
+typedef struct _FAKEDRI_PatchNode
+{
+ const char* psFuncName;
+ void *pDstStart, *pDstEnd;
+ const void *pSrcStart, *pSrcEnd;
+
+ struct _FAKEDRI_PatchNode *pNext;
+} FAKEDRI_PatchNode;
+static FAKEDRI_PatchNode *g_pFreeList=NULL, *g_pRepatchList=NULL;
+#endif
+
+static struct _glapi_table* vbox_glapi_table = NULL;
+fakedri_glxapi_table glxim;
+
+static const __DRIextension **gppSwDriExternsion = NULL;
+static const __DRIcoreExtension *gpSwDriCoreExternsion = NULL;
+static const __DRIswrastExtension *gpSwDriSwrastExtension = NULL;
+
+extern const __DRIextension * __driDriverExtensions[];
+
+#define VBOX_SET_MESA_FUNC(table, name, func) \
+ if (_glapi_get_proc_offset(name)>=0) SET_by_offset(table, _glapi_get_proc_offset(name), func); \
+ else crWarning("%s not found in mesa table", name)
+
+#define GLAPI_ENTRY(Func) VBOX_SET_MESA_FUNC(vbox_glapi_table, "gl"#Func, cr_gl##Func);
+
+static void
+vboxPatchMesaExport(const char* psFuncName, const void *pStart, const void *pEnd);
+
+static void
+vboxPatchMesaGLAPITable()
+{
+ void *pGLTable;
+
+ pGLTable = (void *)_glapi_get_dispatch();
+ vbox_glapi_table = crAlloc(_glapi_get_dispatch_table_size() * sizeof (void *));
+ if (!vbox_glapi_table)
+ {
+ crError("Not enough memory to allocate dispatch table");
+ }
+ crMemcpy(vbox_glapi_table, pGLTable, _glapi_get_dispatch_table_size() * sizeof (void *));
+
+ #include "fakedri_glfuncsList.h"
+
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glBlendEquationSeparateEXT", cr_glBlendEquationSeparate);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glSampleMaskSGIS", cr_glSampleMaskEXT);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glSamplePatternSGIS", cr_glSamplePatternEXT);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2dMESA", cr_glWindowPos2d);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2dvMESA", cr_glWindowPos2dv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2fMESA", cr_glWindowPos2f);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2fvMESA", cr_glWindowPos2fv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2iMESA", cr_glWindowPos2i);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2ivMESA", cr_glWindowPos2iv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2sMESA", cr_glWindowPos2s);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos2svMESA", cr_glWindowPos2sv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3dMESA", cr_glWindowPos3d);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3dvMESA", cr_glWindowPos3dv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3fMESA", cr_glWindowPos3f);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3fvMESA", cr_glWindowPos3fv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3iMESA", cr_glWindowPos3i);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3ivMESA", cr_glWindowPos3iv);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3sMESA", cr_glWindowPos3s);
+ VBOX_SET_MESA_FUNC(vbox_glapi_table, "glWindowPos3svMESA", cr_glWindowPos3sv);
+
+ _glapi_set_dispatch(vbox_glapi_table);
+};
+#undef GLAPI_ENTRY
+
+#define GLXAPI_ENTRY(Func) pGLXTable->Func = VBOXGLXTAG(glX##Func);
+static void
+vboxFillGLXAPITable(fakedri_glxapi_table *pGLXTable)
+{
+ #include "fakedri_glxfuncsList.h"
+}
+#undef GLXAPI_ENTRY
+
+static void
+vboxApplyPatch(const char* psFuncName, void *pDst, const void *pSrc, unsigned long size)
+{
+ void *alPatch;
+ int rv;
+
+ /* Get aligned start address we're going to patch*/
+ alPatch = (void*) ((uintptr_t)pDst & ~(uintptr_t)(PAGESIZE-1));
+
+#ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("MProtecting: %p, %li", alPatch, pDst-alPatch+size);
+#endif
+
+ /* Get write access to mesa functions */
+ rv = RTMemProtect(alPatch, pDst-alPatch+size, RTMEM_PROT_READ|RTMEM_PROT_WRITE|RTMEM_PROT_EXEC);
+ if (RT_FAILURE(rv))
+ {
+ crError("mprotect failed with %x (%s)", rv, psFuncName);
+ }
+
+#ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Writing %li bytes to %p from %p", size, pDst, pSrc);
+#endif
+
+ crMemcpy(pDst, pSrc, size);
+
+ /** @todo Restore the protection, probably have to check what was it before us...*/
+ rv = RTMemProtect(alPatch, pDst-alPatch+size, RTMEM_PROT_READ|RTMEM_PROT_EXEC);
+ if (RT_FAILURE(rv))
+ {
+ crError("mprotect2 failed with %x (%s)", rv, psFuncName);
+ }
+}
+
+#define FAKEDRI_JMP64_PATCH_SIZE 13
+
+#if defined(RT_OS_FREEBSD)
+/* Provide basic dladdr1 flags */
+enum {
+ RTLD_DL_SYMENT = 1
+};
+
+/* Provide a minimal local version of dladdr1 */
+static int
+dladdr1(const void *address, Dl_info *dlip, void **info, int flags)
+{
+ static DRI_ELFSYM desym;
+ GElf_Sym sym;
+ GElf_Shdr shdr;
+ Elf *elf;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ int ret, fd, count, i;
+
+ /* Initialize variables */
+ fd = -1;
+ elf = NULL;
+
+ /* Call dladdr first */
+ ret = dladdr(address, dlip);
+ if (ret == 0) goto err_exit;
+
+ /* Check for supported flags */
+ if (flags != RTLD_DL_SYMENT) return 1;
+
+ /* Open shared library's ELF file */
+ if (elf_version(EV_CURRENT) == EV_NONE) goto err_exit;
+ fd = open(dlip->dli_fname, O_RDONLY);
+ if (fd < 0) goto err_exit;
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (elf == NULL) goto err_exit;
+
+ /* Find the '.dynsym' section */
+ scn = elf_nextscn(elf, NULL);
+ while (scn != NULL) {
+ if (gelf_getshdr(scn, &shdr) == NULL) goto err_exit;
+ if (shdr.sh_type == SHT_DYNSYM) break;
+ scn = elf_nextscn(elf, scn);
+ }
+ if (scn == NULL) goto err_exit;
+
+ /* Search for the requested symbol by name and offset */
+ data = elf_getdata(scn, NULL);
+ count = shdr.sh_size / shdr.sh_entsize;
+ for (i = 0; i < count; i++) {
+ gelf_getsym(data, i, &sym);
+ if ((strcmp(dlip->dli_sname,
+ elf_strptr(elf, shdr.sh_link, sym.st_name)) == 0) &&
+ (sym.st_value == (dlip->dli_saddr - dlip->dli_fbase))) {
+ break;
+ }
+ }
+
+ /* Close ELF file */
+ elf_end(elf);
+ close(fd);
+
+ /* Return symbol entry in native format */
+ desym.st_name = sym.st_name;
+ desym.st_info = sym.st_info;
+ desym.st_other = sym.st_other;
+ desym.st_shndx = sym.st_shndx;
+ desym.st_value = sym.st_value;
+ desym.st_size = sym.st_size;
+ *info = &desym;
+ return 1;
+
+ /* Error handler */
+err_exit:
+ if (elf != NULL) elf_end(elf);
+ if (fd >= 0) close(fd);
+ return 0;
+}
+#endif
+
+static void
+vboxPatchMesaExport(const char* psFuncName, const void *pStart, const void *pEnd)
+{
+ Dl_info dlip;
+ DRI_ELFSYM* sym=0;
+ int rv;
+ void *alPatch;
+ void *pMesaEntry;
+ char patch[FAKEDRI_JMP64_PATCH_SIZE];
+ void *shift;
+ int ignore_size=false;
+
+#ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("\nvboxPatchMesaExport: %s", psFuncName);
+#endif
+
+ pMesaEntry = dlsym(RTLD_DEFAULT, psFuncName);
+
+ if (!pMesaEntry)
+ {
+ crDebug("%s not defined in current scope, are we being loaded by mesa's libGL.so?", psFuncName);
+ return;
+ }
+
+ rv = dladdr1(pMesaEntry, &dlip, (void**)&sym, RTLD_DL_SYMENT);
+ if (!rv || !sym)
+ {
+ crError("Failed to get size for %p(%s)", pMesaEntry, psFuncName);
+ return;
+ }
+
+#if VBOX_OGL_GLX_USE_CSTUBS
+ {
+ Dl_info dlip1;
+ DRI_ELFSYM* sym1=0;
+ int rv;
+
+ rv = dladdr1(pStart, &dlip1, (void**)&sym1, RTLD_DL_SYMENT);
+ if (!rv || !sym1)
+ {
+ crError("Failed to get size for vbox %p", pStart);
+ return;
+ }
+
+ pEnd = pStart + sym1->st_size;
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("VBox Entry: %p, start: %p(%s:%s), size: %li", pStart, dlip1.dli_saddr, dlip1.dli_fname, dlip1.dli_sname, sym1->st_size);
+# endif
+ }
+#endif
+
+#ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Mesa Entry: %p, start: %p(%s:%s), size: %li", pMesaEntry, dlip.dli_saddr, dlip.dli_fname, dlip.dli_sname, sym->st_size);
+ crDebug("VBox code: start: %p, end %p, size: %li", pStart, pEnd, pEnd-pStart);
+#endif
+
+#ifndef VBOX_OGL_GLX_USE_CSTUBS
+ if (sym->st_size<(pEnd-pStart))
+#endif
+ {
+#ifdef RT_ARCH_AMD64
+ int64_t offset;
+#endif
+ /* Try to insert 5 bytes jmp/jmpq to our stub code */
+
+ if (sym->st_size<5)
+ {
+ /** @todo we don't really know the size of targeted static function, but it's long enough in practice. We will also patch same place twice, but it's ok.*/
+ if (!crStrcmp(psFuncName, "glXDestroyContext") || !crStrcmp(psFuncName, "glXFreeContextEXT"))
+ {
+ if (((unsigned char*)dlip.dli_saddr)[0]==0xEB)
+ {
+ /*it's a rel8 jmp, so we're going to patch the place it targets instead of jmp itself*/
+ dlip.dli_saddr = (void*) ((intptr_t)dlip.dli_saddr + ((char*)dlip.dli_saddr)[1] + 2);
+ ignore_size = true;
+ }
+ else
+ {
+ crError("Can't patch size is too small.(%s)", psFuncName);
+ return;
+ }
+ }
+ else if (!crStrcmp(psFuncName, "glXCreateGLXPixmapMESA"))
+ {
+ /** @todo it's just a return 0, which we're fine with for now*/
+ return;
+ }
+ else
+ {
+ crError("Can't patch size is too small.(%s)", psFuncName);
+ return;
+ }
+ }
+
+ shift = (void*)((intptr_t)pStart-((intptr_t)dlip.dli_saddr+5));
+#ifdef RT_ARCH_AMD64
+ offset = (intptr_t)shift;
+ if (offset>INT32_MAX || offset<INT32_MIN)
+ {
+ /*try to insert 64bit abs jmp*/
+ if (sym->st_size>=FAKEDRI_JMP64_PATCH_SIZE || ignore_size)
+ {
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Inserting movq/jmp instead");
+# endif
+ /*add 64bit abs jmp*/
+ patch[0] = 0x49; /*movq %r11,imm64*/
+ patch[1] = 0xBB;
+ crMemcpy(&patch[2], &pStart, 8);
+ patch[10] = 0x41; /*jmp *%r11*/
+ patch[11] = 0xFF;
+ patch[12] = 0xE3;
+ pStart = &patch[0];
+ pEnd = &patch[FAKEDRI_JMP64_PATCH_SIZE];
+ }
+ else
+ {
+ FAKEDRI_PatchNode *pNode;
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Can't patch offset is too big. Pushing for 2nd pass(%s)", psFuncName);
+# endif
+ /*Add patch node to repatch with chain jmps in 2nd pass*/
+ pNode = (FAKEDRI_PatchNode *)crAlloc(sizeof(FAKEDRI_PatchNode));
+ if (!pNode)
+ {
+ crError("Not enough memory.");
+ return;
+ }
+ pNode->psFuncName = psFuncName;
+ pNode->pDstStart = dlip.dli_saddr;
+ pNode->pDstEnd = dlip.dli_saddr+sym->st_size;
+ pNode->pSrcStart = pStart;
+ pNode->pSrcEnd = pEnd;
+ pNode->pNext = g_pRepatchList;
+ g_pRepatchList = pNode;
+ return;
+ }
+ }
+ else
+#endif
+ {
+#ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Inserting jmp[q] with shift %p instead", shift);
+#endif
+ patch[0] = 0xE9;
+ crMemcpy(&patch[1], &shift, 4);
+ pStart = &patch[0];
+ pEnd = &patch[5];
+ }
+ }
+
+ vboxApplyPatch(psFuncName, dlip.dli_saddr, pStart, pEnd-pStart);
+
+#ifdef RT_ARCH_AMD64
+ /*Add rest of mesa function body to free list*/
+ if (sym->st_size-(pEnd-pStart)>=FAKEDRI_JMP64_PATCH_SIZE)
+ {
+ FAKEDRI_PatchNode *pNode = (FAKEDRI_PatchNode *)crAlloc(sizeof(FAKEDRI_PatchNode));
+ if (pNode)
+ {
+ pNode->psFuncName = psFuncName;
+ pNode->pDstStart = dlip.dli_saddr+(pEnd-pStart);
+ pNode->pDstEnd = dlip.dli_saddr+sym->st_size;
+ pNode->pSrcStart = dlip.dli_saddr;
+ pNode->pSrcEnd = NULL;
+ pNode->pNext = g_pFreeList;
+ g_pFreeList = pNode;
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Added free node %s, func start=%p, free start=%p, size=%#lx",
+ psFuncName, pNode->pSrcStart, pNode->pDstStart, pNode->pDstEnd-pNode->pDstStart);
+# endif
+ }
+ }
+#endif
+}
+
+#ifdef RT_ARCH_AMD64
+static void
+vboxRepatchMesaExports(void)
+{
+ FAKEDRI_PatchNode *pFreeNode, *pPatchNode;
+ int64_t offset;
+ char patch[FAKEDRI_JMP64_PATCH_SIZE];
+
+ pPatchNode = g_pRepatchList;
+ while (pPatchNode)
+ {
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("\nvboxRepatchMesaExports %s", pPatchNode->psFuncName);
+# endif
+ /*find free place in mesa functions, to place 64bit jump to our stub code*/
+ pFreeNode = g_pFreeList;
+ while (pFreeNode)
+ {
+ if (pFreeNode->pDstEnd-pFreeNode->pDstStart>=FAKEDRI_JMP64_PATCH_SIZE)
+ {
+ offset = ((intptr_t)pFreeNode->pDstStart-((intptr_t)pPatchNode->pDstStart+5));
+ if (offset<=INT32_MAX && offset>=INT32_MIN)
+ {
+ break;
+ }
+ }
+ pFreeNode=pFreeNode->pNext;
+ }
+
+ if (!pFreeNode)
+ {
+ crError("Failed to find free space, to place repatch for %s.", pPatchNode->psFuncName);
+ return;
+ }
+
+ /*add 32bit rel jmp, from mesa orginal function to free space in other mesa function*/
+ patch[0] = 0xE9;
+ crMemcpy(&patch[1], &offset, 4);
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Adding jmp from mesa %s to mesa %s+%#lx", pPatchNode->psFuncName, pFreeNode->psFuncName,
+ pFreeNode->pDstStart-pFreeNode->pSrcStart);
+# endif
+ vboxApplyPatch(pPatchNode->psFuncName, pPatchNode->pDstStart, &patch[0], 5);
+
+ /*add 64bit abs jmp, from free space to our stub code*/
+ patch[0] = 0x49; /*movq %r11,imm64*/
+ patch[1] = 0xBB;
+ crMemcpy(&patch[2], &pPatchNode->pSrcStart, 8);
+ patch[10] = 0x41; /*jmp *%r11*/
+ patch[11] = 0xFF;
+ patch[12] = 0xE3;
+# ifndef VBOX_NO_MESA_PATCH_REPORTS
+ crDebug("Adding jmp from mesa %s+%#lx to vbox %s", pFreeNode->psFuncName, pFreeNode->pDstStart-pFreeNode->pSrcStart,
+ pPatchNode->psFuncName);
+# endif
+ vboxApplyPatch(pFreeNode->psFuncName, pFreeNode->pDstStart, &patch[0], FAKEDRI_JMP64_PATCH_SIZE);
+ /*mark this space as used*/
+ pFreeNode->pDstStart = pFreeNode->pDstStart+FAKEDRI_JMP64_PATCH_SIZE;
+
+ pPatchNode = pPatchNode->pNext;
+ }
+}
+
+static void
+vboxFakeDriFreeList(FAKEDRI_PatchNode *pList)
+{
+ FAKEDRI_PatchNode *pNode;
+
+ while (pList)
+ {
+ pNode=pList;
+ pList=pNode->pNext;
+ crFree(pNode);
+ }
+}
+#endif
+
+#ifdef VBOX_OGL_GLX_USE_CSTUBS
+static void
+# define GLXAPI_ENTRY(Func) vboxPatchMesaExport("glX"#Func, &vbox_glX##Func, NULL);
+vboxPatchMesaExports()
+#else
+static void
+# define GLXAPI_ENTRY(Func) vboxPatchMesaExport("glX"#Func, &vbox_glX##Func, &vbox_glX##Func##_EndProc);
+vboxPatchMesaExports()
+#endif
+{
+ crDebug("Patching mesa glx entries");
+ #include "fakedri_glxfuncsList.h"
+
+#ifdef RT_ARCH_AMD64
+ vboxRepatchMesaExports();
+ vboxFakeDriFreeList(g_pRepatchList);
+ g_pRepatchList = NULL;
+ vboxFakeDriFreeList(g_pFreeList);
+ g_pFreeList = NULL;
+#endif
+}
+#undef GLXAPI_ENTRY
+
+bool vbox_load_sw_dri()
+{
+ const char *libPaths, *p, *next;;
+ char realDriverName[200];
+ void *handle;
+ int len, i;
+
+ /*code from Mesa-7.2/src/glx/x11/dri_common.c:driOpenDriver*/
+
+ libPaths = NULL;
+ if (geteuid() == getuid()) {
+ /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */
+ libPaths = getenv("LIBGL_DRIVERS_PATH");
+ if (!libPaths)
+ libPaths = getenv("LIBGL_DRIVERS_DIR"); /* deprecated */
+ }
+ if (libPaths == NULL)
+ libPaths = DRI_DEFAULT_DRIVER_DIR;
+
+ handle = NULL;
+ for (p = libPaths; *p; p = next)
+ {
+ next = strchr(p, ':');
+ if (next == NULL)
+ {
+ len = strlen(p);
+ next = p + len;
+ }
+ else
+ {
+ len = next - p;
+ next++;
+ }
+
+ snprintf(realDriverName, sizeof realDriverName, "%.*s/%s_dri.so", len, p, "swrast");
+ crDebug("trying %s", realDriverName);
+ handle = dlopen(realDriverName, RTLD_NOW | RTLD_LOCAL);
+ if (handle) break;
+ }
+
+ /*end code*/
+
+ if (handle) gppSwDriExternsion = dlsym(handle, "__driDriverExtensions");
+
+ if (!gppSwDriExternsion)
+ {
+ crDebug("%s doesn't export __driDriverExtensions", realDriverName);
+ return false;
+ }
+ crDebug("loaded %s", realDriverName);
+
+ for (i = 0; gppSwDriExternsion[i]; i++)
+ {
+ if (strcmp(gppSwDriExternsion[i]->name, __DRI_CORE) == 0)
+ gpSwDriCoreExternsion = (__DRIcoreExtension *) gppSwDriExternsion[i];
+ if (strcmp(gppSwDriExternsion[i]->name, __DRI_SWRAST) == 0)
+ gpSwDriSwrastExtension = (__DRIswrastExtension *) gppSwDriExternsion[i];
+ }
+
+ return gpSwDriCoreExternsion && gpSwDriSwrastExtension;
+}
+
+void __attribute__ ((constructor)) vbox_install_into_mesa(void)
+{
+ {
+#ifdef _X_ATTRIBUTE_PRINTF
+ void (*pxf86Msg)(MessageType type, const char *format, ...) _X_ATTRIBUTE_PRINTF(2,3);
+#else
+ void (*pxf86Msg)(MessageType type, const char *format, ...) _printf_attribute(2,3);
+#endif
+
+ pxf86Msg = dlsym(RTLD_DEFAULT, "xf86Msg");
+ if (pxf86Msg)
+ {
+ pxf86Msg(X_INFO, "Next line is added to allow vboxvideo_drv.so to appear as whitelisted driver\n");
+ pxf86Msg(X_INFO, "The file referenced, is *NOT* loaded\n");
+ pxf86Msg(X_INFO, "Loading %s/ati_drv.so\n", DRI_XORG_DRV_DIR);
+
+ /* we're failing to proxy software dri driver calls for certain xservers, so just make sure we're unloaded for now */
+ __driDriverExtensions[0] = NULL;
+ return;
+ }
+ }
+
+ if (!stubInit())
+ {
+ crDebug("vboxdriInitScreen: stubInit failed");
+ return;
+ }
+
+ /* Load swrast_dri.so to proxy dri related calls there. */
+ if (!vbox_load_sw_dri())
+ {
+ crDebug("vboxdriInitScreen: vbox_load_sw_dri failed...going to fail badly");
+ return;
+ }
+
+ /* Handle gl api.
+ * In the end application call would look like this:
+ * app call glFoo->(mesa asm dispatch stub)->cr_glFoo(vbox asm dispatch stub)->SPU Foo function(packspuFoo or alike)
+ * Note, we don't need to install extension functions via _glapi_add_dispatch, because we'd override glXGetProcAddress.
+ */
+ /* Mesa's dispatch table is different across library versions, have to modify mesa's table using offset info functions*/
+ vboxPatchMesaGLAPITable();
+
+ /* Handle glx api.
+ * In the end application call would look like this:
+ * app call glxFoo->(mesa asm dispatch stub patched with vbox_glXFoo:jmp glxim[Foo's index])->VBOXGLXTAG(glxFoo)
+ */
+ /* Fill structure used by our assembly stubs */
+ vboxFillGLXAPITable(&glxim);
+ /* Now patch functions exported by libGL.so */
+ vboxPatchMesaExports();
+}
+
+/*
+ * @todo we're missing first glx related call from the client application.
+ * Luckily, this doesn't add much problems, except for some cases.
+ */
+
+/* __DRIcoreExtension */
+
+static __DRIscreen *
+vboxdriCreateNewScreen(int screen, int fd, unsigned int sarea_handle,
+ const __DRIextension **extensions, const __DRIconfig ***driverConfigs,
+ void *loaderPrivate)
+{
+ (void) fd;
+ (void) sarea_handle;
+ SWDRI_SAFERET_SWRAST(createNewScreen, screen, extensions, driverConfigs, loaderPrivate);
+}
+
+static void
+vboxdriDestroyScreen(__DRIscreen *screen)
+{
+ SWDRI_SAFECALL_CORE(destroyScreen, screen);
+}
+
+static const __DRIextension **
+vboxdriGetExtensions(__DRIscreen *screen)
+{
+ SWDRI_SAFERET_CORE(getExtensions, screen);
+}
+
+static int
+vboxdriGetConfigAttrib(const __DRIconfig *config,
+ unsigned int attrib,
+ unsigned int *value)
+{
+ SWDRI_SAFERET_CORE(getConfigAttrib, config, attrib, value);
+}
+
+static int
+vboxdriIndexConfigAttrib(const __DRIconfig *config, int index,
+ unsigned int *attrib, unsigned int *value)
+{
+ SWDRI_SAFERET_CORE(indexConfigAttrib, config, index, attrib, value);
+}
+
+static __DRIdrawable *
+vboxdriCreateNewDrawable(__DRIscreen *screen,
+ const __DRIconfig *config,
+ unsigned int drawable_id,
+ unsigned int head,
+ void *loaderPrivate)
+{
+ (void) drawable_id;
+ (void) head;
+ SWDRI_SAFERET_SWRAST(createNewDrawable, screen, config, loaderPrivate);
+}
+
+static void
+vboxdriDestroyDrawable(__DRIdrawable *drawable)
+{
+ SWDRI_SAFECALL_CORE(destroyDrawable, drawable);
+}
+
+static void
+vboxdriSwapBuffers(__DRIdrawable *drawable)
+{
+ SWDRI_SAFECALL_CORE(swapBuffers, drawable);
+}
+
+static __DRIcontext *
+vboxdriCreateNewContext(__DRIscreen *screen,
+ const __DRIconfig *config,
+ __DRIcontext *shared,
+ void *loaderPrivate)
+{
+ SWDRI_SAFERET_CORE(createNewContext, screen, config, shared, loaderPrivate);
+}
+
+static int
+vboxdriCopyContext(__DRIcontext *dest,
+ __DRIcontext *src,
+ unsigned long mask)
+{
+ SWDRI_SAFERET_CORE(copyContext, dest, src, mask);
+}
+
+static void
+vboxdriDestroyContext(__DRIcontext *context)
+{
+ SWDRI_SAFECALL_CORE(destroyContext, context);
+}
+
+static int
+vboxdriBindContext(__DRIcontext *ctx,
+ __DRIdrawable *pdraw,
+ __DRIdrawable *pread)
+{
+ SWDRI_SAFERET_CORE(bindContext, ctx, pdraw, pread);
+}
+
+static int
+vboxdriUnbindContext(__DRIcontext *ctx)
+{
+ SWDRI_SAFERET_CORE(unbindContext, ctx)
+}
+
+/* __DRIlegacyExtension */
+
+static __DRIscreen *
+vboxdriCreateNewScreen_Legacy(int scrn,
+ const __DRIversion *ddx_version,
+ const __DRIversion *dri_version,
+ const __DRIversion *drm_version,
+ const __DRIframebuffer *frame_buffer,
+ drmAddress pSAREA, int fd,
+ const __DRIextension **extensions,
+ const __DRIconfig ***driver_modes,
+ void *loaderPrivate)
+{
+ (void) ddx_version;
+ (void) dri_version;
+ (void) frame_buffer;
+ (void) pSAREA;
+ (void) fd;
+ SWDRI_SAFERET_SWRAST(createNewScreen, scrn, extensions, driver_modes, loaderPrivate);
+}
+
+static __DRIdrawable *
+vboxdriCreateNewDrawable_Legacy(__DRIscreen *psp, const __DRIconfig *config,
+ drm_drawable_t hwDrawable, int renderType,
+ const int *attrs, void *data)
+{
+ (void) hwDrawable;
+ (void) renderType;
+ (void) attrs;
+ (void) data;
+ SWDRI_SAFERET_SWRAST(createNewDrawable, psp, config, data);
+}
+
+static __DRIcontext *
+vboxdriCreateNewContext_Legacy(__DRIscreen *psp, const __DRIconfig *config,
+ int render_type, __DRIcontext *shared,
+ drm_context_t hwContext, void *data)
+{
+ (void) render_type;
+ (void) hwContext;
+ return vboxdriCreateNewContext(psp, config, shared, data);
+}
+
+
+static const __DRIlegacyExtension vboxdriLegacyExtension = {
+ { __DRI_LEGACY, __DRI_LEGACY_VERSION },
+ vboxdriCreateNewScreen_Legacy,
+ vboxdriCreateNewDrawable_Legacy,
+ vboxdriCreateNewContext_Legacy
+};
+
+static const __DRIcoreExtension vboxdriCoreExtension = {
+ { __DRI_CORE, __DRI_CORE_VERSION },
+ vboxdriCreateNewScreen, /* driCreateNewScreen */
+ vboxdriDestroyScreen,
+ vboxdriGetExtensions,
+ vboxdriGetConfigAttrib,
+ vboxdriIndexConfigAttrib,
+ vboxdriCreateNewDrawable, /* driCreateNewDrawable */
+ vboxdriDestroyDrawable,
+ vboxdriSwapBuffers,
+ vboxdriCreateNewContext,
+ vboxdriCopyContext,
+ vboxdriDestroyContext,
+ vboxdriBindContext,
+ vboxdriUnbindContext
+};
+
+/* This structure is used by dri_util from mesa, don't rename it! */
+DECLEXPORT(const __DRIextension *) __driDriverExtensions[] = {
+ &vboxdriLegacyExtension.base,
+ &vboxdriCoreExtension.base,
+ NULL
+};
diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_drv.h b/src/VBox/Additions/common/crOpenGL/fakedri_drv.h
new file mode 100644
index 00000000..5f77bd85
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/fakedri_drv.h
@@ -0,0 +1,123 @@
+/* $Id: fakedri_drv.h $ */
+/** @file
+ * VirtualBox guest OpenGL DRI header
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_fakedri_drv_h
+#define GA_INCLUDED_SRC_common_crOpenGL_fakedri_drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "src/mesa/main/mtypes.h"
+#include "src/mesa/main/dd.h"
+#include "src/mesa/glapi/dispatch.h"
+#include "src/mesa/glapi/glapi.h"
+#include "src/mesa/glapi/glapitable.h"
+#include "src/mesa/glapi/glapioffsets.h"
+#include "src/mesa/drivers/dri/common/dri_util.h"
+#include "GL/internal/dri_interface.h"
+
+#include "glx_proto.h"
+
+#ifdef VBOX_OGL_GLX_USE_CSTUBS
+# include "dri_glx.h"
+#endif
+
+typedef struct _vbox_glxapi_table
+{
+ #define GLXAPI_ENTRY(Func) PGLXFUNC_##Func Func;
+ #include "fakedri_glxfuncsList.h"
+ #undef GLXAPI_ENTRY
+} fakedri_glxapi_table;
+
+extern fakedri_glxapi_table glxim;
+
+#ifdef VBOX_OGL_GLX_USE_CSTUBS
+/* Extern declarations for our C stubs */
+extern void VBOXGLXENTRYTAG(glXFreeMemoryMESA)(Display *dpy, int scrn, void *pointer) ;
+extern GLXContext VBOXGLXENTRYTAG(glXImportContextEXT)(Display *dpy, GLXContextID contextID) ;
+extern GLXContextID VBOXGLXENTRYTAG(glXGetContextIDEXT)(const GLXContext ctx) ;
+extern Bool VBOXGLXENTRYTAG(glXMakeCurrentReadSGI)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx) ;
+extern Display * VBOXGLXENTRYTAG(glXGetCurrentDisplayEXT)(void) ;
+extern void VBOXGLXENTRYTAG(glXFreeContextEXT)(Display *dpy, GLXContext ctx) ;
+extern int VBOXGLXENTRYTAG(glXQueryContextInfoEXT)(Display *dpy, GLXContext ctx) ;
+extern void * VBOXGLXENTRYTAG(glXAllocateMemoryMESA)(Display *dpy, int scrn,
+ size_t size, float readFreq,
+ float writeFreq, float priority);
+extern GLuint VBOXGLXENTRYTAG(glXGetMemoryOffsetMESA)(Display *dpy, int scrn, const void *pointer ) ;
+extern GLXPixmap VBOXGLXENTRYTAG(glXCreateGLXPixmapMESA)(Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap) ;
+extern void VBOXGLXENTRYTAG(glXCopyContext)( Display *dpy, GLXContext src, GLXContext dst, unsigned long mask);
+extern void VBOXGLXENTRYTAG(glXUseXFont)(Font font, int first, int count, int listBase) ;
+extern CR_GLXFuncPtr VBOXGLXENTRYTAG(glXGetProcAddress)(const GLubyte *name) ;
+extern Bool VBOXGLXENTRYTAG(glXQueryExtension)(Display *dpy, int *errorBase, int *eventBase) ;
+extern Bool VBOXGLXENTRYTAG(glXIsDirect)(Display *dpy, GLXContext ctx) ;
+extern GLXPixmap VBOXGLXENTRYTAG(glXCreateGLXPixmap)(Display *dpy, XVisualInfo *vis, Pixmap pixmap) ;
+extern void VBOXGLXENTRYTAG(glXSwapBuffers)(Display *dpy, GLXDrawable drawable) ;
+extern GLXDrawable VBOXGLXENTRYTAG(glXGetCurrentDrawable)(void) ;
+extern void VBOXGLXENTRYTAG(glXWaitGL)(void) ;
+extern Display * VBOXGLXENTRYTAG(glXGetCurrentDisplay)(void) ;
+extern const char * VBOXGLXENTRYTAG(glXQueryServerString)(Display *dpy, int screen, int name) ;
+extern GLXContext VBOXGLXENTRYTAG(glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, Bool direct) ;
+extern int VBOXGLXENTRYTAG(glXGetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int *value) ;
+extern void VBOXGLXENTRYTAG(glXWaitX)(void) ;
+extern GLXContext VBOXGLXENTRYTAG(glXGetCurrentContext)(void) ;
+extern const char * VBOXGLXENTRYTAG(glXGetClientString)(Display *dpy, int name) ;
+extern Bool VBOXGLXENTRYTAG(glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx) ;
+extern void VBOXGLXENTRYTAG(glXDestroyContext)(Display *dpy, GLXContext ctx) ;
+extern CR_GLXFuncPtr VBOXGLXENTRYTAG(glXGetProcAddressARB)(const GLubyte *name) ;
+extern void VBOXGLXENTRYTAG(glXDestroyGLXPixmap)(Display *dpy, GLXPixmap pix) ;
+extern Bool VBOXGLXENTRYTAG(glXQueryVersion)(Display *dpy, int *major, int *minor) ;
+extern XVisualInfo * VBOXGLXENTRYTAG(glXChooseVisual)(Display *dpy, int screen, int *attribList) ;
+extern const char * VBOXGLXENTRYTAG(glXQueryExtensionsString)(Display *dpy, int screen) ;
+extern GLXPbufferSGIX VBOXGLXENTRYTAG(glXCreateGLXPbufferSGIX)(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
+extern int VBOXGLXENTRYTAG(glXQueryGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf, int attribute, unsigned int *value);
+extern GLXFBConfigSGIX * VBOXGLXENTRYTAG(glXChooseFBConfigSGIX)(Display *dpy, int screen, int *attrib_list, int *nelements);
+extern void VBOXGLXENTRYTAG(glXDestroyGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf) ;
+extern void VBOXGLXENTRYTAG(glXSelectEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long mask) ;
+extern void VBOXGLXENTRYTAG(glXGetSelectedEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long *mask) ;
+extern GLXFBConfigSGIX VBOXGLXENTRYTAG(glXGetFBConfigFromVisualSGIX)(Display *dpy, XVisualInfo *vis) ;
+extern XVisualInfo * VBOXGLXENTRYTAG(glXGetVisualFromFBConfigSGIX)(Display *dpy, GLXFBConfig config) ;
+extern GLXContext VBOXGLXENTRYTAG(glXCreateContextWithConfigSGIX)(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+extern GLXPixmap VBOXGLXENTRYTAG(glXCreateGLXPixmapWithConfigSGIX)(Display *dpy, GLXFBConfig config, Pixmap pixmap) ;
+extern int VBOXGLXENTRYTAG(glXGetFBConfigAttribSGIX)(Display *dpy, GLXFBConfig config, int attribute, int *value) ;
+extern GLXFBConfig * VBOXGLXENTRYTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list, int *nelements) ;
+extern GLXPbuffer VBOXGLXENTRYTAG(glXCreatePbuffer)(Display *dpy, GLXFBConfig config, ATTRIB_TYPE *attrib_list) ;
+extern GLXPixmap VBOXGLXENTRYTAG(glXCreatePixmap)(Display *dpy, GLXFBConfig config, Pixmap pixmap, const ATTRIB_TYPE *attrib_list) ;
+extern GLXWindow VBOXGLXENTRYTAG(glXCreateWindow)(Display *dpy, GLXFBConfig config, Window win, ATTRIB_TYPE *attrib_list) ;
+extern GLXContext VBOXGLXENTRYTAG(glXCreateNewContext)(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+extern void VBOXGLXENTRYTAG(glXDestroyPbuffer)(Display *dpy, GLXPbuffer pbuf) ;
+extern void VBOXGLXENTRYTAG(glXDestroyPixmap)(Display *dpy, GLXPixmap pixmap) ;
+extern void VBOXGLXENTRYTAG(glXDestroyWindow)(Display *dpy, GLXWindow win) ;
+extern GLXDrawable VBOXGLXENTRYTAG(glXGetCurrentReadDrawable)(void) ;
+extern int VBOXGLXENTRYTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int *value) ;
+extern GLXFBConfig * VBOXGLXENTRYTAG(glXGetFBConfigs)(Display *dpy, int screen, int *nelements) ;
+extern void VBOXGLXENTRYTAG(glXGetSelectedEvent)(Display *dpy, GLXDrawable draw, unsigned long *event_mask) ;
+extern XVisualInfo * VBOXGLXENTRYTAG(glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config) ;
+extern Bool VBOXGLXENTRYTAG(glXMakeContextCurrent)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx) ;
+extern int VBOXGLXENTRYTAG(glXQueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value) ;
+extern void VBOXGLXENTRYTAG(glXQueryDrawable)(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value) ;
+extern void VBOXGLXENTRYTAG(glXSelectEvent)(Display *dpy, GLXDrawable draw, unsigned long event_mask) ;
+#else
+/* Extern declarations for our asm stubs */
+# define GLXAPI_ENTRY(Func) \
+ extern void vbox_glX##Func;\
+ extern void vbox_glX##Func##_EndProc;
+# include "fakedri_glxfuncsList.h"
+# undef GLXAPI_ENTRY
+#endif
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_fakedri_drv_h */
+
diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h b/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h
new file mode 100644
index 00000000..6928a2ab
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h
@@ -0,0 +1,730 @@
+/* $Id: fakedri_glfuncsList.h $ */
+/** @file
+ * VBox OpenGL list of opengl functions common in Mesa and vbox opengl stub
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GLAPI_ENTRY
+#error GLAPI_ENTRY should be defined.
+#endif
+
+GLAPI_ENTRY(NewList)
+GLAPI_ENTRY(EndList)
+GLAPI_ENTRY(CallList)
+GLAPI_ENTRY(CallLists)
+GLAPI_ENTRY(DeleteLists)
+GLAPI_ENTRY(GenLists)
+GLAPI_ENTRY(ListBase)
+GLAPI_ENTRY(Begin)
+GLAPI_ENTRY(Bitmap)
+GLAPI_ENTRY(Color3b)
+GLAPI_ENTRY(Color3bv)
+GLAPI_ENTRY(Color3d)
+GLAPI_ENTRY(Color3dv)
+GLAPI_ENTRY(Color3f)
+GLAPI_ENTRY(Color3fv)
+GLAPI_ENTRY(Color3i)
+GLAPI_ENTRY(Color3iv)
+GLAPI_ENTRY(Color3s)
+GLAPI_ENTRY(Color3sv)
+GLAPI_ENTRY(Color3ub)
+GLAPI_ENTRY(Color3ubv)
+GLAPI_ENTRY(Color3ui)
+GLAPI_ENTRY(Color3uiv)
+GLAPI_ENTRY(Color3us)
+GLAPI_ENTRY(Color3usv)
+GLAPI_ENTRY(Color4b)
+GLAPI_ENTRY(Color4bv)
+GLAPI_ENTRY(Color4d)
+GLAPI_ENTRY(Color4dv)
+GLAPI_ENTRY(Color4f)
+GLAPI_ENTRY(Color4fv)
+GLAPI_ENTRY(Color4i)
+GLAPI_ENTRY(Color4iv)
+GLAPI_ENTRY(Color4s)
+GLAPI_ENTRY(Color4sv)
+GLAPI_ENTRY(Color4ub)
+GLAPI_ENTRY(Color4ubv)
+GLAPI_ENTRY(Color4ui)
+GLAPI_ENTRY(Color4uiv)
+GLAPI_ENTRY(Color4us)
+GLAPI_ENTRY(Color4usv)
+GLAPI_ENTRY(EdgeFlag)
+GLAPI_ENTRY(EdgeFlagv)
+GLAPI_ENTRY(End)
+GLAPI_ENTRY(Indexd)
+GLAPI_ENTRY(Indexdv)
+GLAPI_ENTRY(Indexf)
+GLAPI_ENTRY(Indexfv)
+GLAPI_ENTRY(Indexi)
+GLAPI_ENTRY(Indexiv)
+GLAPI_ENTRY(Indexs)
+GLAPI_ENTRY(Indexsv)
+GLAPI_ENTRY(Normal3b)
+GLAPI_ENTRY(Normal3bv)
+GLAPI_ENTRY(Normal3d)
+GLAPI_ENTRY(Normal3dv)
+GLAPI_ENTRY(Normal3f)
+GLAPI_ENTRY(Normal3fv)
+GLAPI_ENTRY(Normal3i)
+GLAPI_ENTRY(Normal3iv)
+GLAPI_ENTRY(Normal3s)
+GLAPI_ENTRY(Normal3sv)
+GLAPI_ENTRY(RasterPos2d)
+GLAPI_ENTRY(RasterPos2dv)
+GLAPI_ENTRY(RasterPos2f)
+GLAPI_ENTRY(RasterPos2fv)
+GLAPI_ENTRY(RasterPos2i)
+GLAPI_ENTRY(RasterPos2iv)
+GLAPI_ENTRY(RasterPos2s)
+GLAPI_ENTRY(RasterPos2sv)
+GLAPI_ENTRY(RasterPos3d)
+GLAPI_ENTRY(RasterPos3dv)
+GLAPI_ENTRY(RasterPos3f)
+GLAPI_ENTRY(RasterPos3fv)
+GLAPI_ENTRY(RasterPos3i)
+GLAPI_ENTRY(RasterPos3iv)
+GLAPI_ENTRY(RasterPos3s)
+GLAPI_ENTRY(RasterPos3sv)
+GLAPI_ENTRY(RasterPos4d)
+GLAPI_ENTRY(RasterPos4dv)
+GLAPI_ENTRY(RasterPos4f)
+GLAPI_ENTRY(RasterPos4fv)
+GLAPI_ENTRY(RasterPos4i)
+GLAPI_ENTRY(RasterPos4iv)
+GLAPI_ENTRY(RasterPos4s)
+GLAPI_ENTRY(RasterPos4sv)
+GLAPI_ENTRY(Rectd)
+GLAPI_ENTRY(Rectdv)
+GLAPI_ENTRY(Rectf)
+GLAPI_ENTRY(Rectfv)
+GLAPI_ENTRY(Recti)
+GLAPI_ENTRY(Rectiv)
+GLAPI_ENTRY(Rects)
+GLAPI_ENTRY(Rectsv)
+GLAPI_ENTRY(TexCoord1d)
+GLAPI_ENTRY(TexCoord1dv)
+GLAPI_ENTRY(TexCoord1f)
+GLAPI_ENTRY(TexCoord1fv)
+GLAPI_ENTRY(TexCoord1i)
+GLAPI_ENTRY(TexCoord1iv)
+GLAPI_ENTRY(TexCoord1s)
+GLAPI_ENTRY(TexCoord1sv)
+GLAPI_ENTRY(TexCoord2d)
+GLAPI_ENTRY(TexCoord2dv)
+GLAPI_ENTRY(TexCoord2f)
+GLAPI_ENTRY(TexCoord2fv)
+GLAPI_ENTRY(TexCoord2i)
+GLAPI_ENTRY(TexCoord2iv)
+GLAPI_ENTRY(TexCoord2s)
+GLAPI_ENTRY(TexCoord2sv)
+GLAPI_ENTRY(TexCoord3d)
+GLAPI_ENTRY(TexCoord3dv)
+GLAPI_ENTRY(TexCoord3f)
+GLAPI_ENTRY(TexCoord3fv)
+GLAPI_ENTRY(TexCoord3i)
+GLAPI_ENTRY(TexCoord3iv)
+GLAPI_ENTRY(TexCoord3s)
+GLAPI_ENTRY(TexCoord3sv)
+GLAPI_ENTRY(TexCoord4d)
+GLAPI_ENTRY(TexCoord4dv)
+GLAPI_ENTRY(TexCoord4f)
+GLAPI_ENTRY(TexCoord4fv)
+GLAPI_ENTRY(TexCoord4i)
+GLAPI_ENTRY(TexCoord4iv)
+GLAPI_ENTRY(TexCoord4s)
+GLAPI_ENTRY(TexCoord4sv)
+GLAPI_ENTRY(Vertex2d)
+GLAPI_ENTRY(Vertex2dv)
+GLAPI_ENTRY(Vertex2f)
+GLAPI_ENTRY(Vertex2fv)
+GLAPI_ENTRY(Vertex2i)
+GLAPI_ENTRY(Vertex2iv)
+GLAPI_ENTRY(Vertex2s)
+GLAPI_ENTRY(Vertex2sv)
+GLAPI_ENTRY(Vertex3d)
+GLAPI_ENTRY(Vertex3dv)
+GLAPI_ENTRY(Vertex3f)
+GLAPI_ENTRY(Vertex3fv)
+GLAPI_ENTRY(Vertex3i)
+GLAPI_ENTRY(Vertex3iv)
+GLAPI_ENTRY(Vertex3s)
+GLAPI_ENTRY(Vertex3sv)
+GLAPI_ENTRY(Vertex4d)
+GLAPI_ENTRY(Vertex4dv)
+GLAPI_ENTRY(Vertex4f)
+GLAPI_ENTRY(Vertex4fv)
+GLAPI_ENTRY(Vertex4i)
+GLAPI_ENTRY(Vertex4iv)
+GLAPI_ENTRY(Vertex4s)
+GLAPI_ENTRY(Vertex4sv)
+GLAPI_ENTRY(ClipPlane)
+GLAPI_ENTRY(ColorMaterial)
+GLAPI_ENTRY(CullFace)
+GLAPI_ENTRY(Fogf)
+GLAPI_ENTRY(Fogfv)
+GLAPI_ENTRY(Fogi)
+GLAPI_ENTRY(Fogiv)
+GLAPI_ENTRY(FrontFace)
+GLAPI_ENTRY(Hint)
+GLAPI_ENTRY(Lightf)
+GLAPI_ENTRY(Lightfv)
+GLAPI_ENTRY(Lighti)
+GLAPI_ENTRY(Lightiv)
+GLAPI_ENTRY(LightModelf)
+GLAPI_ENTRY(LightModelfv)
+GLAPI_ENTRY(LightModeli)
+GLAPI_ENTRY(LightModeliv)
+GLAPI_ENTRY(LineStipple)
+GLAPI_ENTRY(LineWidth)
+GLAPI_ENTRY(Materialf)
+GLAPI_ENTRY(Materialfv)
+GLAPI_ENTRY(Materiali)
+GLAPI_ENTRY(Materialiv)
+GLAPI_ENTRY(PointSize)
+GLAPI_ENTRY(PolygonMode)
+GLAPI_ENTRY(PolygonStipple)
+GLAPI_ENTRY(Scissor)
+GLAPI_ENTRY(ShadeModel)
+GLAPI_ENTRY(TexParameterf)
+GLAPI_ENTRY(TexParameterfv)
+GLAPI_ENTRY(TexParameteri)
+GLAPI_ENTRY(TexParameteriv)
+GLAPI_ENTRY(TexImage1D)
+GLAPI_ENTRY(TexImage2D)
+GLAPI_ENTRY(TexEnvf)
+GLAPI_ENTRY(TexEnvfv)
+GLAPI_ENTRY(TexEnvi)
+GLAPI_ENTRY(TexEnviv)
+GLAPI_ENTRY(TexGend)
+GLAPI_ENTRY(TexGendv)
+GLAPI_ENTRY(TexGenf)
+GLAPI_ENTRY(TexGenfv)
+GLAPI_ENTRY(TexGeni)
+GLAPI_ENTRY(TexGeniv)
+GLAPI_ENTRY(FeedbackBuffer)
+GLAPI_ENTRY(SelectBuffer)
+GLAPI_ENTRY(RenderMode)
+GLAPI_ENTRY(InitNames)
+GLAPI_ENTRY(LoadName)
+GLAPI_ENTRY(PassThrough)
+GLAPI_ENTRY(PopName)
+GLAPI_ENTRY(PushName)
+GLAPI_ENTRY(DrawBuffer)
+GLAPI_ENTRY(Clear)
+GLAPI_ENTRY(ClearAccum)
+GLAPI_ENTRY(ClearIndex)
+GLAPI_ENTRY(ClearColor)
+GLAPI_ENTRY(ClearStencil)
+GLAPI_ENTRY(ClearDepth)
+GLAPI_ENTRY(StencilMask)
+GLAPI_ENTRY(ColorMask)
+GLAPI_ENTRY(DepthMask)
+GLAPI_ENTRY(IndexMask)
+GLAPI_ENTRY(Accum)
+GLAPI_ENTRY(Disable)
+GLAPI_ENTRY(Enable)
+GLAPI_ENTRY(Finish)
+GLAPI_ENTRY(Flush)
+GLAPI_ENTRY(PopAttrib)
+GLAPI_ENTRY(PushAttrib)
+GLAPI_ENTRY(Map1d)
+GLAPI_ENTRY(Map1f)
+GLAPI_ENTRY(Map2d)
+GLAPI_ENTRY(Map2f)
+GLAPI_ENTRY(MapGrid1d)
+GLAPI_ENTRY(MapGrid1f)
+GLAPI_ENTRY(MapGrid2d)
+GLAPI_ENTRY(MapGrid2f)
+GLAPI_ENTRY(EvalCoord1d)
+GLAPI_ENTRY(EvalCoord1dv)
+GLAPI_ENTRY(EvalCoord1f)
+GLAPI_ENTRY(EvalCoord1fv)
+GLAPI_ENTRY(EvalCoord2d)
+GLAPI_ENTRY(EvalCoord2dv)
+GLAPI_ENTRY(EvalCoord2f)
+GLAPI_ENTRY(EvalCoord2fv)
+GLAPI_ENTRY(EvalMesh1)
+GLAPI_ENTRY(EvalPoint1)
+GLAPI_ENTRY(EvalMesh2)
+GLAPI_ENTRY(EvalPoint2)
+GLAPI_ENTRY(AlphaFunc)
+GLAPI_ENTRY(BlendFunc)
+GLAPI_ENTRY(LogicOp)
+GLAPI_ENTRY(StencilFunc)
+GLAPI_ENTRY(StencilOp)
+GLAPI_ENTRY(DepthFunc)
+GLAPI_ENTRY(PixelZoom)
+GLAPI_ENTRY(PixelTransferf)
+GLAPI_ENTRY(PixelTransferi)
+GLAPI_ENTRY(PixelStoref)
+GLAPI_ENTRY(PixelStorei)
+GLAPI_ENTRY(PixelMapfv)
+GLAPI_ENTRY(PixelMapuiv)
+GLAPI_ENTRY(PixelMapusv)
+GLAPI_ENTRY(ReadBuffer)
+GLAPI_ENTRY(CopyPixels)
+GLAPI_ENTRY(ReadPixels)
+GLAPI_ENTRY(DrawPixels)
+GLAPI_ENTRY(GetBooleanv)
+GLAPI_ENTRY(GetClipPlane)
+GLAPI_ENTRY(GetDoublev)
+GLAPI_ENTRY(GetError)
+GLAPI_ENTRY(GetFloatv)
+GLAPI_ENTRY(GetIntegerv)
+GLAPI_ENTRY(GetLightfv)
+GLAPI_ENTRY(GetLightiv)
+GLAPI_ENTRY(GetMapdv)
+GLAPI_ENTRY(GetMapfv)
+GLAPI_ENTRY(GetMapiv)
+GLAPI_ENTRY(GetMaterialfv)
+GLAPI_ENTRY(GetMaterialiv)
+GLAPI_ENTRY(GetPixelMapfv)
+GLAPI_ENTRY(GetPixelMapuiv)
+GLAPI_ENTRY(GetPixelMapusv)
+GLAPI_ENTRY(GetPolygonStipple)
+GLAPI_ENTRY(GetString)
+GLAPI_ENTRY(GetTexEnvfv)
+GLAPI_ENTRY(GetTexEnviv)
+GLAPI_ENTRY(GetTexGendv)
+GLAPI_ENTRY(GetTexGenfv)
+GLAPI_ENTRY(GetTexGeniv)
+GLAPI_ENTRY(GetTexImage)
+GLAPI_ENTRY(GetTexParameterfv)
+GLAPI_ENTRY(GetTexParameteriv)
+GLAPI_ENTRY(GetTexLevelParameterfv)
+GLAPI_ENTRY(GetTexLevelParameteriv)
+GLAPI_ENTRY(IsEnabled)
+GLAPI_ENTRY(IsList)
+GLAPI_ENTRY(DepthRange)
+GLAPI_ENTRY(Frustum)
+GLAPI_ENTRY(LoadIdentity)
+GLAPI_ENTRY(LoadMatrixf)
+GLAPI_ENTRY(LoadMatrixd)
+GLAPI_ENTRY(MatrixMode)
+GLAPI_ENTRY(MultMatrixf)
+GLAPI_ENTRY(MultMatrixd)
+GLAPI_ENTRY(Ortho)
+GLAPI_ENTRY(PopMatrix)
+GLAPI_ENTRY(PushMatrix)
+GLAPI_ENTRY(Rotated)
+GLAPI_ENTRY(Rotatef)
+GLAPI_ENTRY(Scaled)
+GLAPI_ENTRY(Scalef)
+GLAPI_ENTRY(Translated)
+GLAPI_ENTRY(Translatef)
+GLAPI_ENTRY(Viewport)
+GLAPI_ENTRY(ArrayElement)
+GLAPI_ENTRY(BindTexture)
+GLAPI_ENTRY(ColorPointer)
+GLAPI_ENTRY(DisableClientState)
+GLAPI_ENTRY(DrawArrays)
+GLAPI_ENTRY(DrawElements)
+GLAPI_ENTRY(EdgeFlagPointer)
+GLAPI_ENTRY(EnableClientState)
+GLAPI_ENTRY(IndexPointer)
+GLAPI_ENTRY(Indexub)
+GLAPI_ENTRY(Indexubv)
+GLAPI_ENTRY(InterleavedArrays)
+GLAPI_ENTRY(NormalPointer)
+GLAPI_ENTRY(PolygonOffset)
+GLAPI_ENTRY(TexCoordPointer)
+GLAPI_ENTRY(VertexPointer)
+GLAPI_ENTRY(AreTexturesResident)
+GLAPI_ENTRY(CopyTexImage1D)
+GLAPI_ENTRY(CopyTexImage2D)
+GLAPI_ENTRY(CopyTexSubImage1D)
+GLAPI_ENTRY(CopyTexSubImage2D)
+GLAPI_ENTRY(DeleteTextures)
+GLAPI_ENTRY(GenTextures)
+GLAPI_ENTRY(GetPointerv)
+GLAPI_ENTRY(IsTexture)
+GLAPI_ENTRY(PrioritizeTextures)
+GLAPI_ENTRY(TexSubImage1D)
+GLAPI_ENTRY(TexSubImage2D)
+GLAPI_ENTRY(PopClientAttrib)
+GLAPI_ENTRY(PushClientAttrib)
+GLAPI_ENTRY(BlendColor)
+GLAPI_ENTRY(BlendEquation)
+GLAPI_ENTRY(DrawRangeElements)
+GLAPI_ENTRY(ColorTable)
+GLAPI_ENTRY(ColorTableParameterfv)
+GLAPI_ENTRY(ColorTableParameteriv)
+GLAPI_ENTRY(CopyColorTable)
+GLAPI_ENTRY(GetColorTable)
+GLAPI_ENTRY(GetColorTableParameterfv)
+GLAPI_ENTRY(GetColorTableParameteriv)
+GLAPI_ENTRY(ColorSubTable)
+GLAPI_ENTRY(CopyColorSubTable)
+GLAPI_ENTRY(ConvolutionFilter1D)
+GLAPI_ENTRY(ConvolutionFilter2D)
+GLAPI_ENTRY(ConvolutionParameterf)
+GLAPI_ENTRY(ConvolutionParameterfv)
+GLAPI_ENTRY(ConvolutionParameteri)
+GLAPI_ENTRY(ConvolutionParameteriv)
+GLAPI_ENTRY(CopyConvolutionFilter1D)
+GLAPI_ENTRY(CopyConvolutionFilter2D)
+GLAPI_ENTRY(GetConvolutionFilter)
+GLAPI_ENTRY(GetConvolutionParameterfv)
+GLAPI_ENTRY(GetConvolutionParameteriv)
+GLAPI_ENTRY(GetSeparableFilter)
+GLAPI_ENTRY(SeparableFilter2D)
+GLAPI_ENTRY(GetHistogram)
+GLAPI_ENTRY(GetHistogramParameterfv)
+GLAPI_ENTRY(GetHistogramParameteriv)
+GLAPI_ENTRY(GetMinmax)
+GLAPI_ENTRY(GetMinmaxParameterfv)
+GLAPI_ENTRY(GetMinmaxParameteriv)
+GLAPI_ENTRY(Histogram)
+GLAPI_ENTRY(Minmax)
+GLAPI_ENTRY(ResetHistogram)
+GLAPI_ENTRY(ResetMinmax)
+GLAPI_ENTRY(TexImage3D)
+GLAPI_ENTRY(TexSubImage3D)
+GLAPI_ENTRY(CopyTexSubImage3D)
+GLAPI_ENTRY(ActiveTextureARB)
+GLAPI_ENTRY(ClientActiveTextureARB)
+GLAPI_ENTRY(MultiTexCoord1dARB)
+GLAPI_ENTRY(MultiTexCoord1dvARB)
+GLAPI_ENTRY(MultiTexCoord1fARB)
+GLAPI_ENTRY(MultiTexCoord1fvARB)
+GLAPI_ENTRY(MultiTexCoord1iARB)
+GLAPI_ENTRY(MultiTexCoord1ivARB)
+GLAPI_ENTRY(MultiTexCoord1sARB)
+GLAPI_ENTRY(MultiTexCoord1svARB)
+GLAPI_ENTRY(MultiTexCoord2dARB)
+GLAPI_ENTRY(MultiTexCoord2dvARB)
+GLAPI_ENTRY(MultiTexCoord2fARB)
+GLAPI_ENTRY(MultiTexCoord2fvARB)
+GLAPI_ENTRY(MultiTexCoord2iARB)
+GLAPI_ENTRY(MultiTexCoord2ivARB)
+GLAPI_ENTRY(MultiTexCoord2sARB)
+GLAPI_ENTRY(MultiTexCoord2svARB)
+GLAPI_ENTRY(MultiTexCoord3dARB)
+GLAPI_ENTRY(MultiTexCoord3dvARB)
+GLAPI_ENTRY(MultiTexCoord3fARB)
+GLAPI_ENTRY(MultiTexCoord3fvARB)
+GLAPI_ENTRY(MultiTexCoord3iARB)
+GLAPI_ENTRY(MultiTexCoord3ivARB)
+GLAPI_ENTRY(MultiTexCoord3sARB)
+GLAPI_ENTRY(MultiTexCoord3svARB)
+GLAPI_ENTRY(MultiTexCoord4dARB)
+GLAPI_ENTRY(MultiTexCoord4dvARB)
+GLAPI_ENTRY(MultiTexCoord4fARB)
+GLAPI_ENTRY(MultiTexCoord4fvARB)
+GLAPI_ENTRY(MultiTexCoord4iARB)
+GLAPI_ENTRY(MultiTexCoord4ivARB)
+GLAPI_ENTRY(MultiTexCoord4sARB)
+GLAPI_ENTRY(MultiTexCoord4svARB)
+GLAPI_ENTRY(AttachShader)
+GLAPI_ENTRY(CreateProgram)
+GLAPI_ENTRY(CreateShader)
+GLAPI_ENTRY(DeleteProgram)
+GLAPI_ENTRY(DeleteShader)
+GLAPI_ENTRY(DetachShader)
+GLAPI_ENTRY(GetAttachedShaders)
+GLAPI_ENTRY(GetProgramInfoLog)
+GLAPI_ENTRY(GetProgramiv)
+GLAPI_ENTRY(GetShaderInfoLog)
+GLAPI_ENTRY(GetShaderiv)
+GLAPI_ENTRY(IsProgram)
+GLAPI_ENTRY(IsShader)
+GLAPI_ENTRY(StencilFuncSeparate)
+GLAPI_ENTRY(StencilMaskSeparate)
+GLAPI_ENTRY(StencilOpSeparate)
+GLAPI_ENTRY(UniformMatrix2x3fv)
+GLAPI_ENTRY(UniformMatrix2x4fv)
+GLAPI_ENTRY(UniformMatrix3x2fv)
+GLAPI_ENTRY(UniformMatrix3x4fv)
+GLAPI_ENTRY(UniformMatrix4x2fv)
+GLAPI_ENTRY(UniformMatrix4x3fv)
+GLAPI_ENTRY(LoadTransposeMatrixdARB)
+GLAPI_ENTRY(LoadTransposeMatrixfARB)
+GLAPI_ENTRY(MultTransposeMatrixdARB)
+GLAPI_ENTRY(MultTransposeMatrixfARB)
+GLAPI_ENTRY(SampleCoverageARB)
+GLAPI_ENTRY(CompressedTexImage1DARB)
+GLAPI_ENTRY(CompressedTexImage2DARB)
+GLAPI_ENTRY(CompressedTexImage3DARB)
+GLAPI_ENTRY(CompressedTexSubImage1DARB)
+GLAPI_ENTRY(CompressedTexSubImage2DARB)
+GLAPI_ENTRY(CompressedTexSubImage3DARB)
+GLAPI_ENTRY(GetCompressedTexImageARB)
+GLAPI_ENTRY(DisableVertexAttribArrayARB)
+GLAPI_ENTRY(EnableVertexAttribArrayARB)
+GLAPI_ENTRY(GetProgramEnvParameterdvARB)
+GLAPI_ENTRY(GetProgramEnvParameterfvARB)
+GLAPI_ENTRY(GetProgramLocalParameterdvARB)
+GLAPI_ENTRY(GetProgramLocalParameterfvARB)
+GLAPI_ENTRY(GetProgramStringARB)
+GLAPI_ENTRY(GetProgramivARB)
+GLAPI_ENTRY(GetVertexAttribdvARB)
+GLAPI_ENTRY(GetVertexAttribfvARB)
+GLAPI_ENTRY(GetVertexAttribivARB)
+GLAPI_ENTRY(ProgramEnvParameter4dARB)
+GLAPI_ENTRY(ProgramEnvParameter4dvARB)
+GLAPI_ENTRY(ProgramEnvParameter4fARB)
+GLAPI_ENTRY(ProgramEnvParameter4fvARB)
+GLAPI_ENTRY(ProgramLocalParameter4dARB)
+GLAPI_ENTRY(ProgramLocalParameter4dvARB)
+GLAPI_ENTRY(ProgramLocalParameter4fARB)
+GLAPI_ENTRY(ProgramLocalParameter4fvARB)
+GLAPI_ENTRY(ProgramStringARB)
+GLAPI_ENTRY(VertexAttrib1dARB)
+GLAPI_ENTRY(VertexAttrib1dvARB)
+GLAPI_ENTRY(VertexAttrib1fARB)
+GLAPI_ENTRY(VertexAttrib1fvARB)
+GLAPI_ENTRY(VertexAttrib1sARB)
+GLAPI_ENTRY(VertexAttrib1svARB)
+GLAPI_ENTRY(VertexAttrib2dARB)
+GLAPI_ENTRY(VertexAttrib2dvARB)
+GLAPI_ENTRY(VertexAttrib2fARB)
+GLAPI_ENTRY(VertexAttrib2fvARB)
+GLAPI_ENTRY(VertexAttrib2sARB)
+GLAPI_ENTRY(VertexAttrib2svARB)
+GLAPI_ENTRY(VertexAttrib3dARB)
+GLAPI_ENTRY(VertexAttrib3dvARB)
+GLAPI_ENTRY(VertexAttrib3fARB)
+GLAPI_ENTRY(VertexAttrib3fvARB)
+GLAPI_ENTRY(VertexAttrib3sARB)
+GLAPI_ENTRY(VertexAttrib3svARB)
+GLAPI_ENTRY(VertexAttrib4NbvARB)
+GLAPI_ENTRY(VertexAttrib4NivARB)
+GLAPI_ENTRY(VertexAttrib4NsvARB)
+GLAPI_ENTRY(VertexAttrib4NubARB)
+GLAPI_ENTRY(VertexAttrib4NubvARB)
+GLAPI_ENTRY(VertexAttrib4NuivARB)
+GLAPI_ENTRY(VertexAttrib4NusvARB)
+GLAPI_ENTRY(VertexAttrib4bvARB)
+GLAPI_ENTRY(VertexAttrib4dARB)
+GLAPI_ENTRY(VertexAttrib4dvARB)
+GLAPI_ENTRY(VertexAttrib4fARB)
+GLAPI_ENTRY(VertexAttrib4fvARB)
+GLAPI_ENTRY(VertexAttrib4ivARB)
+GLAPI_ENTRY(VertexAttrib4sARB)
+GLAPI_ENTRY(VertexAttrib4svARB)
+GLAPI_ENTRY(VertexAttrib4ubvARB)
+GLAPI_ENTRY(VertexAttrib4uivARB)
+GLAPI_ENTRY(VertexAttrib4usvARB)
+GLAPI_ENTRY(VertexAttribPointerARB)
+GLAPI_ENTRY(BindBufferARB)
+GLAPI_ENTRY(BufferDataARB)
+GLAPI_ENTRY(BufferSubDataARB)
+GLAPI_ENTRY(DeleteBuffersARB)
+GLAPI_ENTRY(GenBuffersARB)
+GLAPI_ENTRY(GetBufferParameterivARB)
+GLAPI_ENTRY(GetBufferPointervARB)
+GLAPI_ENTRY(GetBufferSubDataARB)
+GLAPI_ENTRY(IsBufferARB)
+GLAPI_ENTRY(MapBufferARB)
+GLAPI_ENTRY(UnmapBufferARB)
+GLAPI_ENTRY(BeginQueryARB)
+GLAPI_ENTRY(DeleteQueriesARB)
+GLAPI_ENTRY(EndQueryARB)
+GLAPI_ENTRY(GenQueriesARB)
+GLAPI_ENTRY(GetQueryObjectivARB)
+GLAPI_ENTRY(GetQueryObjectuivARB)
+GLAPI_ENTRY(GetQueryivARB)
+GLAPI_ENTRY(IsQueryARB)
+GLAPI_ENTRY(AttachObjectARB)
+GLAPI_ENTRY(CompileShaderARB)
+GLAPI_ENTRY(CreateProgramObjectARB)
+GLAPI_ENTRY(CreateShaderObjectARB)
+GLAPI_ENTRY(DeleteObjectARB)
+GLAPI_ENTRY(DetachObjectARB)
+GLAPI_ENTRY(GetActiveUniformARB)
+GLAPI_ENTRY(GetAttachedObjectsARB)
+GLAPI_ENTRY(GetHandleARB)
+GLAPI_ENTRY(GetInfoLogARB)
+GLAPI_ENTRY(GetObjectParameterfvARB)
+GLAPI_ENTRY(GetObjectParameterivARB)
+GLAPI_ENTRY(GetShaderSourceARB)
+GLAPI_ENTRY(GetUniformLocationARB)
+GLAPI_ENTRY(GetUniformfvARB)
+GLAPI_ENTRY(GetUniformivARB)
+GLAPI_ENTRY(LinkProgramARB)
+GLAPI_ENTRY(ShaderSourceARB)
+GLAPI_ENTRY(Uniform1fARB)
+GLAPI_ENTRY(Uniform1fvARB)
+GLAPI_ENTRY(Uniform1iARB)
+GLAPI_ENTRY(Uniform1ivARB)
+GLAPI_ENTRY(Uniform2fARB)
+GLAPI_ENTRY(Uniform2fvARB)
+GLAPI_ENTRY(Uniform2iARB)
+GLAPI_ENTRY(Uniform2ivARB)
+GLAPI_ENTRY(Uniform3fARB)
+GLAPI_ENTRY(Uniform3fvARB)
+GLAPI_ENTRY(Uniform3iARB)
+GLAPI_ENTRY(Uniform3ivARB)
+GLAPI_ENTRY(Uniform4fARB)
+GLAPI_ENTRY(Uniform4fvARB)
+GLAPI_ENTRY(Uniform4iARB)
+GLAPI_ENTRY(Uniform4ivARB)
+GLAPI_ENTRY(UniformMatrix2fvARB)
+GLAPI_ENTRY(UniformMatrix3fvARB)
+GLAPI_ENTRY(UniformMatrix4fvARB)
+GLAPI_ENTRY(UseProgramObjectARB)
+GLAPI_ENTRY(ValidateProgramARB)
+GLAPI_ENTRY(BindAttribLocationARB)
+GLAPI_ENTRY(GetActiveAttribARB)
+GLAPI_ENTRY(GetAttribLocationARB)
+//GLAPI_ENTRY(DrawBuffersARB)
+//GLAPI_ENTRY(PolygonOffsetEXT)
+//GLAPI_ENTRY(GetPixelTexGenParameterfvSGIS)
+//GLAPI_ENTRY(GetPixelTexGenParameterivSGIS)
+//GLAPI_ENTRY(PixelTexGenParameterfSGIS)
+//GLAPI_ENTRY(PixelTexGenParameterfvSGIS)
+//GLAPI_ENTRY(PixelTexGenParameteriSGIS)
+//GLAPI_ENTRY(PixelTexGenParameterivSGIS)
+GLAPI_ENTRY(PointParameterfEXT)
+GLAPI_ENTRY(PointParameterfvEXT)
+GLAPI_ENTRY(LockArraysEXT)
+GLAPI_ENTRY(UnlockArraysEXT)
+//GLAPI_ENTRY(CullParameterdvEXT)
+//GLAPI_ENTRY(CullParameterfvEXT)
+GLAPI_ENTRY(SecondaryColor3bEXT)
+GLAPI_ENTRY(SecondaryColor3bvEXT)
+GLAPI_ENTRY(SecondaryColor3dEXT)
+GLAPI_ENTRY(SecondaryColor3dvEXT)
+GLAPI_ENTRY(SecondaryColor3fEXT)
+GLAPI_ENTRY(SecondaryColor3fvEXT)
+GLAPI_ENTRY(SecondaryColor3iEXT)
+GLAPI_ENTRY(SecondaryColor3ivEXT)
+GLAPI_ENTRY(SecondaryColor3sEXT)
+GLAPI_ENTRY(SecondaryColor3svEXT)
+GLAPI_ENTRY(SecondaryColor3ubEXT)
+GLAPI_ENTRY(SecondaryColor3ubvEXT)
+GLAPI_ENTRY(SecondaryColor3uiEXT)
+GLAPI_ENTRY(SecondaryColor3uivEXT)
+GLAPI_ENTRY(SecondaryColor3usEXT)
+GLAPI_ENTRY(SecondaryColor3usvEXT)
+GLAPI_ENTRY(SecondaryColorPointerEXT)
+GLAPI_ENTRY(MultiDrawArraysEXT)
+GLAPI_ENTRY(MultiDrawElementsEXT)
+GLAPI_ENTRY(FogCoordPointerEXT)
+GLAPI_ENTRY(FogCoorddEXT)
+GLAPI_ENTRY(FogCoorddvEXT)
+GLAPI_ENTRY(FogCoordfEXT)
+GLAPI_ENTRY(FogCoordfvEXT)
+//GLAPI_ENTRY(PixelTexGenSGIX)
+GLAPI_ENTRY(BlendFuncSeparateEXT)
+GLAPI_ENTRY(FlushVertexArrayRangeNV)
+GLAPI_ENTRY(VertexArrayRangeNV)
+GLAPI_ENTRY(CombinerInputNV)
+GLAPI_ENTRY(CombinerOutputNV)
+GLAPI_ENTRY(CombinerParameterfNV)
+GLAPI_ENTRY(CombinerParameterfvNV)
+GLAPI_ENTRY(CombinerParameteriNV)
+GLAPI_ENTRY(CombinerParameterivNV)
+GLAPI_ENTRY(FinalCombinerInputNV)
+GLAPI_ENTRY(GetCombinerInputParameterfvNV)
+GLAPI_ENTRY(GetCombinerInputParameterivNV)
+GLAPI_ENTRY(GetCombinerOutputParameterfvNV)
+GLAPI_ENTRY(GetCombinerOutputParameterivNV)
+GLAPI_ENTRY(GetFinalCombinerInputParameterfvNV)
+GLAPI_ENTRY(GetFinalCombinerInputParameterivNV)
+GLAPI_ENTRY(DeleteFencesNV)
+GLAPI_ENTRY(FinishFenceNV)
+GLAPI_ENTRY(GenFencesNV)
+GLAPI_ENTRY(GetFenceivNV)
+GLAPI_ENTRY(IsFenceNV)
+GLAPI_ENTRY(SetFenceNV)
+GLAPI_ENTRY(TestFenceNV)
+GLAPI_ENTRY(AreProgramsResidentNV)
+GLAPI_ENTRY(BindProgramNV)
+GLAPI_ENTRY(DeleteProgramsNV)
+GLAPI_ENTRY(ExecuteProgramNV)
+GLAPI_ENTRY(GenProgramsNV)
+GLAPI_ENTRY(GetProgramParameterdvNV)
+GLAPI_ENTRY(GetProgramParameterfvNV)
+GLAPI_ENTRY(GetProgramStringNV)
+GLAPI_ENTRY(GetProgramivNV)
+GLAPI_ENTRY(GetTrackMatrixivNV)
+GLAPI_ENTRY(GetVertexAttribPointervNV)
+GLAPI_ENTRY(GetVertexAttribdvNV)
+GLAPI_ENTRY(GetVertexAttribfvNV)
+GLAPI_ENTRY(GetVertexAttribivNV)
+GLAPI_ENTRY(IsProgramNV)
+GLAPI_ENTRY(LoadProgramNV)
+GLAPI_ENTRY(ProgramParameters4dvNV)
+GLAPI_ENTRY(ProgramParameters4fvNV)
+GLAPI_ENTRY(RequestResidentProgramsNV)
+GLAPI_ENTRY(TrackMatrixNV)
+GLAPI_ENTRY(VertexAttrib1dNV)
+GLAPI_ENTRY(VertexAttrib1dvNV)
+GLAPI_ENTRY(VertexAttrib1fNV)
+GLAPI_ENTRY(VertexAttrib1fvNV)
+GLAPI_ENTRY(VertexAttrib1sNV)
+GLAPI_ENTRY(VertexAttrib1svNV)
+GLAPI_ENTRY(VertexAttrib2dNV)
+GLAPI_ENTRY(VertexAttrib2dvNV)
+GLAPI_ENTRY(VertexAttrib2fNV)
+GLAPI_ENTRY(VertexAttrib2fvNV)
+GLAPI_ENTRY(VertexAttrib2sNV)
+GLAPI_ENTRY(VertexAttrib2svNV)
+GLAPI_ENTRY(VertexAttrib3dNV)
+GLAPI_ENTRY(VertexAttrib3dvNV)
+GLAPI_ENTRY(VertexAttrib3fNV)
+GLAPI_ENTRY(VertexAttrib3fvNV)
+GLAPI_ENTRY(VertexAttrib3sNV)
+GLAPI_ENTRY(VertexAttrib3svNV)
+GLAPI_ENTRY(VertexAttrib4dNV)
+GLAPI_ENTRY(VertexAttrib4dvNV)
+GLAPI_ENTRY(VertexAttrib4fNV)
+GLAPI_ENTRY(VertexAttrib4fvNV)
+GLAPI_ENTRY(VertexAttrib4sNV)
+GLAPI_ENTRY(VertexAttrib4svNV)
+GLAPI_ENTRY(VertexAttrib4ubNV)
+GLAPI_ENTRY(VertexAttrib4ubvNV)
+GLAPI_ENTRY(VertexAttribPointerNV)
+GLAPI_ENTRY(VertexAttribs1dvNV)
+GLAPI_ENTRY(VertexAttribs1fvNV)
+GLAPI_ENTRY(VertexAttribs1svNV)
+GLAPI_ENTRY(VertexAttribs2dvNV)
+GLAPI_ENTRY(VertexAttribs2fvNV)
+GLAPI_ENTRY(VertexAttribs2svNV)
+GLAPI_ENTRY(VertexAttribs3dvNV)
+GLAPI_ENTRY(VertexAttribs3fvNV)
+GLAPI_ENTRY(VertexAttribs3svNV)
+GLAPI_ENTRY(VertexAttribs4dvNV)
+GLAPI_ENTRY(VertexAttribs4fvNV)
+GLAPI_ENTRY(VertexAttribs4svNV)
+GLAPI_ENTRY(VertexAttribs4ubvNV)
+GLAPI_ENTRY(PointParameteriNV)
+GLAPI_ENTRY(PointParameterivNV)
+GLAPI_ENTRY(GetProgramNamedParameterdvNV)
+GLAPI_ENTRY(GetProgramNamedParameterfvNV)
+GLAPI_ENTRY(ProgramNamedParameter4dNV)
+GLAPI_ENTRY(ProgramNamedParameter4dvNV)
+GLAPI_ENTRY(ProgramNamedParameter4fNV)
+GLAPI_ENTRY(ProgramNamedParameter4fvNV)
+//GLAPI_ENTRY(BlendEquationSeparateEXT)
+GLAPI_ENTRY(BindFramebufferEXT)
+GLAPI_ENTRY(BindRenderbufferEXT)
+GLAPI_ENTRY(CheckFramebufferStatusEXT)
+GLAPI_ENTRY(DeleteFramebuffersEXT)
+GLAPI_ENTRY(DeleteRenderbuffersEXT)
+GLAPI_ENTRY(FramebufferRenderbufferEXT)
+GLAPI_ENTRY(FramebufferTexture1DEXT)
+GLAPI_ENTRY(FramebufferTexture2DEXT)
+GLAPI_ENTRY(FramebufferTexture3DEXT)
+GLAPI_ENTRY(GenFramebuffersEXT)
+GLAPI_ENTRY(GenRenderbuffersEXT)
+GLAPI_ENTRY(GenerateMipmapEXT)
+GLAPI_ENTRY(GetFramebufferAttachmentParameterivEXT)
+GLAPI_ENTRY(GetRenderbufferParameterivEXT)
+GLAPI_ENTRY(IsFramebufferEXT)
+GLAPI_ENTRY(IsRenderbufferEXT)
+GLAPI_ENTRY(RenderbufferStorageEXT)
+
diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h b/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h
new file mode 100644
index 00000000..c1e7274d
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h
@@ -0,0 +1,93 @@
+/* $Id: fakedri_glxfuncsList.h $ */
+/** @file
+ * VBox OpenGL list of opengl functions common in Mesa and vbox opengl stub
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GLXAPI_ENTRY
+#error GLXAPI_ENTRY should be defined.
+#endif
+
+/*This should match glX* entries which are exported by Mesa's libGL.so,
+ * use something like the following to get the list:
+ * objdump -T libGL.so|grep glX|grep -v __|awk '{print $7;};'|sed 's/glX//'|awk '{OFS=""; print "GLXAPI_ENTRY(",$1,")"}'
+ */
+
+/* #######Note: if you change the list, don't forget to change Linux_i386_glxapi_exports.py####### */
+
+GLXAPI_ENTRY(CopyContext)
+GLXAPI_ENTRY(UseXFont)
+/*GLXAPI_ENTRY(GetDriverConfig)*/
+GLXAPI_ENTRY(GetProcAddress)
+GLXAPI_ENTRY(QueryExtension)
+GLXAPI_ENTRY(IsDirect)
+GLXAPI_ENTRY(DestroyGLXPbufferSGIX)
+GLXAPI_ENTRY(QueryGLXPbufferSGIX)
+GLXAPI_ENTRY(CreateGLXPixmap)
+GLXAPI_ENTRY(CreateGLXPixmapWithConfigSGIX)
+GLXAPI_ENTRY(QueryContext)
+GLXAPI_ENTRY(CreateContextWithConfigSGIX)
+GLXAPI_ENTRY(SwapBuffers)
+GLXAPI_ENTRY(CreateNewContext)
+GLXAPI_ENTRY(SelectEventSGIX)
+GLXAPI_ENTRY(GetCurrentDrawable)
+GLXAPI_ENTRY(ChooseFBConfig)
+GLXAPI_ENTRY(WaitGL)
+GLXAPI_ENTRY(GetFBConfigs)
+GLXAPI_ENTRY(CreatePixmap)
+GLXAPI_ENTRY(GetSelectedEventSGIX)
+GLXAPI_ENTRY(GetCurrentReadDrawable)
+GLXAPI_ENTRY(GetCurrentDisplay)
+GLXAPI_ENTRY(QueryServerString)
+GLXAPI_ENTRY(CreateWindow)
+GLXAPI_ENTRY(SelectEvent)
+GLXAPI_ENTRY(GetVisualFromFBConfigSGIX)
+GLXAPI_ENTRY(GetFBConfigFromVisualSGIX)
+GLXAPI_ENTRY(QueryDrawable)
+GLXAPI_ENTRY(CreateContext)
+GLXAPI_ENTRY(GetConfig)
+GLXAPI_ENTRY(CreateGLXPbufferSGIX)
+GLXAPI_ENTRY(CreatePbuffer)
+GLXAPI_ENTRY(ChooseFBConfigSGIX)
+GLXAPI_ENTRY(WaitX)
+GLXAPI_ENTRY(GetVisualFromFBConfig)
+/*GLXAPI_ENTRY(GetScreenDriver)*/
+GLXAPI_ENTRY(GetFBConfigAttrib)
+GLXAPI_ENTRY(GetCurrentContext)
+GLXAPI_ENTRY(GetClientString)
+GLXAPI_ENTRY(DestroyPixmap)
+GLXAPI_ENTRY(MakeCurrent)
+GLXAPI_ENTRY(DestroyContext)
+GLXAPI_ENTRY(GetProcAddressARB)
+GLXAPI_ENTRY(GetSelectedEvent)
+GLXAPI_ENTRY(DestroyPbuffer)
+GLXAPI_ENTRY(DestroyWindow)
+GLXAPI_ENTRY(DestroyGLXPixmap)
+GLXAPI_ENTRY(QueryVersion)
+GLXAPI_ENTRY(ChooseVisual)
+GLXAPI_ENTRY(MakeContextCurrent)
+GLXAPI_ENTRY(QueryExtensionsString)
+GLXAPI_ENTRY(GetFBConfigAttribSGIX)
+#ifdef VBOXOGL_FAKEDRI
+GLXAPI_ENTRY(FreeMemoryMESA)
+GLXAPI_ENTRY(QueryContextInfoEXT)
+GLXAPI_ENTRY(ImportContextEXT)
+GLXAPI_ENTRY(GetContextIDEXT)
+GLXAPI_ENTRY(MakeCurrentReadSGI)
+GLXAPI_ENTRY(AllocateMemoryMESA)
+GLXAPI_ENTRY(GetMemoryOffsetMESA)
+GLXAPI_ENTRY(CreateGLXPixmapMESA)
+GLXAPI_ENTRY(GetCurrentDisplayEXT)
+GLXAPI_ENTRY(FreeContextEXT)
+#endif
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/Makefile.kup b/src/VBox/Additions/common/crOpenGL/feedback/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/Makefile.kup
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback.def b/src/VBox/Additions/common/crOpenGL/feedback/feedback.def
new file mode 100644
index 00000000..9edc7163
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback.def
@@ -0,0 +1,6 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback.py b/src/VBox/Additions/common/crOpenGL/feedback/feedback.py
new file mode 100755
index 00000000..181b23fd
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback.py
@@ -0,0 +1,270 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - generated by feedback.py */
+#include <stdio.h>
+#include "cr_spu.h"
+#include "feedbackspu.h"
+#include "feedbackspu_proto.h"
+#include "cr_packfunctions.h"
+#include "cr_glstate.h"
+
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ if apiutil.FindSpecial( "feedback", func_name ):
+ print('static %s FEEDBACKSPU_APIENTRY feedbackspu_%s(%s)' % ( return_type, func_name, apiutil.MakeDeclarationString(params) ))
+ print('{')
+ print('\tfeedback_spu.super.%s(%s);' % ( func_name, apiutil.MakeCallString(params) ))
+ print('}')
+
+
+
+print("""
+#define CHANGE(name, func) crSPUChangeInterface((void *)&(feedback_spu.self), (void *)feedback_spu.self.name, (void *)((SPUGenericFunction) func))
+#define CHANGESWAP(name, swapfunc, regfunc) crSPUChangeInterface( (void *)&(feedback_spu.self), (void *)feedback_spu.self.name, (void *)((SPUGenericFunction) (feedback_spu.swap ? swapfunc: regfunc )))
+
+static void __loadFeedbackAPI( void )
+{
+""")
+for func_name in keys:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ if apiutil.FindSpecial( "feedback", func_name ):
+ print('\tCHANGE(%s, crStateFeedback%s);' % (func_name, func_name ))
+print("""
+}
+
+static void __loadSelectAPI( void )
+{
+""")
+for func_name in keys:
+ if apiutil.FindSpecial( "select", func_name ):
+ print('\tCHANGE(%s, crStateSelect%s);' % (func_name, func_name ))
+ elif apiutil.FindSpecial( "feedback", func_name ):
+ print('\tCHANGE(%s, feedbackspu_%s);' % (func_name, func_name ))
+print("""
+}
+
+static void __loadRenderAPI( void )
+{
+""")
+
+for func_name in keys:
+ return_type = apiutil.ReturnType(func_name)
+ if apiutil.FindSpecial( "feedback", func_name ) or apiutil.FindSpecial( "select", func_name ):
+ print('\tCHANGE(%s, feedbackspu_%s);' % (func_name, func_name ))
+print("""
+}
+""")
+
+print("""
+static GLint FEEDBACKSPU_APIENTRY feedbackspu_RenderMode ( GLenum mode )
+{
+ feedback_spu.render_mode = mode;
+
+ switch (mode) {
+ case GL_FEEDBACK:
+ /*printf("Switching to Feedback API\\n");*/
+ __loadFeedbackAPI( );
+ break;
+ case GL_SELECT:
+ /*printf("Switching to Selection API\\n");*/
+ __loadSelectAPI( );
+ break;
+ case GL_RENDER:
+ /*printf("Switching to Render API\\n");*/
+ __loadRenderAPI( );
+ break;
+ }
+
+ return crStateRenderMode( mode );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_Begin ( GLenum mode )
+{
+ if (feedback_spu.render_mode == GL_FEEDBACK)
+ crStateFeedbackBegin( mode );
+ else if (feedback_spu.render_mode == GL_SELECT)
+ crStateSelectBegin( mode );
+ else
+ {
+ crStateBegin( mode );
+ feedback_spu.super.Begin( mode );
+ }
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_End ( void )
+{
+ if (feedback_spu.render_mode == GL_FEEDBACK)
+ crStateFeedbackEnd( );
+ else if (feedback_spu.render_mode == GL_SELECT)
+ crStateSelectEnd( );
+ else
+ {
+ crStateEnd( );
+ feedback_spu.super.End( );
+ }
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_Bitmap ( GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap )
+{
+ crStateBitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+
+ if (feedback_spu.render_mode == GL_FEEDBACK)
+ crStateFeedbackBitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+ else if (feedback_spu.render_mode == GL_SELECT)
+ crStateSelectBitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+ else
+ feedback_spu.super.Bitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_CopyPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum type )
+{
+ if (feedback_spu.render_mode == GL_FEEDBACK)
+ crStateFeedbackCopyPixels( x, y, width, height, type );
+ else if (feedback_spu.render_mode == GL_SELECT)
+ crStateSelectCopyPixels( x, y, width, height, type );
+ else
+ feedback_spu.super.CopyPixels( x, y, width, height, type );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_DrawPixels( GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ if (feedback_spu.render_mode == GL_FEEDBACK)
+ crStateFeedbackDrawPixels( width, height, format, type, pixels );
+ else if (feedback_spu.render_mode == GL_SELECT)
+ crStateSelectDrawPixels( width, height, format, type, pixels );
+ else
+ feedback_spu.super.DrawPixels( width, height, format, type, pixels );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_GetBooleanv( GLenum pname, GLboolean *params )
+
+{
+ if (pname == GL_FEEDBACK_BUFFER_SIZE ||
+ pname == GL_FEEDBACK_BUFFER_TYPE ||
+ pname == GL_SELECTION_BUFFER_SIZE)
+ crStateFeedbackGetBooleanv( pname, params );
+ else
+ if (pname == GL_VIEWPORT && feedback_spu.default_viewport)
+ crStateGetBooleanv( pname, params );
+ else
+ feedback_spu.super.GetBooleanv( pname, params );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_GetDoublev( GLenum pname, GLdouble *params )
+
+{
+ if (pname == GL_FEEDBACK_BUFFER_SIZE ||
+ pname == GL_FEEDBACK_BUFFER_TYPE ||
+ pname == GL_SELECTION_BUFFER_SIZE)
+ crStateFeedbackGetDoublev( pname, params );
+ else
+ if (pname == GL_VIEWPORT && feedback_spu.default_viewport)
+ crStateGetDoublev( pname, params );
+ else
+ feedback_spu.super.GetDoublev( pname, params );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_GetFloatv( GLenum pname, GLfloat *params )
+
+{
+ if (pname == GL_FEEDBACK_BUFFER_SIZE ||
+ pname == GL_FEEDBACK_BUFFER_TYPE ||
+ pname == GL_SELECTION_BUFFER_SIZE)
+ crStateFeedbackGetFloatv( pname, params );
+ else
+ if (pname == GL_VIEWPORT && feedback_spu.default_viewport)
+ crStateGetFloatv( pname, params );
+ else
+ feedback_spu.super.GetFloatv( pname, params );
+}
+
+static void FEEDBACKSPU_APIENTRY feedbackspu_GetIntegerv( GLenum pname, GLint *params )
+
+{
+ if (pname == GL_FEEDBACK_BUFFER_SIZE ||
+ pname == GL_FEEDBACK_BUFFER_TYPE ||
+ pname == GL_SELECTION_BUFFER_SIZE)
+ crStateFeedbackGetIntegerv( pname, params );
+ else
+ if (pname == GL_VIEWPORT && feedback_spu.default_viewport)
+ crStateGetIntegerv( pname, params );
+ else
+ feedback_spu.super.GetIntegerv( pname, params );
+}
+
+SPUNamedFunctionTable _cr_feedback_table[] = {
+""")
+
+for func_name in keys:
+ if apiutil.FindSpecial( "feedback_state", func_name ):
+ print('\t{ "%s", (SPUGenericFunction) feedbackspu_%s }, ' % ( func_name, func_name ))
+print("""
+ { "GetBooleanv", (SPUGenericFunction) feedbackspu_GetBooleanv },
+ { "GetDoublev", (SPUGenericFunction) feedbackspu_GetDoublev },
+ { "GetFloatv", (SPUGenericFunction) feedbackspu_GetFloatv },
+ { "GetIntegerv", (SPUGenericFunction) feedbackspu_GetIntegerv },
+ { "FeedbackBuffer", (SPUGenericFunction) crStateFeedbackBuffer },
+ { "SelectBuffer", (SPUGenericFunction) crStateSelectBuffer },
+ { "InitNames", (SPUGenericFunction) crStateInitNames },
+ { "LoadName", (SPUGenericFunction) crStateLoadName },
+ { "PushName", (SPUGenericFunction) crStatePushName },
+ { "PopName", (SPUGenericFunction) crStatePopName },
+ { "Begin", (SPUGenericFunction) feedbackspu_Begin },
+ { "End", (SPUGenericFunction) feedbackspu_End },
+ { "Bitmap", (SPUGenericFunction) feedbackspu_Bitmap },
+ { "CopyPixels", (SPUGenericFunction) feedbackspu_CopyPixels },
+ { "DrawPixels", (SPUGenericFunction) feedbackspu_DrawPixels },
+ { "TexCoord1d", (SPUGenericFunction) feedbackspu_TexCoord1d },
+ { "TexCoord1dv", (SPUGenericFunction) feedbackspu_TexCoord1dv },
+ { "TexCoord1f", (SPUGenericFunction) feedbackspu_TexCoord1f },
+ { "TexCoord1fv", (SPUGenericFunction) feedbackspu_TexCoord1fv },
+ { "TexCoord1s", (SPUGenericFunction) feedbackspu_TexCoord1s },
+ { "TexCoord1sv", (SPUGenericFunction) feedbackspu_TexCoord1sv },
+ { "TexCoord1i", (SPUGenericFunction) feedbackspu_TexCoord1i },
+ { "TexCoord1iv", (SPUGenericFunction) feedbackspu_TexCoord1iv },
+ { "TexCoord2d", (SPUGenericFunction) feedbackspu_TexCoord2d },
+ { "TexCoord2dv", (SPUGenericFunction) feedbackspu_TexCoord2dv },
+ { "TexCoord2f", (SPUGenericFunction) feedbackspu_TexCoord2f },
+ { "TexCoord2fv", (SPUGenericFunction) feedbackspu_TexCoord2fv },
+ { "TexCoord2s", (SPUGenericFunction) feedbackspu_TexCoord2s },
+ { "TexCoord2sv", (SPUGenericFunction) feedbackspu_TexCoord2sv },
+ { "TexCoord2i", (SPUGenericFunction) feedbackspu_TexCoord2i },
+ { "TexCoord2iv", (SPUGenericFunction) feedbackspu_TexCoord2iv },
+ { "TexCoord3d", (SPUGenericFunction) feedbackspu_TexCoord3d },
+ { "TexCoord3dv", (SPUGenericFunction) feedbackspu_TexCoord3dv },
+ { "TexCoord3f", (SPUGenericFunction) feedbackspu_TexCoord3f },
+ { "TexCoord3fv", (SPUGenericFunction) feedbackspu_TexCoord3fv },
+ { "TexCoord3s", (SPUGenericFunction) feedbackspu_TexCoord3s },
+ { "TexCoord3sv", (SPUGenericFunction) feedbackspu_TexCoord3sv },
+ { "TexCoord3i", (SPUGenericFunction) feedbackspu_TexCoord3i },
+ { "TexCoord3iv", (SPUGenericFunction) feedbackspu_TexCoord3iv },
+ { "TexCoord4d", (SPUGenericFunction) feedbackspu_TexCoord4d },
+ { "TexCoord4dv", (SPUGenericFunction) feedbackspu_TexCoord4dv },
+ { "TexCoord4f", (SPUGenericFunction) feedbackspu_TexCoord4f },
+ { "TexCoord4fv", (SPUGenericFunction) feedbackspu_TexCoord4fv },
+ { "TexCoord4s", (SPUGenericFunction) feedbackspu_TexCoord4s },
+ { "TexCoord4sv", (SPUGenericFunction) feedbackspu_TexCoord4sv },
+ { "TexCoord4i", (SPUGenericFunction) feedbackspu_TexCoord4i },
+ { "TexCoord4iv", (SPUGenericFunction) feedbackspu_TexCoord4iv },
+ { "RenderMode", (SPUGenericFunction) feedbackspu_RenderMode },
+ { NULL, NULL }
+};
+""")
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c b/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c
new file mode 100644
index 00000000..11158b5a
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c
@@ -0,0 +1,131 @@
+/* $Id: feedback_context.c $ */
+/** @file
+ * VBox feedback spu, context tracking.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "cr_spu.h"
+#include "cr_error.h"
+#include "feedbackspu.h"
+
+/** @todo r=bird: None of the code here is referenced externally, so I've
+ * just prototyped the function here at the top of the file to make
+ * the compiler happy. */
+GLint FEEDBACKSPU_APIENTRY feedbackspu_VBoxCreateContext( GLint con, const char *dpyName, GLint visual, GLint shareCtx );
+GLint FEEDBACKSPU_APIENTRY feedbackspu_CreateContext( const char *dpyName, GLint visual, GLint shareCtx );
+void FEEDBACKSPU_APIENTRY feedbackspu_MakeCurrent( GLint window, GLint nativeWindow, GLint ctx );
+void FEEDBACKSPU_APIENTRY feedbackspu_DestroyContext( GLint ctx );
+
+
+/** @todo Multithreading case. (See feedback_spu.self.RenderMode)*/
+
+GLint FEEDBACKSPU_APIENTRY
+feedbackspu_VBoxCreateContext( GLint con, const char *dpyName, GLint visual, GLint shareCtx )
+{
+ GLint ctx, slot;
+
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&feedback_spu.mutex);
+#endif
+
+ ctx = feedback_spu.child.VBoxCreateContext(con, dpyName, visual, shareCtx);
+
+ /* find an empty context slot */
+ for (slot = 0; slot < feedback_spu.numContexts; slot++) {
+ if (!feedback_spu.context[slot].clientState) {
+ /* found empty slot */
+ break;
+ }
+ }
+ if (slot == feedback_spu.numContexts) {
+ feedback_spu.numContexts++;
+ }
+
+ feedback_spu.context[slot].clientState = crStateCreateContext(NULL, visual, NULL);
+ feedback_spu.context[slot].clientCtx = ctx;
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&feedback_spu.mutex);
+#endif
+
+ return ctx;
+}
+
+GLint FEEDBACKSPU_APIENTRY
+feedbackspu_CreateContext( const char *dpyName, GLint visual, GLint shareCtx )
+{
+ return feedbackspu_VBoxCreateContext( 0, dpyName, visual, shareCtx );
+}
+
+void FEEDBACKSPU_APIENTRY
+feedbackspu_MakeCurrent( GLint window, GLint nativeWindow, GLint ctx )
+{
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&feedback_spu.mutex);
+#endif
+ feedback_spu.child.MakeCurrent(window, nativeWindow, ctx);
+
+ if (ctx) {
+ int slot;
+ GLint oldmode;
+
+ for (slot=0; slot<feedback_spu.numContexts; ++slot)
+ if (feedback_spu.context[slot].clientCtx == ctx) break;
+ CRASSERT(slot < feedback_spu.numContexts);
+
+ crStateMakeCurrent(feedback_spu.context[slot].clientState);
+
+ crStateGetIntegerv(GL_RENDER_MODE, &oldmode);
+
+ if (oldmode!=feedback_spu.render_mode)
+ {
+ feedback_spu.self.RenderMode(oldmode);
+ }
+ }
+ else
+ {
+ crStateMakeCurrent(NULL);
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&feedback_spu.mutex);
+#endif
+}
+
+void FEEDBACKSPU_APIENTRY
+feedbackspu_DestroyContext( GLint ctx )
+{
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&feedback_spu.mutex);
+#endif
+ feedback_spu.child.DestroyContext(ctx);
+
+ if (ctx) {
+ int slot;
+
+ for (slot=0; slot<feedback_spu.numContexts; ++slot)
+ if (feedback_spu.context[slot].clientCtx == ctx) break;
+ CRASSERT(slot < feedback_spu.numContexts);
+
+ crStateDestroyContext(feedback_spu.context[slot].clientState);
+
+ feedback_spu.context[slot].clientState = NULL;
+ feedback_spu.context[slot].clientCtx = 0;
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&feedback_spu.mutex);
+#endif
+}
+
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_funcs.py b/src/VBox/Additions/common/crOpenGL/feedback/feedback_funcs.py
new file mode 100755
index 00000000..d3c40833
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_funcs.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY feedback_funcs.py SCRIPT */
+#ifndef CR_STATE_FEEDBACK_FUNCS_H
+#define CR_STATE_FEEDBACK_FUNCS_H
+
+#include "cr_error.h"
+
+#if defined(WINDOWS)
+#define STATE_APIENTRY __stdcall
+#else
+#define STATE_APIENTRY
+#endif
+
+#define STATE_UNUSED(x) ((void)x)""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in apiutil.AllSpecials( "feedback" ):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('%s STATE_APIENTRY crStateFeedback%s(%s);' % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+
+for func_name in apiutil.AllSpecials( "select" ):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('%s STATE_APIENTRY crStateSelect%s(%s);' % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+print('\n#endif /* CR_STATE_FEEDBACK_FUNCS_H */')
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_special b/src/VBox/Additions/common/crOpenGL/feedback/feedback_special
new file mode 100644
index 00000000..75eb7f8f
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_special
@@ -0,0 +1,64 @@
+Vertex2d
+Vertex2dv
+Vertex2f
+Vertex2fv
+Vertex2i
+Vertex2iv
+Vertex2s
+Vertex2sv
+Vertex3d
+Vertex3dv
+Vertex3f
+Vertex3fv
+Vertex3i
+Vertex3iv
+Vertex3s
+Vertex3sv
+Vertex4d
+Vertex4dv
+Vertex4f
+Vertex4fv
+Vertex4i
+Vertex4iv
+Vertex4s
+Vertex4sv
+Rectf
+Recti
+Rectd
+Rects
+Rectiv
+Rectfv
+Rectdv
+Rectsv
+TexCoord1d
+TexCoord1dv
+TexCoord1f
+TexCoord1fv
+TexCoord1i
+TexCoord1iv
+TexCoord1s
+TexCoord1sv
+TexCoord2d
+TexCoord2dv
+TexCoord2f
+TexCoord2fv
+TexCoord2i
+TexCoord2iv
+TexCoord2s
+TexCoord2sv
+TexCoord3d
+TexCoord3dv
+TexCoord3f
+TexCoord3fv
+TexCoord3i
+TexCoord3iv
+TexCoord3s
+TexCoord3sv
+TexCoord4d
+TexCoord4dv
+TexCoord4f
+TexCoord4fv
+TexCoord4i
+TexCoord4iv
+TexCoord4s
+TexCoord4sv
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_state.py b/src/VBox/Additions/common/crOpenGL/feedback/feedback_state.py
new file mode 100755
index 00000000..01bab425
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_state.py
@@ -0,0 +1,34 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+#include "cr_server.h"
+#include "feedbackspu.h"
+#include "feedbackspu_proto.h"
+""")
+custom = ["CreateContext", "VBoxCreateContext", "MakeCurrent", "DestroyContext"]
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if apiutil.FindSpecial( "feedback_state", func_name ):
+ if func_name in custom:
+ continue
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('%s FEEDBACKSPU_APIENTRY feedbackspu_%s(%s)' % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print('{')
+ print('\tcrState%s(%s);' % (func_name, apiutil.MakeCallString(params)))
+ print('')
+ print('\tfeedback_spu.super.%s(%s);' % (func_name, apiutil.MakeCallString(params)))
+ print('}')
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_state_special b/src/VBox/Additions/common/crOpenGL/feedback/feedback_state_special
new file mode 100644
index 00000000..15bec65d
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_state_special
@@ -0,0 +1,66 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+ClipPlane
+MatrixMode
+LoadIdentity
+PopMatrix
+PushMatrix
+LoadMatrixf
+LoadMatrixd
+MultMatrixf
+MultMatrixd
+LoadTransposeMatrixfARB
+LoadTransposeMatrixdARB
+MultTransposeMatrixfARB
+MultTransposeMatrixdARB
+Translatef
+Translated
+Rotatef
+Rotated
+Scalef
+Scaled
+Frustum
+Ortho
+Viewport
+DepthRange
+Scissor
+PushAttrib
+PopAttrib
+PassThrough
+PolygonMode
+Color4f
+Color4fv
+Color3f
+Color3fv
+RasterPos2d
+RasterPos2dv
+RasterPos2f
+RasterPos2fv
+RasterPos2i
+RasterPos2iv
+RasterPos2s
+RasterPos2sv
+RasterPos3d
+RasterPos3dv
+RasterPos3f
+RasterPos3fv
+RasterPos3i
+RasterPos3iv
+RasterPos3s
+RasterPos3sv
+RasterPos4d
+RasterPos4dv
+RasterPos4f
+RasterPos4fv
+RasterPos4i
+RasterPos4iv
+RasterPos4s
+RasterPos4sv
+CreateContext
+MakeCurrent
+DestroyContext
+VBoxAttachThread
+VBoxDetachThread
+VBoxCreateContext
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.h b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.h
new file mode 100644
index 00000000..7d72a70b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_feedback_feedbackspu_h
+#define GA_INCLUDED_SRC_common_crOpenGL_feedback_feedbackspu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef WINDOWS
+#define FEEDBACKSPU_APIENTRY __stdcall
+#else
+#define FEEDBACKSPU_APIENTRY
+#endif
+
+#include "cr_spu.h"
+#include "cr_timer.h"
+#include "cr_glstate.h"
+
+typedef struct context_info_t ContextInfo;
+
+struct context_info_t {
+ CRContext *clientState; /* used to store client-side GL state */
+ GLint clientCtx; /* client context ID */
+};
+
+typedef struct {
+ int id;
+ int has_child;
+ SPUDispatchTable self, child, super;
+
+ int render_mode;
+
+ int default_viewport;
+
+ CRCurrentStatePointers current;
+
+ CRContext *defaultctx;
+ int numContexts;
+ ContextInfo context[CR_MAX_CONTEXTS];
+
+#ifdef CHROMIUM_THREADSAFE
+ CRmutex mutex;
+#endif
+} feedbackSPU;
+
+extern feedbackSPU feedback_spu;
+
+extern SPUNamedFunctionTable _cr_feedback_table[];
+
+extern SPUOptions feedbackSPUOptions[];
+
+extern void feedbackspuGatherConfiguration( void );
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_feedback_feedbackspu_h */
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.rc b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.rc
new file mode 100644
index 00000000..b9db8122
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu.rc
@@ -0,0 +1,69 @@
+/* $Id: feedbackspu.rc $ */
+/** @file
+ * VBoxOGLfeedbackspu - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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_DRV
+ FILESUBTYPE VFT2_DRV_DISPLAY
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxOGLfeedbackpu\0"
+#ifdef VBOX_WDDM_WOW64
+ VALUE "OriginalFilename", "VBoxOGLfeedbackpu-x86.dll\0"
+#else
+ VALUE "OriginalFilename", "VBoxOGLfeedbackpu.dll\0"
+#endif
+ 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
+
+1 RCDATA
+BEGIN
+// Machine dependent parameters
+ 17, // Height of vertical thumb
+ 17, // Width of horizontal thumb
+ 2, // Icon horiz compression factor
+ 2, // Icon vert compression factor
+ 1, // Cursor horz compression factor
+ 1, // Cursor vert compression factor
+ 0, // Kanji window height
+ 1, // cxBorder (thickness of vertical lines)
+ 1 // cyBorder (thickness of horizontal lines)
+END
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_config.c b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_config.c
new file mode 100644
index 00000000..9449a2bb
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_config.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_string.h"
+#include "cr_environment.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "feedbackspu.h"
+
+#include <stdio.h>
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static void __setDefaults( void )
+{
+ feedback_spu.render_mode = GL_RENDER;
+}
+
+static void set_default_viewport( void *foo, const char *response )
+{
+ (void) foo;
+ sscanf( response, "%d", &(feedback_spu.default_viewport) );
+}
+
+/* option, type, nr, default, min, max, title, callback
+ */
+SPUOptions feedbackSPUOptions[] = {
+
+ { "default_viewport", CR_BOOL, 1, "0", "0", "1",
+ "Return default viewport parameters", (SPUOptionCB)set_default_viewport },
+
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+
+};
+
+
+void feedbackspuGatherConfiguration( void )
+{
+ __setDefaults();
+}
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_init.c b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_init.c
new file mode 100644
index 00000000..f264763c
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_init.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "cr_environment.h"
+#include "cr_string.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_server.h"
+#include "feedbackspu.h"
+#include <fcntl.h>
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+feedbackSPU feedback_spu;
+
+static SPUFunctions feedback_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_feedback_table /* THE ACTUAL FUNCTIONS */
+};
+
+static SPUFunctions *feedbackSPUInit( int id, SPU *child, SPU *self,
+ unsigned int context_id,
+ unsigned int num_contexts )
+{
+ (void) context_id;
+ (void) num_contexts;
+
+#ifdef CHROMIUM_THREADSAFE
+ crInitMutex(&feedback_spu.mutex);
+#endif
+
+ feedback_spu.id = id;
+ feedback_spu.has_child = 0;
+ if (child)
+ {
+ crSPUInitDispatchTable( &(feedback_spu.child) );
+ crSPUCopyDispatchTable( &(feedback_spu.child), &(child->dispatch_table) );
+ feedback_spu.has_child = 1;
+ }
+ crSPUInitDispatchTable( &(feedback_spu.super) );
+ crSPUCopyDispatchTable( &(feedback_spu.super), &(self->superSPU->dispatch_table) );
+ feedbackspuGatherConfiguration();
+
+ /* create/init default state tracker */
+ crStateInit();
+
+ feedback_spu.defaultctx = crStateCreateContext(NULL, 0, NULL);
+ crStateSetCurrent(feedback_spu.defaultctx);
+
+ feedback_spu.numContexts = 0;
+ crMemZero(feedback_spu.context, CR_MAX_CONTEXTS * sizeof(ContextInfo));
+
+ return &feedback_functions;
+}
+
+static void feedbackSPUSelfDispatch(SPUDispatchTable *self)
+{
+ crSPUInitDispatchTable( &(feedback_spu.self) );
+ crSPUCopyDispatchTable( &(feedback_spu.self), self );
+}
+
+static int feedbackSPUCleanup(void)
+{
+ return 1;
+}
+
+int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
+ SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
+ SPUOptionsPtr *options, int *flags )
+{
+ *name = "feedback";
+ *super = "passthrough";
+ *init = feedbackSPUInit;
+ *self = feedbackSPUSelfDispatch;
+ *cleanup = feedbackSPUCleanup;
+ *options = feedbackSPUOptions;
+ *flags = (SPU_NO_PACKER|SPU_NOT_TERMINAL|SPU_MAX_SERVERS_ZERO);
+
+ return 1;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_proto.py b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_proto.py
new file mode 100755
index 00000000..92a5c1ce
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/feedbackspu_proto.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - generated by feedback.py */
+
+#ifndef FEEDBACKSPU_PROTO_H
+#define FEEDBACKSPU_PROTO_H
+
+#include "feedbackspu.h"
+
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if apiutil.FindSpecial( "feedback_state", func_name ):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('extern %s FEEDBACKSPU_APIENTRY feedbackspu_%s(%s);' % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+
+
+print("""
+#endif
+""")
diff --git a/src/VBox/Additions/common/crOpenGL/feedback/select_special b/src/VBox/Additions/common/crOpenGL/feedback/select_special
new file mode 100644
index 00000000..f35dba0a
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/feedback/select_special
@@ -0,0 +1,56 @@
+Vertex2d
+Vertex2dv
+Vertex2f
+Vertex2fv
+Vertex2i
+Vertex2iv
+Vertex2s
+Vertex2sv
+Vertex3d
+Vertex3dv
+Vertex3f
+Vertex3fv
+Vertex3i
+Vertex3iv
+Vertex3s
+Vertex3sv
+Vertex4d
+Vertex4dv
+Vertex4f
+Vertex4fv
+Vertex4i
+Vertex4iv
+Vertex4s
+Vertex4sv
+Rectf
+Recti
+Rectd
+Rects
+Rectiv
+Rectfv
+Rectdv
+Rectsv
+RasterPos2d
+RasterPos2dv
+RasterPos2f
+RasterPos2fv
+RasterPos2i
+RasterPos2iv
+RasterPos2s
+RasterPos2sv
+RasterPos3d
+RasterPos3dv
+RasterPos3f
+RasterPos3fv
+RasterPos3i
+RasterPos3iv
+RasterPos3s
+RasterPos3sv
+RasterPos4d
+RasterPos4dv
+RasterPos4f
+RasterPos4fv
+RasterPos4i
+RasterPos4iv
+RasterPos4s
+RasterPos4sv
diff --git a/src/VBox/Additions/common/crOpenGL/getprocaddress.py b/src/VBox/Additions/common/crOpenGL/getprocaddress.py
new file mode 100755
index 00000000..39cfdf94
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/getprocaddress.py
@@ -0,0 +1,125 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE getprocaddress.py SCRIPT */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_string.h"
+#include "cr_version.h"
+#include "stub.h"
+#include "dri_glx.h"
+#if defined(VBOXOGL_DRI) || defined(VBOXOGL_FAKEDRI)
+#include "cr_gl.h"
+#include "fakedri_drv.h"
+#endif
+
+struct name_address {
+ const char *name;
+ CR_PROC address;
+};
+
+static struct name_address functions[] = {
+""")
+
+
+keys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+for func_name in keys:
+ if "Chromium" == apiutil.Category(func_name):
+ continue
+ if "VBox" == apiutil.Category(func_name):
+ continue
+ if func_name == "BoundsInfoCR":
+ continue
+ if "GL_chromium" == apiutil.Category(func_name):
+ pass #continue
+
+ wrap = apiutil.GetCategoryWrapper(func_name)
+ name = "gl" + func_name
+ address = "VBOXGLTAG(gl" + func_name + ")"
+ if wrap:
+ print('#ifdef CR_%s' % wrap)
+ print('\t{ "%s", (CR_PROC) %s },' % (name, address))
+ if wrap:
+ print('#endif')
+
+
+print("\t/* Chromium binding/glue functions */")
+
+for func_name in keys:
+ if (func_name == "Writeback" or
+ func_name == "BoundsInfoCR" or
+ func_name == "GetUniformsLocations" or
+ func_name == "GetAttribsLocations"):
+ continue
+ if apiutil.Category(func_name) == "Chromium":
+ print('\t{ "cr%s", (CR_PROC) cr%s },' % (func_name, func_name))
+
+
+print("""
+ { NULL, NULL }
+};
+
+CR_PROC CR_APIENTRY crGetProcAddress( const char *name )
+{
+ int i;
+ stubInit();
+
+ for (i = 0; functions[i].name; i++) {
+ if (crStrcmp(name, functions[i].name) == 0) {
+ return functions[i].address;
+ }
+ }
+
+
+#define GLXAPI_ENTRY(Func) if (!crStrcmp(name, "glX"#Func)) return (CR_PROC) &VBOXGLXENTRYTAG(glX##Func);
+#include "fakedri_glxfuncsList.h"
+#undef GLXAPI_ENTRY
+
+ /*CR_EXT_texture_from_pixmap*/
+ if (!crStrcmp(name, "glXBindTexImageEXT")) return (CR_PROC) VBOXGLXTAG(glXBindTexImageEXT);
+ if (!crStrcmp(name, "glXReleaseTexImageEXT")) return (CR_PROC) VBOXGLXTAG(glXReleaseTexImageEXT);
+
+#if defined(Linux) && defined(CR_EXT_framebuffer_blit)
+ /* Hacky way to make gnome3 happy on ubuntu 11.04, even though glBlitFramebuffer is part of OpenGL 3.0 spec,
+ * it expects to find glBlitFramebuffer and not glBlitFramebufferEXT after checking for EXT_framebuffer_blit support.
+ * Untill 3.0 support, it's better to go this way instead of adding an alias to src/VBox/GuestHost/OpenGL/glapi_parser/apispec.txt.
+ */
+ if (!crStrcmp(name, "glBlitFramebuffer")) return crGetProcAddress("glBlitFramebufferEXT");
+#endif
+
+ if (name) crDebug("Returning NULL for %s", name);
+ return NULL;
+}
+
+""")
+
+
+
+# XXX should crGetProcAddress really handle WGL/GLX functions???
+
+print_foo = """
+/* As these are Windows specific (i.e. wgl), define these now.... */
+#ifdef WINDOWS
+ {
+ wglGetExtensionsStringEXTFunc_t wglGetExtensionsStringEXT = NULL;
+ wglChoosePixelFormatFunc_t wglChoosePixelFormatEXT = NULL;
+ wglGetPixelFormatAttribivEXTFunc_t wglGetPixelFormatAttribivEXT = NULL;
+ wglGetPixelFormatAttribfvEXTFunc_t wglGetPixelFormatAttribfvEXT = NULL;
+ if (!crStrcmp(name, "wglGetExtensionsStringEXT")) return (CR_PROC) wglGetExtensionsStringEXT;
+ if (!crStrcmp(name, "wglChoosePixelFormatEXT")) return (CR_PROC) wglChoosePixelFormatEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribivEXT")) return (CR_PROC) wglGetPixelFormatAttribivEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribfvEXT")) return (CR_PROC) wglGetPixelFormatAttribfvEXT;
+ }
+#endif
+"""
diff --git a/src/VBox/Additions/common/crOpenGL/glx.c b/src/VBox/Additions/common/crOpenGL/glx.c
new file mode 100644
index 00000000..eb102877
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/glx.c
@@ -0,0 +1,2113 @@
+/* $Id: glx.c $ */
+/** @file
+ * VBox OpenGL GLX implementation
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ * Original copyright notice:
+ *
+ * Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+/* opengl_stub/glx.c */
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_spu.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "stub.h"
+#include "dri_glx.h"
+#include "GL/internal/glcore.h"
+#include "cr_glstate.h"
+
+#include <X11/Xregion.h>
+
+/* Force full pixmap update if there're more damaged regions than this number*/
+#define CR_MAX_DAMAGE_REGIONS_TRACKED 50
+
+/* Force "bigger" update (full or clip) if it's reducing number of regions updated
+ * but doesn't increase updated area more than given number
+ */
+#define CR_MIN_DAMAGE_PROFIT_SIZE 64*64
+
+/** @todo combine it in some other place*/
+/* Size of pack spu buffer - some delta for commands packing, see pack/packspu_config.c*/
+
+/** Ramshankar: Solaris compiz fix */
+#ifdef RT_OS_SOLARIS
+# define CR_MAX_TRANSFER_SIZE 20*1024*1024
+#else
+# define CR_MAX_TRANSFER_SIZE 4*1024*1024
+#endif
+
+/** For optimizing glXMakeCurrent */
+static Display *currentDisplay = NULL;
+static GLXDrawable currentDrawable = 0;
+static GLXDrawable currentReadDrawable = 0;
+
+static void stubXshmUpdateImageRect(Display *dpy, GLXDrawable draw, GLX_Pixmap_t *pGlxPixmap, XRectangle *pRect);
+static void stubQueryXDamageExtension(Display *dpy, ContextInfo *pContext);
+
+static bool isGLXVisual(Display *dpy, XVisualInfo *vis)
+{
+ return vis->visualid == XVisualIDFromVisual(DefaultVisual(dpy, vis->screen));
+}
+
+static GLXFBConfig fbConfigFromVisual(Display *dpy, XVisualInfo *vis)
+{
+ (void)dpy;
+
+ if (!isGLXVisual(dpy, vis))
+ return 0;
+ return (GLXFBConfig)vis->visualid;
+}
+
+static GLXFBConfig defaultFBConfigForScreen(Display *dpy, int screen)
+{
+ return (GLXFBConfig) XVisualIDFromVisual(DefaultVisual(dpy, screen));
+}
+
+static XVisualInfo *visualInfoFromFBConfig(Display *dpy, GLXFBConfig config)
+{
+ XVisualInfo info, *pret;
+ int nret;
+
+ info.visualid = (VisualID)config;
+ pret = XGetVisualInfo(dpy, VisualIDMask, &info, &nret);
+ if (nret == 1)
+ return pret;
+ XFree(pret);
+ return NULL;
+}
+
+DECLEXPORT(XVisualInfo *)
+VBOXGLXTAG(glXChooseVisual)( Display *dpy, int screen, int *attribList )
+{
+ bool useRGBA = false;
+ int *attrib;
+ XVisualInfo searchvis, *pret;
+ int nvisuals;
+ stubInit();
+
+ for (attrib = attribList; *attrib != None; attrib++)
+ {
+ switch (*attrib)
+ {
+ case GLX_USE_GL:
+ /* ignored, this is mandatory */
+ break;
+
+ case GLX_BUFFER_SIZE:
+ /* this is for color-index visuals, which we don't support */
+ attrib++;
+ break;
+
+ case GLX_LEVEL:
+ if (attrib[1] != 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_RGBA:
+ useRGBA = true;
+ break;
+
+ case GLX_STEREO:
+ goto err_exit;
+ /*
+ crWarning( "glXChooseVisual: stereo unsupported" );
+ return NULL;
+ */
+ break;
+
+ case GLX_AUX_BUFFERS:
+ if (attrib[1] != 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_RED_SIZE:
+ case GLX_GREEN_SIZE:
+ case GLX_BLUE_SIZE:
+ if (attrib[1] > 8)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_ALPHA_SIZE:
+ if (attrib[1] > 8)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_DEPTH_SIZE:
+ if (attrib[1] > 24)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_STENCIL_SIZE:
+ if (attrib[1] > 8)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_ACCUM_RED_SIZE:
+ case GLX_ACCUM_GREEN_SIZE:
+ case GLX_ACCUM_BLUE_SIZE:
+ case GLX_ACCUM_ALPHA_SIZE:
+ if (attrib[1] > 16)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_SAMPLE_BUFFERS_SGIS: /* aka GLX_SAMPLES_ARB */
+ if (attrib[1] > 0)
+ goto err_exit;
+ attrib++;
+ break;
+ case GLX_SAMPLES_SGIS: /* aka GLX_SAMPLES_ARB */
+ if (attrib[1] > 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_DOUBLEBUFFER: /* @todo, check if we support it */
+ break;
+
+#ifdef GLX_VERSION_1_3
+ case GLX_X_VISUAL_TYPE:
+ case GLX_TRANSPARENT_TYPE_EXT:
+ case GLX_TRANSPARENT_INDEX_VALUE_EXT:
+ case GLX_TRANSPARENT_RED_VALUE_EXT:
+ case GLX_TRANSPARENT_GREEN_VALUE_EXT:
+ case GLX_TRANSPARENT_BLUE_VALUE_EXT:
+ case GLX_TRANSPARENT_ALPHA_VALUE_EXT:
+ /* ignore */
+ crWarning("glXChooseVisual: ignoring attribute 0x%x", *attrib);
+ attrib++;
+ break;
+#endif
+
+ default:
+ crWarning( "glXChooseVisual: bad attrib=0x%x, ignoring", *attrib );
+ attrib++;
+ //return NULL;
+ }
+ }
+
+ if (!useRGBA)
+ return NULL;
+
+ XLOCK(dpy);
+ searchvis.visualid = XVisualIDFromVisual(DefaultVisual(dpy, screen));
+ pret = XGetVisualInfo(dpy, VisualIDMask, &searchvis, &nvisuals);
+ XUNLOCK(dpy);
+
+ if (nvisuals!=1) crWarning("glXChooseVisual: XGetVisualInfo returned %i visuals for %x", nvisuals, (unsigned int) searchvis.visualid);
+ if (pret)
+ crDebug("glXChooseVisual returned %x depth=%i", (unsigned int)pret->visualid, pret->depth);
+ return pret;
+
+err_exit:
+ crDebug("glXChooseVisual returning NULL, due to attrib=0x%x, next=0x%x", attrib[0], attrib[1]);
+ return NULL;
+}
+
+/**
+ ** There is a problem with glXCopyContext.
+ ** IRIX and Mesa both define glXCopyContext
+ ** to have the mask argument being a
+ ** GLuint. XFree 4 and oss.sgi.com
+ ** define it to be an unsigned long.
+ ** Solution: We don't support
+ ** glXCopyContext anyway so we'll just
+ ** \#ifdef out the code.
+ */
+DECLEXPORT(void)
+VBOXGLXTAG(glXCopyContext)( Display *dpy, GLXContext src, GLXContext dst,
+#if defined(AIX) || defined(PLAYSTATION2)
+GLuint mask
+#elif defined(SunOS)
+unsigned long mask
+#else
+unsigned long mask
+#endif
+)
+{
+ (void) dpy;
+ (void) src;
+ (void) dst;
+ (void) mask;
+ crWarning( "Unsupported GLX Call: glXCopyContext()" );
+}
+
+
+/**
+ * Get the display string for the given display pointer.
+ * Never return just ":0.0". In that case, prefix with our host name.
+ */
+static void
+stubGetDisplayString( Display *dpy, char *nameResult, int maxResult )
+{
+ const char *dpyName = DisplayString(dpy);
+ char host[1000];
+
+ host[0] = 0;
+ if (crStrlen(host) + crStrlen(dpyName) >= maxResult - 1)
+ {
+ /* return null string */
+ crWarning("Very long host / display name string in stubDisplayString!");
+ nameResult[0] = 0;
+ }
+ else
+ {
+ /* return host concatenated with dpyName */
+ crStrcpy(nameResult, host);
+ crStrcat(nameResult, dpyName);
+ }
+}
+
+
+
+DECLEXPORT(GLXContext)
+VBOXGLXTAG(glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, Bool direct)
+{
+ char dpyName[MAX_DPY_NAME];
+ ContextInfo *context;
+ int visBits = CR_RGB_BIT | CR_DOUBLE_BIT | CR_DEPTH_BIT; /* default vis */
+
+ (void)vis;
+ stubInit();
+
+ CRASSERT(stub.contextTable);
+
+ /*
+ {
+ int i, numExt;
+ char **list;
+
+ list = XListExtensions(dpy, &numExt);
+ crDebug("X extensions [%i]:", numExt);
+ for (i=0; i<numExt; ++i)
+ {
+ crDebug("%s", list[i]);
+ }
+ XFreeExtensionList(list);
+ }
+ */
+
+ stubGetDisplayString(dpy, dpyName, MAX_DPY_NAME);
+
+ context = stubNewContext(dpyName, visBits, UNDECIDED, (unsigned long) share);
+ if (!context)
+ return 0;
+
+ context->dpy = dpy;
+ context->direct = direct;
+
+ stubQueryXDamageExtension(dpy, context);
+
+ return (GLXContext) context->id;
+}
+
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyContext)( Display *dpy, GLXContext ctx )
+{
+ (void) dpy;
+ stubDestroyContext( (unsigned long) ctx );
+}
+
+typedef struct _stubFindPixmapParms_t {
+ ContextInfo *pCtx;
+ GLX_Pixmap_t *pGlxPixmap;
+ GLXDrawable draw;
+} stubFindPixmapParms_t;
+
+static void stubFindPixmapCB(unsigned long key, void *data1, void *data2)
+{
+ ContextInfo *pCtx = (ContextInfo *) data1;
+ stubFindPixmapParms_t *pParms = (stubFindPixmapParms_t *) data2;
+ GLX_Pixmap_t *pGlxPixmap = (GLX_Pixmap_t *) crHashtableSearch(pCtx->pGLXPixmapsHash, (unsigned int) pParms->draw);
+ (void)key;
+
+ if (pGlxPixmap)
+ {
+ pParms->pCtx = pCtx;
+ pParms->pGlxPixmap = pGlxPixmap;
+ }
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXMakeCurrent)( Display *dpy, GLXDrawable drawable, GLXContext ctx )
+{
+ ContextInfo *context;
+ WindowInfo *window;
+ Bool retVal;
+
+ /*crDebug("glXMakeCurrent(%p, 0x%x, 0x%x)", (void *) dpy, (int) drawable, (int) ctx);*/
+
+ /*check if passed drawable is GLXPixmap and not X Window*/
+ if (drawable)
+ {
+ GLX_Pixmap_t *pGlxPixmap = (GLX_Pixmap_t *) crHashtableSearch(stub.pGLXPixmapsHash, (unsigned int) drawable);
+
+ if (!pGlxPixmap)
+ {
+ stubFindPixmapParms_t parms;
+ parms.pGlxPixmap = NULL;
+ parms.draw = drawable;
+ crHashtableWalk(stub.contextTable, stubFindPixmapCB, &parms);
+ pGlxPixmap = parms.pGlxPixmap;
+ }
+
+ if (pGlxPixmap)
+ {
+ /** @todo */
+ crWarning("Unimplemented glxMakeCurrent call with GLXPixmap passed, unexpected things might happen.");
+ }
+ }
+
+ if (ctx && drawable)
+ {
+ crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) ctx);
+ window = stubGetWindowInfo(dpy, drawable);
+
+ if (context && context->type == UNDECIDED) {
+ XLOCK(dpy);
+ XSync(dpy, 0); /* sync to force window creation on the server */
+ XUNLOCK(dpy);
+ }
+ }
+ else
+ {
+ dpy = NULL;
+ window = NULL;
+ context = NULL;
+ }
+
+ currentDisplay = dpy;
+ currentDrawable = drawable;
+
+ retVal = stubMakeCurrent(window, context);
+
+ if (ctx && drawable)
+ {
+ crHashtableUnlock(stub.contextTable);
+ crHashtableUnlock(stub.windowTable);
+ }
+
+ return retVal;
+}
+
+DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreateGLXPixmap)( Display *dpy, XVisualInfo *vis, Pixmap pixmap )
+{
+ stubInit();
+ return VBOXGLXTAG(glXCreatePixmap)(dpy, fbConfigFromVisual(dpy, vis), pixmap, NULL);
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyGLXPixmap)( Display *dpy, GLXPixmap pix )
+{
+ VBOXGLXTAG(glXDestroyPixmap)(dpy, pix);
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXGetConfig)( Display *dpy, XVisualInfo *vis, int attrib, int *value )
+{
+ if (!vis) {
+ /* SGI OpenGL Performer hits this */
+ crWarning("glXGetConfig called with NULL XVisualInfo");
+ return GLX_BAD_VISUAL;
+ }
+
+ stubInit();
+
+ *value = 0; /* For sanity */
+
+ switch ( attrib ) {
+
+ case GLX_USE_GL:
+ *value = isGLXVisual(dpy, vis);
+ break;
+
+ case GLX_BUFFER_SIZE:
+ *value = 32;
+ break;
+
+ case GLX_LEVEL:
+ *value = 0; /* for now */
+ break;
+
+ case GLX_RGBA:
+ *value = 1;
+ break;
+
+ case GLX_DOUBLEBUFFER:
+ *value = 1;
+ break;
+
+ case GLX_STEREO:
+ *value = 1;
+ break;
+
+ case GLX_AUX_BUFFERS:
+ *value = 0;
+ break;
+
+ case GLX_RED_SIZE:
+ *value = 8;
+ break;
+
+ case GLX_GREEN_SIZE:
+ *value = 8;
+ break;
+
+ case GLX_BLUE_SIZE:
+ *value = 8;
+ break;
+
+ case GLX_ALPHA_SIZE:
+ *value = 8;
+ break;
+
+ case GLX_DEPTH_SIZE:
+ *value = 24;
+ break;
+
+ case GLX_STENCIL_SIZE:
+ *value = 8;
+ break;
+
+ case GLX_ACCUM_RED_SIZE:
+ *value = 16;
+ break;
+
+ case GLX_ACCUM_GREEN_SIZE:
+ *value = 16;
+ break;
+
+ case GLX_ACCUM_BLUE_SIZE:
+ *value = 16;
+ break;
+
+ case GLX_ACCUM_ALPHA_SIZE:
+ *value = 16;
+ break;
+
+ case GLX_SAMPLE_BUFFERS_SGIS:
+ *value = 0; /* fix someday */
+ break;
+
+ case GLX_SAMPLES_SGIS:
+ *value = 0; /* fix someday */
+ break;
+
+ case GLX_VISUAL_CAVEAT_EXT:
+ *value = GLX_NONE_EXT;
+ break;
+#if defined(SunOS) || 1
+ /*
+ I don't think this is even a valid attribute for glxGetConfig.
+ No idea why this gets called under SunOS but we simply ignore it
+ -- jw
+ */
+ case GLX_X_VISUAL_TYPE:
+ crWarning ("Ignoring Unsupported GLX Call: glxGetConfig with attrib 0x%x", attrib);
+ break;
+#endif
+
+ case GLX_TRANSPARENT_TYPE:
+ *value = GLX_NONE_EXT;
+ break;
+ case GLX_TRANSPARENT_INDEX_VALUE:
+ *value = 0;
+ break;
+ case GLX_TRANSPARENT_RED_VALUE:
+ *value = 0;
+ break;
+ case GLX_TRANSPARENT_GREEN_VALUE:
+ *value = 0;
+ break;
+ case GLX_TRANSPARENT_BLUE_VALUE:
+ *value = 0;
+ break;
+ case GLX_TRANSPARENT_ALPHA_VALUE:
+ *value = 0;
+ break;
+ case GLX_DRAWABLE_TYPE:
+ *value = GLX_WINDOW_BIT;
+ break;
+ default:
+ crWarning( "Unsupported GLX Call: glXGetConfig with attrib 0x%x, ignoring...", attrib );
+ //return GLX_BAD_ATTRIBUTE;
+ *value = 0;
+ }
+
+ return 0;
+}
+
+DECLEXPORT(GLXContext) VBOXGLXTAG(glXGetCurrentContext)( void )
+{
+ ContextInfo *context = stubGetCurrentContext();
+ if (context)
+ return (GLXContext) context->id;
+ else
+ return (GLXContext) NULL;
+}
+
+DECLEXPORT(GLXDrawable) VBOXGLXTAG(glXGetCurrentDrawable)(void)
+{
+ return currentDrawable;
+}
+
+DECLEXPORT(Display *) VBOXGLXTAG(glXGetCurrentDisplay)(void)
+{
+ return currentDisplay;
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXIsDirect)(Display *dpy, GLXContext ctx)
+{
+ (void) dpy;
+ (void) ctx;
+ crDebug("->glXIsDirect");
+ return True;
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXQueryExtension)(Display *dpy, int *errorBase, int *eventBase)
+{
+ (void) dpy;
+ (void) errorBase;
+ (void) eventBase;
+ return 1; /* You BET we do... */
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXQueryVersion)( Display *dpy, int *major, int *minor )
+{
+ (void) dpy;
+ *major = 1;
+ *minor = 3;
+ return 1;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXSwapBuffers)( Display *dpy, GLXDrawable drawable )
+{
+ WindowInfo *window = stubGetWindowInfo(dpy, drawable);
+ stubSwapBuffers( window, 0 );
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXUseXFont)( Font font, int first, int count, int listBase )
+{
+ ContextInfo *context = stubGetCurrentContext();
+ Display *dpy = context->dpy;
+ if (dpy) {
+ stubUseXFont( dpy, font, first, count, listBase );
+ }
+ else {
+ dpy = XOpenDisplay(NULL);
+ if (!dpy)
+ return;
+ stubUseXFont( dpy, font, first, count, listBase );
+ XCloseDisplay(dpy);
+ }
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXWaitGL)( void )
+{
+ static int first_call = 1;
+
+ if ( first_call )
+ {
+ crDebug( "Ignoring unsupported GLX call: glXWaitGL()" );
+ first_call = 0;
+ }
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXWaitX)( void )
+{
+ static int first_call = 1;
+
+ if ( first_call )
+ {
+ crDebug( "Ignoring unsupported GLX call: glXWaitX()" );
+ first_call = 0;
+ }
+}
+
+DECLEXPORT(const char *) VBOXGLXTAG(glXQueryExtensionsString)( Display *dpy, int screen )
+{
+ /* XXX maybe also advertise GLX_SGIS_multisample? */
+
+ static const char *retval = "GLX_ARB_multisample GLX_EXT_texture_from_pixmap GLX_SGIX_fbconfig GLX_ARB_get_proc_address";
+
+ (void) dpy;
+ (void) screen;
+
+ crDebug("->glXQueryExtensionsString");
+ return retval;
+}
+
+DECLEXPORT(const char *) VBOXGLXTAG(glXGetClientString)( Display *dpy, int name )
+{
+ const char *retval;
+ (void) dpy;
+ (void) name;
+
+ switch ( name ) {
+
+ case GLX_VENDOR:
+ retval = "Chromium";
+ break;
+
+ case GLX_VERSION:
+ retval = "1.3 Chromium";
+ break;
+
+ case GLX_EXTENSIONS:
+ /** @todo should be a screen not a name...but it's not used anyway*/
+ retval = glXQueryExtensionsString(dpy, name);
+ break;
+
+ default:
+ retval = NULL;
+ }
+
+ return retval;
+}
+
+DECLEXPORT(const char *) VBOXGLXTAG(glXQueryServerString)( Display *dpy, int screen, int name )
+{
+ const char *retval;
+ (void) dpy;
+ (void) screen;
+
+ switch ( name ) {
+
+ case GLX_VENDOR:
+ retval = "Chromium";
+ break;
+
+ case GLX_VERSION:
+ retval = "1.3 Chromium";
+ break;
+
+ case GLX_EXTENSIONS:
+ retval = glXQueryExtensionsString(dpy, screen);
+ break;
+
+ default:
+ retval = NULL;
+ }
+
+ return retval;
+}
+
+DECLEXPORT(CR_GLXFuncPtr) VBOXGLXTAG(glXGetProcAddressARB)( const GLubyte *name )
+{
+ return (CR_GLXFuncPtr) crGetProcAddress( (const char *) name );
+}
+
+DECLEXPORT(CR_GLXFuncPtr) VBOXGLXTAG(glXGetProcAddress)( const GLubyte *name )
+{
+ return (CR_GLXFuncPtr) crGetProcAddress( (const char *) name );
+}
+
+
+#if GLX_EXTRAS
+
+DECLEXPORT(GLXPbufferSGIX)
+VBOXGLXTAG(glXCreateGLXPbufferSGIX)(Display *dpy, GLXFBConfigSGIX config,
+ unsigned int width, unsigned int height,
+ int *attrib_list)
+{
+ (void) dpy;
+ (void) config;
+ (void) width;
+ (void) height;
+ (void) attrib_list;
+ crWarning("glXCreateGLXPbufferSGIX not implemented by Chromium");
+ return 0;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf)
+{
+ (void) dpy;
+ (void) pbuf;
+ crWarning("glXDestroyGLXPbufferSGIX not implemented by Chromium");
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXSelectEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long mask)
+{
+ (void) dpy;
+ (void) drawable;
+ (void) mask;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXGetSelectedEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long *mask)
+{
+ (void) dpy;
+ (void) drawable;
+ (void) mask;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXQueryGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf,
+ int attribute, unsigned int *value)
+{
+ (void) dpy;
+ (void) pbuf;
+ (void) attribute;
+ (void) value;
+ crWarning("glXQueryGLXPbufferSGIX not implemented by Chromium");
+ return 0;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttribSGIX)(Display *dpy, GLXFBConfig config,
+ int attribute, int *value)
+{
+ return VBOXGLXTAG(glXGetFBConfigAttrib)(dpy, config, attribute, value);
+}
+
+DECLEXPORT(GLXFBConfigSGIX *)
+VBOXGLXTAG(glXChooseFBConfigSGIX)(Display *dpy, int screen,
+ int *attrib_list, int *nelements)
+{
+ return VBOXGLXTAG(glXChooseFBConfig)(dpy, screen, attrib_list, nelements);
+}
+
+DECLEXPORT(GLXPixmap)
+VBOXGLXTAG(glXCreateGLXPixmapWithConfigSGIX)(Display *dpy,
+ GLXFBConfig config,
+ Pixmap pixmap)
+{
+ return VBOXGLXTAG(glXCreatePixmap)(dpy, config, pixmap, NULL);
+}
+
+DECLEXPORT(GLXContext)
+VBOXGLXTAG(glXCreateContextWithConfigSGIX)(Display *dpy, GLXFBConfig config,
+ int render_type,
+ GLXContext share_list,
+ Bool direct)
+{
+ (void)config;
+ if (render_type!=GLX_RGBA_TYPE_SGIX)
+ {
+ crWarning("glXCreateContextWithConfigSGIX: Unsupported render type %i", render_type);
+ return NULL;
+ }
+ return VBOXGLXTAG(glXCreateContext)(dpy, NULL, share_list, direct);
+}
+
+DECLEXPORT(XVisualInfo *)
+VBOXGLXTAG(glXGetVisualFromFBConfigSGIX)(Display *dpy,
+ GLXFBConfig config)
+{
+ return visualInfoFromFBConfig(dpy, config);
+}
+
+DECLEXPORT(GLXFBConfigSGIX)
+VBOXGLXTAG(glXGetFBConfigFromVisualSGIX)(Display *dpy, XVisualInfo *vis)
+{
+ if (!vis)
+ {
+ return NULL;
+ }
+ /*Note: Caller is supposed to call XFree on returned value, so can't just return (GLXFBConfig)vis->visualid*/
+ return (GLXFBConfigSGIX) visualInfoFromFBConfig(dpy, fbConfigFromVisual(dpy, vis));
+}
+
+/*
+ * GLX 1.3 functions
+ */
+DECLEXPORT(GLXFBConfig *)
+VBOXGLXTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list, int *nelements)
+{
+ ATTRIB_TYPE *attrib;
+ intptr_t fbconfig = 0;
+
+ stubInit();
+
+ if (!attrib_list)
+ {
+ return VBOXGLXTAG(glXGetFBConfigs)(dpy, screen, nelements);
+ }
+
+ for (attrib = attrib_list; *attrib != None; attrib++)
+ {
+ switch (*attrib)
+ {
+ case GLX_FBCONFIG_ID:
+ fbconfig = attrib[1];
+ attrib++;
+ break;
+
+ case GLX_BUFFER_SIZE:
+ /* this is ignored except for color-index visuals, which we don't support */
+ attrib++;
+ break;
+
+ case GLX_LEVEL:
+ if (attrib[1] != 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_AUX_BUFFERS:
+ if (attrib[1] != 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_DOUBLEBUFFER: /* @todo, check if we support it */
+ attrib++;
+ break;
+
+ case GLX_STEREO:
+ if (attrib[1] != 0)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_RED_SIZE:
+ case GLX_GREEN_SIZE:
+ case GLX_BLUE_SIZE:
+ case GLX_ALPHA_SIZE:
+ if (attrib[1] > 8)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_DEPTH_SIZE:
+ if (attrib[1] > 24)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_STENCIL_SIZE:
+ if (attrib[1] > 8)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_ACCUM_RED_SIZE:
+ case GLX_ACCUM_GREEN_SIZE:
+ case GLX_ACCUM_BLUE_SIZE:
+ case GLX_ACCUM_ALPHA_SIZE:
+ if (attrib[1] > 16)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_X_RENDERABLE:
+ case GLX_CONFIG_CAVEAT:
+ attrib++;
+ break;
+
+ case GLX_RENDER_TYPE:
+ if (attrib[1]!=GLX_RGBA_BIT)
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_DRAWABLE_TYPE:
+ if ( !(attrib[1] & GLX_WINDOW_BIT)
+ && !(attrib[1] & GLX_PIXMAP_BIT))
+ goto err_exit;
+ attrib++;
+ break;
+
+ case GLX_X_VISUAL_TYPE:
+ case GLX_TRANSPARENT_TYPE_EXT:
+ case GLX_TRANSPARENT_INDEX_VALUE_EXT:
+ case GLX_TRANSPARENT_RED_VALUE_EXT:
+ case GLX_TRANSPARENT_GREEN_VALUE_EXT:
+ case GLX_TRANSPARENT_BLUE_VALUE_EXT:
+ case GLX_TRANSPARENT_ALPHA_VALUE_EXT:
+ /* ignore */
+ crWarning("glXChooseVisual: ignoring attribute 0x%x", *attrib);
+ attrib++;
+ break;
+
+ break;
+ default:
+ crWarning( "glXChooseVisual: bad attrib=0x%x, ignoring", *attrib );
+ attrib++;
+ break;
+ }
+ }
+
+ if (fbconfig)
+ {
+ GLXFBConfig *pGLXFBConfigs;
+
+ *nelements = 1;
+ pGLXFBConfigs = (GLXFBConfig *) crAlloc(*nelements * sizeof(GLXFBConfig));
+ pGLXFBConfigs[0] = (GLXFBConfig)fbconfig;
+ return pGLXFBConfigs;
+ }
+ else
+ {
+ return VBOXGLXTAG(glXGetFBConfigs)(dpy, screen, nelements);
+ }
+
+err_exit:
+ crWarning("glXChooseFBConfig returning NULL, due to attrib=0x%x, next=0x%x", attrib[0], attrib[1]);
+ return NULL;
+}
+
+DECLEXPORT(GLXContext)
+VBOXGLXTAG(glXCreateNewContext)(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
+{
+ (void) dpy;
+ (void) config;
+ (void) render_type;
+ (void) share_list;
+ (void) direct;
+
+ if (render_type != GLX_RGBA_TYPE)
+ {
+ crWarning("glXCreateNewContext, unsupported render_type %x", render_type);
+ return NULL;
+ }
+
+ return VBOXGLXTAG(glXCreateContext)(dpy, NULL, share_list, direct);
+}
+
+DECLEXPORT(GLXPbuffer)
+VBOXGLXTAG(glXCreatePbuffer)(Display *dpy, GLXFBConfig config, ATTRIB_TYPE *attrib_list)
+{
+ (void) dpy;
+ (void) config;
+ (void) attrib_list;
+ crWarning("glXCreatePbuffer not implemented by Chromium");
+ return 0;
+}
+
+/* Note: there're examples where glxpixmaps are created without current context, so can't do much of the work here.
+ * Instead we'd do necessary initialization on first use of those pixmaps.
+ */
+DECLEXPORT(GLXPixmap)
+VBOXGLXTAG(glXCreatePixmap)(Display *dpy, GLXFBConfig config, Pixmap pixmap, ATTRIB_TYPE *attrib_list)
+{
+ ATTRIB_TYPE *attrib;
+ GLX_Pixmap_t *pGlxPixmap;
+ (void) dpy;
+ (void) config;
+
+#if 0
+ {
+ int x, y;
+ unsigned int w, h;
+ unsigned int border;
+ unsigned int depth;
+ Window root;
+
+ crDebug("glXCreatePixmap called for %lu", pixmap);
+
+ XLOCK(dpy);
+ if (!XGetGeometry(dpy, pixmap, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ XSync(dpy, False);
+ if (!XGetGeometry(dpy, pixmap, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ crDebug("fail");
+ }
+ }
+ crDebug("root: %lu, [%i,%i %u,%u]", root, x, y, w, h);
+ XUNLOCK(dpy);
+ }
+#endif
+
+ pGlxPixmap = crCalloc(sizeof(GLX_Pixmap_t));
+ if (!pGlxPixmap)
+ {
+ crWarning("glXCreatePixmap failed to allocate memory");
+ return 0;
+ }
+
+ pGlxPixmap->format = GL_RGBA;
+ pGlxPixmap->target = GL_TEXTURE_2D;
+
+ if (attrib_list)
+ {
+ for (attrib = attrib_list; *attrib != None; attrib++)
+ {
+ switch (*attrib)
+ {
+ case GLX_TEXTURE_FORMAT_EXT:
+ attrib++;
+ switch (*attrib)
+ {
+ case GLX_TEXTURE_FORMAT_RGBA_EXT:
+ pGlxPixmap->format = GL_RGBA;
+ break;
+ case GLX_TEXTURE_FORMAT_RGB_EXT:
+ pGlxPixmap->format = GL_RGB;
+ break;
+ default:
+ crDebug("Unexpected GLX_TEXTURE_FORMAT_EXT 0x%x", (unsigned int) *attrib);
+ }
+ break;
+ case GLX_TEXTURE_TARGET_EXT:
+ attrib++;
+ switch (*attrib)
+ {
+ case GLX_TEXTURE_2D_EXT:
+ pGlxPixmap->target = GL_TEXTURE_2D;
+ break;
+ case GLX_TEXTURE_RECTANGLE_EXT:
+ pGlxPixmap->target = GL_TEXTURE_RECTANGLE_NV;
+ break;
+ default:
+ crDebug("Unexpected GLX_TEXTURE_TARGET_EXT 0x%x", (unsigned int) *attrib);
+ }
+ break;
+ default: attrib++;
+ }
+ }
+ }
+
+ crHashtableAdd(stub.pGLXPixmapsHash, (unsigned int) pixmap, pGlxPixmap);
+ return (GLXPixmap) pixmap;
+}
+
+DECLEXPORT(GLXWindow)
+VBOXGLXTAG(glXCreateWindow)(Display *dpy, GLXFBConfig config, Window win, ATTRIB_TYPE *attrib_list)
+{
+ GLXFBConfig *realcfg;
+ int nconfigs;
+ (void) config;
+
+ if (stub.wsInterface.glXGetFBConfigs)
+ {
+ realcfg = stub.wsInterface.glXGetFBConfigs(dpy, 0, &nconfigs);
+ if (!realcfg || nconfigs<1)
+ {
+ crWarning("glXCreateWindow !realcfg || nconfigs<1");
+ return 0;
+ }
+ else
+ {
+ return stub.wsInterface.glXCreateWindow(dpy, realcfg[0], win, attrib_list);
+ }
+ }
+ else
+ {
+ if (attrib_list && *attrib_list!=None)
+ {
+ crWarning("Non empty attrib list in glXCreateWindow");
+ return 0;
+ }
+ return (GLXWindow)win;
+ }
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyPbuffer)(Display *dpy, GLXPbuffer pbuf)
+{
+ (void) dpy;
+ (void) pbuf;
+ crWarning("glXDestroyPbuffer not implemented by Chromium");
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyPixmap)(Display *dpy, GLXPixmap pixmap)
+{
+ stubFindPixmapParms_t parms;
+
+ if (crHashtableSearch(stub.pGLXPixmapsHash, (unsigned int) pixmap))
+ {
+ /*it's valid but never used glxpixmap, so simple free stored ptr*/
+ crHashtableDelete(stub.pGLXPixmapsHash, (unsigned int) pixmap, crFree);
+ return;
+ }
+ else
+ {
+ /*it's either invalid glxpixmap or one which was already initialized, so it's stored in appropriate ctx hash*/
+ parms.pCtx = NULL;
+ parms.pGlxPixmap = NULL;
+ parms.draw = pixmap;
+ crHashtableWalk(stub.contextTable, stubFindPixmapCB, &parms);
+ }
+
+ if (!parms.pGlxPixmap)
+ {
+ crWarning("glXDestroyPixmap called for unknown glxpixmap 0x%x", (unsigned int) pixmap);
+ return;
+ }
+
+ XLOCK(dpy);
+ if (parms.pGlxPixmap->gc)
+ {
+ XFreeGC(dpy, parms.pGlxPixmap->gc);
+ }
+
+ if (parms.pGlxPixmap->hShmPixmap>0)
+ {
+ XFreePixmap(dpy, parms.pGlxPixmap->hShmPixmap);
+ }
+ XUNLOCK(dpy);
+
+ if (parms.pGlxPixmap->hDamage>0)
+ {
+ //crDebug("Destroy: Damage for drawable 0x%x, handle 0x%x", (unsigned int) pixmap, (unsigned int) parms.pGlxPixmap->damage);
+ XDamageDestroy(dpy, parms.pGlxPixmap->hDamage);
+ }
+
+ if (parms.pGlxPixmap->pDamageRegion)
+ {
+ XDestroyRegion(parms.pGlxPixmap->pDamageRegion);
+ }
+
+ crHashtableDelete(parms.pCtx->pGLXPixmapsHash, (unsigned int) pixmap, crFree);
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXDestroyWindow)(Display *dpy, GLXWindow win)
+{
+ (void) dpy;
+ (void) win;
+ /*crWarning("glXDestroyWindow not implemented by Chromium");*/
+}
+
+DECLEXPORT(GLXDrawable) VBOXGLXTAG(glXGetCurrentReadDrawable)(void)
+{
+ return currentReadDrawable;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int *value)
+{
+ XVisualInfo * pVisual;
+ const char * pExt;
+
+ switch (attribute)
+ {
+ case GLX_DRAWABLE_TYPE:
+ *value = GLX_PIXMAP_BIT | GLX_WINDOW_BIT;
+ break;
+ case GLX_BIND_TO_TEXTURE_TARGETS_EXT:
+ *value = GLX_TEXTURE_2D_BIT_EXT;
+ pExt = (const char *) stub.spu->dispatch_table.GetString(GL_EXTENSIONS);
+ if (crStrstr(pExt, "GL_NV_texture_rectangle")
+ || crStrstr(pExt, "GL_ARB_texture_rectangle")
+ || crStrstr(pExt, "GL_EXT_texture_rectangle"))
+ {
+ *value |= GLX_TEXTURE_RECTANGLE_BIT_EXT;
+ }
+ break;
+ case GLX_BIND_TO_TEXTURE_RGBA_EXT:
+ *value = True;
+ break;
+ case GLX_BIND_TO_TEXTURE_RGB_EXT:
+ *value = True;
+ break;
+ case GLX_DOUBLEBUFFER:
+ //crDebug("attribute=GLX_DOUBLEBUFFER");
+ *value = True;
+ break;
+ case GLX_Y_INVERTED_EXT:
+ *value = True;
+ break;
+ case GLX_ALPHA_SIZE:
+ //crDebug("attribute=GLX_ALPHA_SIZE");
+ *value = 8;
+ break;
+ case GLX_BUFFER_SIZE:
+ //crDebug("attribute=GLX_BUFFER_SIZE");
+ *value = 32;
+ break;
+ case GLX_STENCIL_SIZE:
+ //crDebug("attribute=GLX_STENCIL_SIZE");
+ *value = 8;
+ break;
+ case GLX_DEPTH_SIZE:
+ *value = 24;
+ //crDebug("attribute=GLX_DEPTH_SIZE");
+ break;
+ case GLX_BIND_TO_MIPMAP_TEXTURE_EXT:
+ *value = 0;
+ break;
+ case GLX_RENDER_TYPE:
+ //crDebug("attribute=GLX_RENDER_TYPE");
+ *value = GLX_RGBA_BIT;
+ break;
+ case GLX_CONFIG_CAVEAT:
+ //crDebug("attribute=GLX_CONFIG_CAVEAT");
+ *value = GLX_NONE;
+ break;
+ case GLX_VISUAL_ID:
+ //crDebug("attribute=GLX_VISUAL_ID");
+ pVisual = visualInfoFromFBConfig(dpy, config);
+ if (!pVisual)
+ {
+ crWarning("glXGetFBConfigAttrib for %p, failed to get XVisualInfo", config);
+ return GLX_BAD_ATTRIBUTE;
+ }
+ *value = pVisual->visualid;
+ XFree(pVisual);
+ break;
+ case GLX_FBCONFIG_ID:
+ *value = (int)(intptr_t)config;
+ break;
+ case GLX_RED_SIZE:
+ case GLX_GREEN_SIZE:
+ case GLX_BLUE_SIZE:
+ *value = 8;
+ break;
+ case GLX_LEVEL:
+ *value = 0;
+ break;
+ case GLX_STEREO:
+ *value = false;
+ break;
+ case GLX_AUX_BUFFERS:
+ *value = 0;
+ break;
+ case GLX_ACCUM_RED_SIZE:
+ case GLX_ACCUM_GREEN_SIZE:
+ case GLX_ACCUM_BLUE_SIZE:
+ case GLX_ACCUM_ALPHA_SIZE:
+ *value = 0;
+ break;
+ case GLX_X_VISUAL_TYPE:
+ *value = GLX_TRUE_COLOR;
+ break;
+ case GLX_TRANSPARENT_TYPE:
+ *value = GLX_NONE;
+ break;
+ case GLX_SAMPLE_BUFFERS:
+ case GLX_SAMPLES:
+ *value = 1;
+ break;
+ case GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT:
+ *value = 0;
+ break;
+ default:
+ crDebug("glXGetFBConfigAttrib: unknown attribute=0x%x", attribute);
+ return GLX_BAD_ATTRIBUTE;
+ }
+
+ return Success;
+}
+
+DECLEXPORT(GLXFBConfig *) VBOXGLXTAG(glXGetFBConfigs)(Display *dpy, int screen, int *nelements)
+{
+ int i;
+
+ GLXFBConfig *pGLXFBConfigs = crAlloc(sizeof(GLXFBConfig));
+
+ *nelements = 1;
+ XLOCK(dpy);
+ *pGLXFBConfigs = defaultFBConfigForScreen(dpy, screen);
+ XUNLOCK(dpy);
+
+ crDebug("glXGetFBConfigs returned %i configs", *nelements);
+ for (i=0; i<*nelements; ++i)
+ {
+ crDebug("glXGetFBConfigs[%i]=0x%x", i, (unsigned)(uintptr_t) pGLXFBConfigs[i]);
+ }
+ return pGLXFBConfigs;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXGetSelectedEvent)(Display *dpy, GLXDrawable draw, unsigned long *event_mask)
+{
+ (void) dpy;
+ (void) draw;
+ (void) event_mask;
+ crWarning("glXGetSelectedEvent not implemented by Chromium");
+}
+
+DECLEXPORT(XVisualInfo *) VBOXGLXTAG(glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config)
+{
+ return visualInfoFromFBConfig(dpy, config);
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXMakeContextCurrent)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx)
+{
+ currentReadDrawable = read;
+ return VBOXGLXTAG(glXMakeCurrent)(display, draw, ctx);
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXQueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value)
+{
+ (void) dpy;
+ (void) ctx;
+ (void) attribute;
+ (void) value;
+ crWarning("glXQueryContext not implemented by Chromium");
+ return 0;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXQueryDrawable)(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value)
+{
+ (void) dpy;
+ (void) draw;
+ (void) attribute;
+ (void) value;
+ crWarning("glXQueryDrawable not implemented by Chromium");
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXSelectEvent)(Display *dpy, GLXDrawable draw, unsigned long event_mask)
+{
+ (void) dpy;
+ (void) draw;
+ (void) event_mask;
+ crWarning("glXSelectEvent not implemented by Chromium");
+}
+
+#ifdef CR_EXT_texture_from_pixmap
+/*typedef struct
+{
+ int x, y;
+ unsigned int w, h, border, depth;
+ Window root;
+ void *data;
+} pminfo;*/
+
+static void stubInitXSharedMemory(Display *dpy)
+{
+ int vma, vmi;
+ Bool pixmaps;
+
+ if (stub.bShmInitFailed || stub.xshmSI.shmid>=0)
+ return;
+
+ stub.bShmInitFailed = GL_TRUE;
+
+ /* Check for extension and pixmaps format */
+ XLOCK(dpy);
+ if (!XShmQueryExtension(dpy))
+ {
+ crWarning("No XSHM extension");
+ XUNLOCK(dpy);
+ return;
+ }
+
+ if (!XShmQueryVersion(dpy, &vma, &vmi, &pixmaps) || !pixmaps)
+ {
+ crWarning("XSHM extension doesn't support pixmaps");
+ XUNLOCK(dpy);
+ return;
+ }
+
+ if (XShmPixmapFormat(dpy)!=ZPixmap)
+ {
+ crWarning("XSHM extension doesn't support ZPixmap format");
+ XUNLOCK(dpy);
+ return;
+ }
+ XUNLOCK(dpy);
+
+ /* Alloc shared memory, so far using hardcoded value...could fail for bigger displays one day */
+ stub.xshmSI.readOnly = false;
+ stub.xshmSI.shmid = shmget(IPC_PRIVATE, 4*4096*2048, IPC_CREAT | 0600);
+ if (stub.xshmSI.shmid<0)
+ {
+ crWarning("XSHM Failed to create shared segment");
+ return;
+ }
+
+ stub.xshmSI.shmaddr = (char*) shmat(stub.xshmSI.shmid, NULL, 0);
+ if (stub.xshmSI.shmaddr==(void*)-1)
+ {
+ crWarning("XSHM Failed to attach shared segment");
+ shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
+ return;
+ }
+
+ XLOCK(dpy);
+ if (!XShmAttach(dpy, &stub.xshmSI))
+ {
+ crWarning("XSHM Failed to attach shared segment to XServer");
+ shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
+ shmdt(stub.xshmSI.shmaddr);
+ XUNLOCK(dpy);
+ return;
+ }
+ XUNLOCK(dpy);
+
+ stub.bShmInitFailed = GL_FALSE;
+ crInfo("Using XSHM for GLX_EXT_texture_from_pixmap");
+
+ /*Anyway mark to be deleted when our process detaches it, in case of segfault etc*/
+
+/* Ramshankar: Solaris compiz fix */
+#ifndef RT_OS_SOLARIS
+ shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
+#endif
+}
+
+void stubQueryXDamageExtension(Display *dpy, ContextInfo *pContext)
+{
+ int erb, vma, vmi;
+
+ CRASSERT(pContext);
+
+ if (pContext->damageQueryFailed)
+ return;
+
+ pContext->damageQueryFailed = True;
+
+ if (!XDamageQueryExtension(dpy, &pContext->damageEventsBase, &erb)
+ || !XDamageQueryVersion(dpy, &vma, &vmi))
+ {
+ crWarning("XDamage not found or old version (%i.%i), going to run *very* slow", vma, vmi);
+ return;
+ }
+
+ crDebug("XDamage %i.%i", vma, vmi);
+ pContext->damageQueryFailed = False;
+}
+
+static void stubFetchDamageOnDrawable(Display *dpy, GLX_Pixmap_t *pGlxPixmap)
+{
+ Damage damage = pGlxPixmap->hDamage;
+
+ if (damage)
+ {
+ XRectangle *returnRects;
+ int nReturnRects;
+
+ /* Get the damage region as a server region */
+ XserverRegion serverDamageRegion = XFixesCreateRegion (dpy, NULL, 0);
+
+ /* Unite damage region with server region and clear damage region */
+ XDamageSubtract (dpy,
+ damage,
+ None, /* subtract all damage from this region */
+ serverDamageRegion /* save in serverDamageRegion */);
+
+ /* Fetch damage rectangles */
+ returnRects = XFixesFetchRegion (dpy, serverDamageRegion, &nReturnRects);
+
+ /* Delete region */
+ XFixesDestroyRegion (dpy, serverDamageRegion);
+
+ if (pGlxPixmap->pDamageRegion)
+ {
+ /* If it's dirty and regions are empty, it marked for full update, so do nothing.*/
+ if (!pGlxPixmap->bPixmapImageDirty || !XEmptyRegion(pGlxPixmap->pDamageRegion))
+ {
+ int i = 0;
+ for (; i < nReturnRects; ++i)
+ {
+ if (CR_MAX_DAMAGE_REGIONS_TRACKED <= pGlxPixmap->pDamageRegion->numRects)
+ {
+ /* Mark for full update */
+ EMPTY_REGION(pGlxPixmap->pDamageRegion);
+ }
+ else
+ {
+ /* Add to damage regions */
+ XUnionRectWithRegion(&returnRects[i], pGlxPixmap->pDamageRegion, pGlxPixmap->pDamageRegion);
+ }
+ }
+ }
+ }
+
+ XFree(returnRects);
+
+ pGlxPixmap->bPixmapImageDirty = True;
+ }
+}
+
+static const CRPixelPackState defaultPacking =
+{
+ 0, /*rowLength*/
+ 0, /*skipRows*/
+ 0, /*skipPixels*/
+ 1, /*alignment*/
+ 0, /*imageHeight*/
+ 0, /*skipImages*/
+ GL_FALSE, /*swapBytes*/
+ GL_FALSE /*lsbFirst*/
+};
+
+static void stubGetUnpackState(CRPixelPackState *pUnpackState)
+{
+ stub.spu->dispatch_table.GetIntegerv(GL_UNPACK_ROW_LENGTH, &pUnpackState->rowLength);
+ stub.spu->dispatch_table.GetIntegerv(GL_UNPACK_SKIP_ROWS, &pUnpackState->skipRows);
+ stub.spu->dispatch_table.GetIntegerv(GL_UNPACK_SKIP_PIXELS, &pUnpackState->skipPixels);
+ stub.spu->dispatch_table.GetIntegerv(GL_UNPACK_ALIGNMENT, &pUnpackState->alignment);
+ stub.spu->dispatch_table.GetBooleanv(GL_UNPACK_SWAP_BYTES, &pUnpackState->swapBytes);
+ stub.spu->dispatch_table.GetBooleanv(GL_UNPACK_LSB_FIRST, &pUnpackState->psLSBFirst);
+}
+
+static void stubSetUnpackState(const CRPixelPackState *pUnpackState)
+{
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_ROW_LENGTH, pUnpackState->rowLength);
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_SKIP_ROWS, pUnpackState->skipRows);
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_SKIP_PIXELS, pUnpackState->skipPixels);
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_ALIGNMENT, pUnpackState->alignment);
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_SWAP_BYTES, pUnpackState->swapBytes);
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_LSB_FIRST, pUnpackState->psLSBFirst);
+}
+
+static GLX_Pixmap_t* stubInitGlxPixmap(GLX_Pixmap_t* pCreateInfoPixmap, Display *dpy, GLXDrawable draw, ContextInfo *pContext)
+{
+ int x, y;
+ unsigned int w, h;
+ unsigned int border;
+ unsigned int depth;
+ Window root;
+ GLX_Pixmap_t *pGlxPixmap;
+
+ CRASSERT(pContext && pCreateInfoPixmap);
+
+ XLOCK(dpy);
+ if (!XGetGeometry(dpy, (Pixmap)draw, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ XSync(dpy, False);
+ if (!XGetGeometry(dpy, (Pixmap)draw, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ crWarning("stubInitGlxPixmap failed in call to XGetGeometry for 0x%x", (int) draw);
+ XUNLOCK(dpy);
+ return NULL;
+ }
+ }
+
+ pGlxPixmap = crAlloc(sizeof(GLX_Pixmap_t));
+ if (!pGlxPixmap)
+ {
+ crWarning("stubInitGlxPixmap failed to allocate memory");
+ XUNLOCK(dpy);
+ return NULL;
+ }
+
+ pGlxPixmap->x = x;
+ pGlxPixmap->y = y;
+ pGlxPixmap->w = w;
+ pGlxPixmap->h = h;
+ pGlxPixmap->border = border;
+ pGlxPixmap->depth = depth;
+ pGlxPixmap->root = root;
+ pGlxPixmap->format = pCreateInfoPixmap->format;
+ pGlxPixmap->target = pCreateInfoPixmap->target;
+
+ /* Try to allocate shared memory
+ * As we're allocating huge chunk of memory, do it in this function, only if this extension is really used
+ */
+ if (!stub.bShmInitFailed && stub.xshmSI.shmid<0)
+ {
+ stubInitXSharedMemory(dpy);
+ }
+
+ if (stub.xshmSI.shmid>=0)
+ {
+ XGCValues xgcv;
+ xgcv.graphics_exposures = False;
+ xgcv.subwindow_mode = IncludeInferiors;
+ pGlxPixmap->gc = XCreateGC(dpy, (Pixmap)draw, GCGraphicsExposures|GCSubwindowMode, &xgcv);
+
+ pGlxPixmap->hShmPixmap = XShmCreatePixmap(dpy, pGlxPixmap->root, stub.xshmSI.shmaddr, &stub.xshmSI,
+ pGlxPixmap->w, pGlxPixmap->h, pGlxPixmap->depth);
+ }
+ else
+ {
+ pGlxPixmap->gc = NULL;
+ pGlxPixmap->hShmPixmap = 0;
+ }
+ XUNLOCK(dpy);
+
+ /* If there's damage extension, then get handle for damage events related to this pixmap */
+ if (!pContext->damageQueryFailed)
+ {
+ pGlxPixmap->hDamage = XDamageCreate(dpy, (Pixmap)draw, XDamageReportNonEmpty);
+ /*crDebug("Create: Damage for drawable 0x%x, handle 0x%x (level=%i)",
+ (unsigned int) draw, (unsigned int) pGlxPixmap->damage, (int) XDamageReportRawRectangles);*/
+ pGlxPixmap->pDamageRegion = XCreateRegion();
+ if (!pGlxPixmap->pDamageRegion)
+ {
+ crWarning("stubInitGlxPixmap failed to create empty damage region for drawable 0x%x", (unsigned int) draw);
+ }
+
+ /*We have never seen this pixmap before, so mark it as dirty for first use*/
+ pGlxPixmap->bPixmapImageDirty = True;
+ }
+ else
+ {
+ pGlxPixmap->hDamage = 0;
+ pGlxPixmap->pDamageRegion = NULL;
+ }
+
+ /* glTexSubImage2D generates GL_INVALID_OP if texture array hasn't been defined by a call to glTexImage2D first.
+ * It's fine for small textures which would be updated in stubXshmUpdateWholeImage, but we'd never call glTexImage2D for big ones.
+ * Note that we're making empty texture by passing NULL as pixels pointer, so there's no overhead transferring data to host.*/
+ if (CR_MAX_TRANSFER_SIZE < 4*pGlxPixmap->w*pGlxPixmap->h)
+ {
+ stub.spu->dispatch_table.TexImage2D(pGlxPixmap->target, 0, pGlxPixmap->format, pGlxPixmap->w, pGlxPixmap->h, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ }
+
+ crHashtableAdd(pContext->pGLXPixmapsHash, (unsigned int) draw, pGlxPixmap);
+ crHashtableDelete(stub.pGLXPixmapsHash, (unsigned int) draw, crFree);
+
+ return pGlxPixmap;
+}
+
+static void stubXshmUpdateWholeImage(Display *dpy, GLXDrawable draw, GLX_Pixmap_t *pGlxPixmap)
+{
+ /* To limit the size of transferring buffer, split bigger texture into regions
+ * which fit into connection buffer. Could be done in hgcm or packspu but implementation in this place allows to avoid
+ * unnecessary memcpy.
+ * This also workarounds guest driver failures when sending 6+mb texture buffers on linux.
+ */
+ if (CR_MAX_TRANSFER_SIZE < 4*pGlxPixmap->w*pGlxPixmap->h)
+ {
+ XRectangle rect;
+
+ rect.x = pGlxPixmap->x;
+ rect.y = pGlxPixmap->y;
+ rect.width = pGlxPixmap->w;
+ rect.height = CR_MAX_TRANSFER_SIZE/(4*pGlxPixmap->w);
+
+ /*crDebug("Texture size too big, splitting in lower sized chunks. [%i,%i,%i,%i] (%i)",
+ pGlxPixmap->x, pGlxPixmap->y, pGlxPixmap->w, pGlxPixmap->h, rect.height);*/
+
+ for (; (rect.y+rect.height)<=(pGlxPixmap->y+(int)pGlxPixmap->h); rect.y+=rect.height)
+ {
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &rect);
+ }
+
+ if (rect.y!=(pGlxPixmap->y+(int)pGlxPixmap->h))
+ {
+ rect.height=pGlxPixmap->h-rect.y;
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &rect);
+ }
+ }
+ else
+ {
+ CRPixelPackState unpackState;
+
+ XLOCK(dpy);
+ XCopyArea(dpy, (Pixmap)draw, pGlxPixmap->hShmPixmap, pGlxPixmap->gc,
+ pGlxPixmap->x, pGlxPixmap->y, pGlxPixmap->w, pGlxPixmap->h, 0, 0);
+ /* Have to make sure XCopyArea is processed */
+ XSync(dpy, False);
+ XUNLOCK(dpy);
+
+ stubGetUnpackState(&unpackState);
+ stubSetUnpackState(&defaultPacking);
+ stub.spu->dispatch_table.TexImage2D(pGlxPixmap->target, 0, pGlxPixmap->format, pGlxPixmap->w, pGlxPixmap->h, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, stub.xshmSI.shmaddr);
+ stubSetUnpackState(&unpackState);
+ /*crDebug("Sync texture for drawable 0x%x(dmg handle 0x%x) [%i,%i,%i,%i]",
+ (unsigned int) draw, (unsigned int)pGlxPixmap->hDamage,
+ pGlxPixmap->x, pGlxPixmap->y, pGlxPixmap->w, pGlxPixmap->h);*/
+ }
+}
+
+static void stubXshmUpdateImageRect(Display *dpy, GLXDrawable draw, GLX_Pixmap_t *pGlxPixmap, XRectangle *pRect)
+{
+ /* See comment in stubXshmUpdateWholeImage */
+ if (CR_MAX_TRANSFER_SIZE < 4*pRect->width*pRect->height)
+ {
+ XRectangle rect;
+
+ rect.x = pRect->x;
+ rect.y = pRect->y;
+ rect.width = pRect->width;
+ rect.height = CR_MAX_TRANSFER_SIZE/(4*pRect->width);
+
+ /*crDebug("Region size too big, splitting in lower sized chunks. [%i,%i,%i,%i] (%i)",
+ pRect->x, pRect->y, pRect->width, pRect->height, rect.height);*/
+
+ for (; (rect.y+rect.height)<=(pRect->y+pRect->height); rect.y+=rect.height)
+ {
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &rect);
+ }
+
+ if (rect.y!=(pRect->y+pRect->height))
+ {
+ rect.height=pRect->y+pRect->height-rect.y;
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &rect);
+ }
+ }
+ else
+ {
+ CRPixelPackState unpackState;
+
+ XLOCK(dpy);
+ XCopyArea(dpy, (Pixmap)draw, pGlxPixmap->hShmPixmap, pGlxPixmap->gc,
+ pRect->x, pRect->y, pRect->width, pRect->height, 0, 0);
+ /* Have to make sure XCopyArea is processed */
+ XSync(dpy, False);
+ XUNLOCK(dpy);
+
+ stubGetUnpackState(&unpackState);
+ stubSetUnpackState(&defaultPacking);
+ if (pRect->width!=pGlxPixmap->w)
+ {
+ stub.spu->dispatch_table.PixelStorei(GL_UNPACK_ROW_LENGTH, pGlxPixmap->w);
+ }
+ stub.spu->dispatch_table.TexSubImage2D(pGlxPixmap->target, 0, pRect->x, pRect->y, pRect->width, pRect->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, stub.xshmSI.shmaddr);
+ stubSetUnpackState(&unpackState);
+
+ /*crDebug("Region sync texture for drawable 0x%x(dmg handle 0x%x) [%i,%i,%i,%i]",
+ (unsigned int) draw, (unsigned int)pGlxPixmap->hDamage,
+ pRect->x, pRect->y, pRect->width, pRect->height);*/
+ }
+}
+
+#if 0
+Bool checkevents(Display *display, XEvent *event, XPointer arg)
+{
+ //crDebug("got type: 0x%x", event->type);
+ if (event->type==damage_evb+XDamageNotify)
+ {
+ ContextInfo *context = stubGetCurrentContext();
+ XDamageNotifyEvent *e = (XDamageNotifyEvent *) event;
+ /* we're interested in pixmaps only...and those have e->drawable set to 0 or other strange value for some odd reason
+ * so have to walk glxpixmaps hashtable to find if we have damage event handle assigned to some pixmap
+ */
+ /*crDebug("Event: Damage for drawable 0x%x, handle 0x%x (level=%i) [%i,%i,%i,%i]",
+ (unsigned int) e->drawable, (unsigned int) e->damage, (int) e->level,
+ e->area.x, e->area.y, e->area.width, e->area.height);*/
+ CRASSERT(context);
+ crHashtableWalk(context->pGLXPixmapsHash, checkdamageCB, e);
+ }
+ return False;
+}
+#endif
+
+/** @todo check what error codes could we throw for failures here*/
+DECLEXPORT(void) VBOXGLXTAG(glXBindTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer, const int *attrib_list)
+{
+ ContextInfo *context = stubGetCurrentContext();
+ GLX_Pixmap_t *pGlxPixmap;
+ RT_NOREF(buffer, attrib_list);
+
+ if (!context)
+ {
+ crWarning("glXBindTexImageEXT called without current context");
+ return;
+ }
+
+ pGlxPixmap = (GLX_Pixmap_t *) crHashtableSearch(context->pGLXPixmapsHash, (unsigned int) draw);
+ if (!pGlxPixmap)
+ {
+ pGlxPixmap = (GLX_Pixmap_t *) crHashtableSearch(stub.pGLXPixmapsHash, (unsigned int) draw);
+ if (!pGlxPixmap)
+ {
+ crDebug("Unknown drawable 0x%x in glXBindTexImageEXT!", (unsigned int) draw);
+ return;
+ }
+ pGlxPixmap = stubInitGlxPixmap(pGlxPixmap, dpy, draw, context);
+ if (!pGlxPixmap)
+ {
+ crDebug("glXBindTexImageEXT failed to get pGlxPixmap");
+ return;
+ }
+ }
+
+ /* If there's damage extension, then process incoming events as we need the information right now */
+ if (!context->damageQueryFailed)
+ {
+ /* Sync connection */
+ XLOCK(dpy);
+ XSync(dpy, False);
+ XUNLOCK(dpy);
+
+ stubFetchDamageOnDrawable(dpy, pGlxPixmap);
+ }
+
+ /* No shared memory? Rollback to use slow x protocol then */
+ if (stub.xshmSI.shmid<0)
+ {
+ /** @todo add damage support here too*/
+ XImage *pxim;
+ CRPixelPackState unpackState;
+
+ XLOCK(dpy);
+ pxim = XGetImage(dpy, (Pixmap)draw, pGlxPixmap->x, pGlxPixmap->y, pGlxPixmap->w, pGlxPixmap->h, AllPlanes, ZPixmap);
+ XUNLOCK(dpy);
+ /*if (pxim)
+ {
+ if (!ptextable)
+ {
+ ptextable = crAllocHashtable();
+ }
+ pm = crHashtableSearch(ptextable, (unsigned int) draw);
+ if (!pm)
+ {
+ pm = crCalloc(sizeof(pminfo));
+ crHashtableAdd(ptextable, (unsigned int) draw, pm);
+ }
+ pm->w = w;
+ pm->h = h;
+ if (pm->data) crFree(pm->data);
+ pm->data = crAlloc(4*w*h);
+ crMemcpy(pm->data, (void*)(&(pxim->data[0])), 4*w*h);
+ }*/
+
+ if (NULL==pxim)
+ {
+ crWarning("Failed, to get pixmap data for 0x%x", (unsigned int) draw);
+ return;
+ }
+
+ stubGetUnpackState(&unpackState);
+ stubSetUnpackState(&defaultPacking);
+ stub.spu->dispatch_table.TexImage2D(pGlxPixmap->target, 0, pGlxPixmap->format, pxim->width, pxim->height, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, (void*)(&(pxim->data[0])));
+ stubSetUnpackState(&unpackState);
+ XDestroyImage(pxim);
+ }
+ else /* Use shm to get pixmap data */
+ {
+ /* Check if we have damage extension */
+ if (!context->damageQueryFailed)
+ {
+ if (pGlxPixmap->bPixmapImageDirty)
+ {
+ /* Either we failed to allocate damage region or this pixmap is marked for full update */
+ if (!pGlxPixmap->pDamageRegion || XEmptyRegion(pGlxPixmap->pDamageRegion))
+ {
+ /*crDebug("**FULL** update for 0x%x", (unsigned int)draw);*/
+ stubXshmUpdateWholeImage(dpy, draw, pGlxPixmap);
+ }
+ else
+ {
+ long fullArea, damageArea=0, clipdamageArea, i;
+ XRectangle damageClipBox;
+
+ fullArea = pGlxPixmap->w * pGlxPixmap->h;
+ XClipBox(pGlxPixmap->pDamageRegion, &damageClipBox);
+ clipdamageArea = damageClipBox.width * damageClipBox.height;
+
+ //crDebug("FullSize [%i,%i,%i,%i]", pGlxPixmap->x, pGlxPixmap->y, pGlxPixmap->w, pGlxPixmap->h);
+ //crDebug("Clip [%i,%i,%i,%i]", damageClipBox.x, damageClipBox.y, damageClipBox.width, damageClipBox.height);
+
+ for (i=0; i<pGlxPixmap->pDamageRegion->numRects; ++i)
+ {
+ BoxPtr pBox = &pGlxPixmap->pDamageRegion->rects[i];
+ damageArea += (pBox->x2-pBox->x1)*(pBox->y2-pBox->y1);
+ //crDebug("Damage rect [%i,%i,%i,%i]", pBox->x1, pBox->y1, pBox->x2, pBox->y2);
+ }
+
+ if (damageArea>clipdamageArea || clipdamageArea>fullArea)
+ {
+ crWarning("glXBindTexImageEXT, damage regions seems to be broken, forcing full update");
+ /*crDebug("**FULL** update for 0x%x, numRect=%li, *FS*=%li, CS=%li, DS=%li",
+ (unsigned int)draw, pGlxPixmap->pDamageRegion->numRects, fullArea, clipdamageArea, damageArea);*/
+ stubXshmUpdateWholeImage(dpy, draw, pGlxPixmap);
+ }
+ else /*We have corect damage info*/
+ {
+ if (CR_MIN_DAMAGE_PROFIT_SIZE > (fullArea-damageArea))
+ {
+ /*crDebug("**FULL** update for 0x%x, numRect=%li, *FS*=%li, CS=%li, DS=%li",
+ (unsigned int)draw, pGlxPixmap->pDamageRegion->numRects, fullArea, clipdamageArea, damageArea);*/
+ stubXshmUpdateWholeImage(dpy, draw, pGlxPixmap);
+ }
+ else if (CR_MIN_DAMAGE_PROFIT_SIZE > (clipdamageArea-damageArea))
+ {
+ /*crDebug("**PARTIAL** update for 0x%x, numRect=%li, FS=%li, *CS*=%li, DS=%li",
+ (unsigned int)draw, pGlxPixmap->pDamageRegion->numRects, fullArea, clipdamageArea, damageArea);*/
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &damageClipBox);
+ }
+ else
+ {
+ /*crDebug("**PARTIAL** update for 0x%x, numRect=*%li*, FS=%li, CS=%li, *DS*=%li",
+ (unsigned int)draw, pGlxPixmap->pDamageRegion->numRects, fullArea, clipdamageArea, damageArea);*/
+ for (i=0; i<pGlxPixmap->pDamageRegion->numRects; ++i)
+ {
+ XRectangle rect;
+ BoxPtr pBox = &pGlxPixmap->pDamageRegion->rects[i];
+
+ rect.x = pBox->x1;
+ rect.y = pBox->y1;
+ rect.width = pBox->x2-pBox->x1;
+ rect.height = pBox->y2-pBox->y1;
+
+ stubXshmUpdateImageRect(dpy, draw, pGlxPixmap, &rect);
+ }
+ }
+ }
+ }
+
+ /* Clean dirty flag and damage region */
+ pGlxPixmap->bPixmapImageDirty = False;
+ if (pGlxPixmap->pDamageRegion)
+ EMPTY_REGION(pGlxPixmap->pDamageRegion);
+ }
+ }
+ else
+ {
+ stubXshmUpdateWholeImage(dpy, draw, pGlxPixmap);
+ }
+ }
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXReleaseTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer)
+{
+ (void) dpy;
+ (void) draw;
+ (void) buffer;
+ //crDebug("glXReleaseTexImageEXT 0x%x", (unsigned int)draw);
+}
+#endif
+
+#endif /* GLX_EXTRAS */
+
+
+#ifdef GLX_SGIX_video_resize
+/* more dummy funcs. These help when linking with older GLUTs */
+
+DECLEXPORT(int) VBOXGLXTAG(glXBindChannelToWindowSGIX)(Display *dpy, int scrn, int chan, Window w)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) chan;
+ (void) w;
+ crDebug("glXBindChannelToWindowSGIX");
+ return 0;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXChannelRectSGIX)(Display *dpy, int scrn, int chan, int x , int y, int w, int h)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) chan;
+ (void) x;
+ (void) y;
+ (void) w;
+ (void) h;
+ crDebug("glXChannelRectSGIX");
+ return 0;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXQueryChannelRectSGIX)(Display *dpy, int scrn, int chan, int *x, int *y, int *w, int *h)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) chan;
+ (void) x;
+ (void) y;
+ (void) w;
+ (void) h;
+ crDebug("glXQueryChannelRectSGIX");
+ return 0;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXQueryChannelDeltasSGIX)(Display *dpy, int scrn, int chan, int *dx, int *dy, int *dw, int *dh)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) chan;
+ (void) dx;
+ (void) dy;
+ (void) dw;
+ (void) dh;
+ crDebug("glXQueryChannelDeltasSGIX");
+ return 0;
+}
+
+DECLEXPORT(int) VBOXGLXTAG(glXChannelRectSyncSGIX)(Display *dpy, int scrn, int chan, GLenum synctype)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) chan;
+ (void) synctype;
+ crDebug("glXChannelRectSyncSGIX");
+ return 0;
+}
+
+#endif /* GLX_SGIX_video_resize */
+
+#ifdef VBOXOGL_FAKEDRI
+DECLEXPORT(const char *) VBOXGLXTAG(glXGetDriverConfig)(const char *driverName)
+{
+ return NULL;
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXFreeMemoryMESA)(Display *dpy, int scrn, void *pointer)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) pointer;
+}
+
+DECLEXPORT(GLXContext) VBOXGLXTAG(glXImportContextEXT)(Display *dpy, GLXContextID contextID)
+{
+ (void) dpy;
+ (void) contextID;
+ return NULL;
+}
+
+DECLEXPORT(GLXContextID) VBOXGLXTAG(glXGetContextIDEXT)(const GLXContext ctx)
+{
+ (void) ctx;
+ return 0;
+}
+
+DECLEXPORT(Bool) VBOXGLXTAG(glXMakeCurrentReadSGI)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx)
+{
+ return VBOXGLXTAG(glXMakeContextCurrent)(display, draw, read, ctx);
+}
+
+DECLEXPORT(const char *) VBOXGLXTAG(glXGetScreenDriver)(Display *dpy, int scrNum)
+{
+ static char *screendriver = "vboxvideo";
+ return screendriver;
+}
+
+DECLEXPORT(Display *) VBOXGLXTAG(glXGetCurrentDisplayEXT)(void)
+{
+ return VBOXGLXTAG(glXGetCurrentDisplay());
+}
+
+DECLEXPORT(void) VBOXGLXTAG(glXFreeContextEXT)(Display *dpy, GLXContext ctx)
+{
+ VBOXGLXTAG(glXDestroyContext(dpy, ctx));
+}
+
+/*Mesa internal*/
+DECLEXPORT(int) VBOXGLXTAG(glXQueryContextInfoEXT)(Display *dpy, GLXContext ctx)
+{
+ (void) dpy;
+ (void) ctx;
+ return 0;
+}
+
+DECLEXPORT(void *) VBOXGLXTAG(glXAllocateMemoryMESA)(Display *dpy, int scrn,
+ size_t size, float readFreq,
+ float writeFreq, float priority)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) size;
+ (void) readFreq;
+ (void) writeFreq;
+ (void) priority;
+ return NULL;
+}
+
+DECLEXPORT(GLuint) VBOXGLXTAG(glXGetMemoryOffsetMESA)(Display *dpy, int scrn, const void *pointer)
+{
+ (void) dpy;
+ (void) scrn;
+ (void) pointer;
+ return 0;
+}
+
+DECLEXPORT(GLXPixmap) VBOXGLXTAG(glXCreateGLXPixmapMESA)(Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap)
+{
+ (void) dpy;
+ (void) visual;
+ (void) pixmap;
+ (void) cmap;
+ return 0;
+}
+
+#endif /*VBOXOGL_FAKEDRI*/
diff --git a/src/VBox/Additions/common/crOpenGL/glx_c_exports.c b/src/VBox/Additions/common/crOpenGL/glx_c_exports.c
new file mode 100644
index 00000000..c8068375
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/glx_c_exports.c
@@ -0,0 +1,375 @@
+/* $Id: glx_c_exports.c $ */
+/** @file
+ * VirtualBox guest OpenGL DRI GLX C stubs
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "stub.h"
+#include "dri_glx.h"
+#include "fakedri_drv.h"
+
+
+#ifdef VBOXOGL_FAKEDRI
+/*DECLEXPORT(void) VBOXGLXENTRYTAG(glXGetDriverConfig)(const char *driverName)
+{
+ return glxim.GetDriverConfig(driverName);
+}*/
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXFreeMemoryMESA)(Display *dpy, int scrn, void *pointer)
+{
+ return glxim.FreeMemoryMESA(dpy, scrn, pointer);
+}
+
+DECLEXPORT(GLXContext) VBOXGLXENTRYTAG(glXImportContextEXT)(Display *dpy, GLXContextID contextID)
+{
+ return glxim.ImportContextEXT(dpy, contextID);
+}
+
+DECLEXPORT(GLXContextID) VBOXGLXENTRYTAG(glXGetContextIDEXT)(const GLXContext ctx)
+{
+ return glxim.GetContextIDEXT(ctx);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXMakeCurrentReadSGI)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx)
+{
+ return glxim.MakeCurrentReadSGI(display, draw, read, ctx);
+}
+
+
+/*const char * VBOXGLXENTRYTAG(glXGetScreenDriver)(Display *dpy, int scrNum)
+{
+ return glxim.GetScreenDriver(dpy, scrNum);
+}*/
+
+
+DECLEXPORT(Display*) VBOXGLXENTRYTAG(glXGetCurrentDisplayEXT)(void)
+{
+ return glxim.GetCurrentDisplayEXT();
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXFreeContextEXT)(Display *dpy, GLXContext ctx)
+{
+ return glxim.FreeContextEXT(dpy, ctx);
+}
+
+/*Mesa internal*/
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXQueryContextInfoEXT)(Display *dpy, GLXContext ctx)
+{
+ return glxim.QueryContextInfoEXT(dpy, ctx);
+}
+
+DECLEXPORT(void *) VBOXGLXENTRYTAG(glXAllocateMemoryMESA)(Display *dpy, int scrn,
+ size_t size, float readFreq,
+ float writeFreq, float priority)
+{
+ return glxim.AllocateMemoryMESA(dpy, scrn, size, readFreq, writeFreq, priority);
+}
+
+DECLEXPORT(GLuint) VBOXGLXENTRYTAG(glXGetMemoryOffsetMESA)(Display *dpy, int scrn, const void *pointer )
+{
+ return glxim.GetMemoryOffsetMESA(dpy, scrn, pointer);
+}
+
+
+DECLEXPORT(GLXPixmap) VBOXGLXENTRYTAG(glXCreateGLXPixmapMESA)(Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap)
+{
+ return glxim.CreateGLXPixmapMESA(dpy, visual, pixmap, cmap);
+}
+#endif
+
+/*Common glX functions*/
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXCopyContext)( Display *dpy, GLXContext src, GLXContext dst, unsigned long mask)
+{
+ return glxim.CopyContext(dpy, src, dst, mask);
+}
+
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXUseXFont)(Font font, int first, int count, int listBase)
+{
+ return glxim.UseXFont(font, first, count, listBase);
+}
+
+DECLEXPORT(CR_GLXFuncPtr) VBOXGLXENTRYTAG(glXGetProcAddress)(const GLubyte *name)
+{
+ return glxim.GetProcAddress(name);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXQueryExtension)(Display *dpy, int *errorBase, int *eventBase)
+{
+ return glxim.QueryExtension(dpy, errorBase, eventBase);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXIsDirect)(Display *dpy, GLXContext ctx)
+{
+ return glxim.IsDirect(dpy, ctx);
+}
+
+DECLEXPORT(GLXPixmap) VBOXGLXENTRYTAG(glXCreateGLXPixmap)(Display *dpy, XVisualInfo *vis, Pixmap pixmap)
+{
+ return glxim.CreateGLXPixmap(dpy, vis, pixmap);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXSwapBuffers)(Display *dpy, GLXDrawable drawable)
+{
+ return glxim.SwapBuffers(dpy, drawable);
+}
+
+
+DECLEXPORT(GLXDrawable) VBOXGLXENTRYTAG(glXGetCurrentDrawable)(void)
+{
+ return glxim.GetCurrentDrawable();
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXWaitGL)(void)
+{
+ return glxim.WaitGL();
+}
+
+DECLEXPORT(Display *) VBOXGLXENTRYTAG(glXGetCurrentDisplay)(void)
+{
+ return glxim.GetCurrentDisplay();
+}
+
+DECLEXPORT(const char *) VBOXGLXENTRYTAG(glXQueryServerString)(Display *dpy, int screen, int name)
+{
+ return glxim.QueryServerString(dpy, screen, name);
+}
+
+DECLEXPORT(GLXContext) VBOXGLXENTRYTAG(glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, Bool direct)
+{
+ return glxim.CreateContext(dpy, vis, share, direct);
+}
+
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXGetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int *value)
+{
+ return glxim.GetConfig(dpy, vis, attrib, value);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXWaitX)(void)
+{
+ return glxim.WaitX();
+}
+
+DECLEXPORT(GLXContext) VBOXGLXENTRYTAG(glXGetCurrentContext)(void)
+{
+ return glxim.GetCurrentContext();
+}
+
+DECLEXPORT(const char *) VBOXGLXENTRYTAG(glXGetClientString)(Display *dpy, int name)
+{
+ return glxim.GetClientString(dpy, name);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx)
+{
+ return glxim.MakeCurrent(dpy, drawable, ctx);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyContext)(Display *dpy, GLXContext ctx)
+{
+ return glxim.DestroyContext(dpy, ctx);
+}
+
+DECLEXPORT(CR_GLXFuncPtr) VBOXGLXENTRYTAG(glXGetProcAddressARB)(const GLubyte *name)
+{
+ return glxim.GetProcAddressARB(name);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyGLXPixmap)(Display *dpy, GLXPixmap pix)
+{
+ return glxim.DestroyGLXPixmap(dpy, pix);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXQueryVersion)(Display *dpy, int *major, int *minor)
+{
+ return glxim.QueryVersion(dpy, major, minor);
+}
+
+DECLEXPORT(XVisualInfo *) VBOXGLXENTRYTAG(glXChooseVisual)(Display *dpy, int screen, int *attribList)
+{
+ return glxim.ChooseVisual(dpy, screen, attribList);
+}
+
+DECLEXPORT(const char *) VBOXGLXENTRYTAG(glXQueryExtensionsString)(Display *dpy, int screen)
+{
+ return glxim.QueryExtensionsString(dpy, screen);
+}
+
+#if GLX_EXTRAS
+DECLEXPORT(GLXPbufferSGIX) VBOXGLXENTRYTAG(glXCreateGLXPbufferSGIX)
+(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list)
+{
+ return glxim.CreateGLXPbufferSGIX(dpy, config, width, height, attrib_list);
+}
+
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXQueryGLXPbufferSGIX)
+(Display *dpy, GLXPbuffer pbuf, int attribute, unsigned int *value)
+{
+ return glxim.QueryGLXPbufferSGIX(dpy, pbuf, attribute, value);
+}
+
+DECLEXPORT(GLXFBConfigSGIX *) VBOXGLXENTRYTAG(glXChooseFBConfigSGIX)
+(Display *dpy, int screen, int *attrib_list, int *nelements)
+{
+ return glxim.ChooseFBConfigSGIX(dpy, screen, attrib_list, nelements);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf)
+{
+ return glxim.DestroyGLXPbufferSGIX(dpy, pbuf);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXSelectEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long mask)
+{
+ return glxim.SelectEventSGIX(dpy, drawable, mask);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXGetSelectedEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long *mask)
+{
+ return glxim.GetSelectedEventSGIX(dpy, drawable, mask);
+}
+
+DECLEXPORT(GLXFBConfigSGIX) VBOXGLXENTRYTAG(glXGetFBConfigFromVisualSGIX)(Display *dpy, XVisualInfo *vis)
+{
+ return glxim.GetFBConfigFromVisualSGIX(dpy, vis);
+}
+
+DECLEXPORT(XVisualInfo *) VBOXGLXENTRYTAG(glXGetVisualFromFBConfigSGIX)(Display *dpy, GLXFBConfig config)
+{
+ return glxim.GetVisualFromFBConfigSGIX(dpy, config);
+}
+
+DECLEXPORT(GLXContext) VBOXGLXENTRYTAG(glXCreateContextWithConfigSGIX)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
+{
+ return glxim.CreateContextWithConfigSGIX(dpy, config, render_type, share_list, direct);
+}
+
+DECLEXPORT(GLXPixmap) VBOXGLXENTRYTAG(glXCreateGLXPixmapWithConfigSGIX)(Display *dpy, GLXFBConfig config, Pixmap pixmap)
+{
+ return glxim.CreateGLXPixmapWithConfigSGIX(dpy, config, pixmap);
+}
+
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXGetFBConfigAttribSGIX)(Display *dpy, GLXFBConfig config, int attribute, int *value)
+{
+ return glxim.GetFBConfigAttribSGIX(dpy, config, attribute, value);
+}
+
+
+/*
+ * GLX 1.3 functions
+ */
+DECLEXPORT(GLXFBConfig *) VBOXGLXENTRYTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list, int *nelements)
+{
+ return glxim.ChooseFBConfig(dpy, screen, attrib_list, nelements);
+}
+
+DECLEXPORT(GLXPbuffer) VBOXGLXENTRYTAG(glXCreatePbuffer)(Display *dpy, GLXFBConfig config, ATTRIB_TYPE *attrib_list)
+{
+ return glxim.CreatePbuffer(dpy, config, attrib_list);
+}
+
+DECLEXPORT(GLXPixmap) VBOXGLXENTRYTAG(glXCreatePixmap)(Display *dpy, GLXFBConfig config, Pixmap pixmap, const ATTRIB_TYPE *attrib_list)
+{
+ return glxim.CreatePixmap(dpy, config, pixmap, attrib_list);
+}
+
+DECLEXPORT(GLXWindow) VBOXGLXENTRYTAG(glXCreateWindow)(Display *dpy, GLXFBConfig config, Window win, ATTRIB_TYPE *attrib_list)
+{
+ return glxim.CreateWindow(dpy, config, win, attrib_list);
+}
+
+
+DECLEXPORT(GLXContext) VBOXGLXENTRYTAG(glXCreateNewContext)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
+{
+ return glxim.CreateNewContext(dpy, config, render_type, share_list, direct);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyPbuffer)(Display *dpy, GLXPbuffer pbuf)
+{
+ return glxim.DestroyPbuffer(dpy, pbuf);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyPixmap)(Display *dpy, GLXPixmap pixmap)
+{
+ return glxim.DestroyPixmap(dpy, pixmap);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXDestroyWindow)(Display *dpy, GLXWindow win)
+{
+ return glxim.DestroyWindow(dpy, win);
+}
+
+DECLEXPORT(GLXDrawable) VBOXGLXENTRYTAG(glXGetCurrentReadDrawable)(void)
+{
+ return glxim.GetCurrentReadDrawable();
+}
+
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int *value)
+{
+ return glxim.GetFBConfigAttrib(dpy, config, attribute, value);
+}
+
+DECLEXPORT(GLXFBConfig *) VBOXGLXENTRYTAG(glXGetFBConfigs)(Display *dpy, int screen, int *nelements)
+{
+ return glxim.GetFBConfigs(dpy, screen, nelements);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXGetSelectedEvent)(Display *dpy, GLXDrawable draw, unsigned long *event_mask)
+{
+ return glxim.GetSelectedEvent(dpy, draw, event_mask);
+}
+
+DECLEXPORT(XVisualInfo *) VBOXGLXENTRYTAG(glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config)
+{
+ return glxim.GetVisualFromFBConfig(dpy, config);
+}
+
+DECLEXPORT(Bool) VBOXGLXENTRYTAG(glXMakeContextCurrent)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx)
+{
+ return glxim.MakeContextCurrent(display, draw, read, ctx);
+}
+
+DECLEXPORT(int) VBOXGLXENTRYTAG(glXQueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value)
+{
+ return glxim.QueryContext(dpy, ctx, attribute, value);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXQueryDrawable)(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value)
+{
+ return glxim.QueryDrawable(dpy, draw, attribute, value);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXSelectEvent)(Display *dpy, GLXDrawable draw, unsigned long event_mask)
+{
+ return glxim.SelectEvent(dpy, draw, event_mask);
+}
+
+/*
+#ifdef CR_EXT_texture_from_pixmap
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXBindTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer, const int *attrib_list)
+{
+ return glxim.BindTexImageEXT(dpy, draw, buffer, attrib_list);
+}
+
+DECLEXPORT(void) VBOXGLXENTRYTAG(glXReleaseTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer)
+{
+ return glxim.ReleaseTexImageEXT(dpy, draw, buffer);
+}
+#endif
+*/
+
+#endif /* GLX_EXTRAS */
+
diff --git a/src/VBox/Additions/common/crOpenGL/glx_proto.h b/src/VBox/Additions/common/crOpenGL/glx_proto.h
new file mode 100644
index 00000000..ee35dce1
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/glx_proto.h
@@ -0,0 +1,149 @@
+/* $Id: glx_proto.h $ */
+/** @file
+ * VirtualBox guest OpenGL DRI GLX header C prototypes
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_glx_proto_h
+#define GA_INCLUDED_SRC_common_crOpenGL_glx_proto_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "chromium.h"
+#include "stub.h"
+
+#if defined(VBOXOGL_FAKEDRI) || defined(VBOXOGL_DRI)
+typedef const char * (*PGLXFUNC_GetDriverConfig)(const char *driverName);
+typedef void (*PGLXFUNC_FreeMemoryMESA)(Display *dpy, int scrn, void *pointer);
+typedef GLXContext (*PGLXFUNC_ImportContextEXT)(Display *dpy, GLXContextID contextID);
+typedef GLXContextID (*PGLXFUNC_GetContextIDEXT)(const GLXContext ctx);
+typedef Bool (*PGLXFUNC_MakeCurrentReadSGI)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+typedef const char * (*PGLXFUNC_GetScreenDriver)(Display *dpy, int scrNum);
+typedef Display * (*PGLXFUNC_GetCurrentDisplayEXT)(void);
+typedef void (*PGLXFUNC_FreeContextEXT)(Display *dpy, GLXContext ctx);
+
+/*Mesa internal*/
+typedef int (*PGLXFUNC_QueryContextInfoEXT)(Display *dpy, GLXContext ctx);
+typedef void * (*PGLXFUNC_AllocateMemoryMESA)(Display *dpy, int scrn,
+ size_t size, float readFreq,
+ float writeFreq, float priority);
+typedef GLuint (*PGLXFUNC_GetMemoryOffsetMESA)(Display *dpy, int scrn, const void *pointer );
+typedef GLXPixmap (*PGLXFUNC_CreateGLXPixmapMESA)(Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
+#endif
+
+/*Common glX functions*/
+typedef void (*PGLXFUNC_CopyContext)(Display *dpy, GLXContext src, GLXContext dst,unsigned long mask);
+typedef void (*PGLXFUNC_UseXFont)(Font font, int first, int count, int listBase);
+typedef CR_GLXFuncPtr (*PGLXFUNC_GetProcAddress)(const GLubyte *name);
+typedef Bool (*PGLXFUNC_QueryExtension)(Display *dpy, int *errorBase, int *eventBase);
+typedef Bool (*PGLXFUNC_IsDirect)(Display *dpy, GLXContext ctx);
+typedef GLXPixmap (*PGLXFUNC_CreateGLXPixmap)(Display *dpy, XVisualInfo *vis, Pixmap pixmap);
+typedef void (*PGLXFUNC_SwapBuffers)(Display *dpy, GLXDrawable drawable);
+typedef GLXDrawable (*PGLXFUNC_GetCurrentDrawable)(void);
+typedef void (*PGLXFUNC_WaitGL)(void);
+typedef Display * (*PGLXFUNC_GetCurrentDisplay)(void);
+typedef const char * (*PGLXFUNC_QueryServerString)(Display *dpy, int screen, int name);
+typedef GLXContext (*PGLXFUNC_CreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, Bool direct);
+typedef int (*PGLXFUNC_GetConfig)(Display *dpy, XVisualInfo *vis, int attrib, int *value);
+typedef void (*PGLXFUNC_WaitX)(void);
+typedef GLXContext (*PGLXFUNC_GetCurrentContext)(void);
+typedef const char * (*PGLXFUNC_GetClientString)(Display *dpy, int name);
+typedef Bool (*PGLXFUNC_MakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
+typedef void (*PGLXFUNC_DestroyContext)(Display *dpy, GLXContext ctx);
+typedef CR_GLXFuncPtr (*PGLXFUNC_GetProcAddressARB)(const GLubyte *name);
+typedef void (*PGLXFUNC_DestroyGLXPixmap)(Display *dpy, GLXPixmap pix);
+typedef Bool (*PGLXFUNC_QueryVersion)(Display *dpy, int *major, int *minor);
+typedef XVisualInfo * (*PGLXFUNC_ChooseVisual)(Display *dpy, int screen, int *attribList);
+typedef const char * (*PGLXFUNC_QueryExtensionsString)(Display *dpy, int screen);
+
+/**
+ * Set this to 1 if you want to build stub functions for the
+ * GL_SGIX_pbuffer and GLX_SGIX_fbconfig extensions.
+ * This used to be disabled, due to "messy compilation issues",
+ * according to the earlier comment; but they're needed just
+ * to resolve symbols for OpenInventor applications, and I
+ * haven't found any reference to exactly what the "messy compilation
+ * issues" are, so I'm re-enabling the code by default.
+ */
+#define GLX_EXTRAS 1
+
+#define GLX_SGIX_video_resize 1
+
+/**
+ * Prototypes, in case they're not in glx.h or glxext.h
+ * Unfortunately, there's some inconsistency between the extension
+ * specs, and the SGI, NVIDIA, XFree86 and common glxext.h header
+ * files.
+ */
+#if defined(GLX_GLXEXT_VERSION)
+/* match glxext.h, XFree86, Mesa */
+#define ATTRIB_TYPE const int
+#else
+#define ATTRIB_TYPE int
+#endif
+
+#if GLX_EXTRAS
+typedef GLXPbufferSGIX (*PGLXFUNC_CreateGLXPbufferSGIX)
+(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
+
+typedef int (*PGLXFUNC_QueryGLXPbufferSGIX)
+(Display *dpy, GLXPbuffer pbuf, int attribute, unsigned int *value);
+
+typedef GLXFBConfigSGIX * (*PGLXFUNC_ChooseFBConfigSGIX)
+(Display *dpy, int screen, int *attrib_list, int *nelements);
+
+typedef void (*PGLXFUNC_DestroyGLXPbufferSGIX)(Display *dpy, GLXPbuffer pbuf);
+typedef void (*PGLXFUNC_SelectEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long mask);
+typedef void (*PGLXFUNC_GetSelectedEventSGIX)(Display *dpy, GLXDrawable drawable, unsigned long *mask);
+
+typedef GLXFBConfigSGIX (*PGLXFUNC_GetFBConfigFromVisualSGIX)(Display *dpy, XVisualInfo *vis);
+typedef XVisualInfo * (*PGLXFUNC_GetVisualFromFBConfigSGIX)(Display *dpy, GLXFBConfig config);
+typedef GLXContext (*PGLXFUNC_CreateContextWithConfigSGIX)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+
+typedef GLXPixmap (*PGLXFUNC_CreateGLXPixmapWithConfigSGIX)(Display *dpy, GLXFBConfig config, Pixmap pixmap);
+typedef int (*PGLXFUNC_GetFBConfigAttribSGIX)(Display *dpy, GLXFBConfig config, int attribute, int *value);
+
+/*
+ * GLX 1.3 functions
+ */
+typedef GLXFBConfig * (*PGLXFUNC_ChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list, int *nelements);
+typedef GLXPbuffer (*PGLXFUNC_CreatePbuffer)(Display *dpy, GLXFBConfig config, ATTRIB_TYPE *attrib_list);
+typedef GLXPixmap (*PGLXFUNC_CreatePixmap)(Display *dpy, GLXFBConfig config, Pixmap pixmap, const ATTRIB_TYPE *attrib_list);
+typedef GLXWindow (*PGLXFUNC_CreateWindow)(Display *dpy, GLXFBConfig config, Window win, ATTRIB_TYPE *attrib_list);
+typedef GLXContext (*PGLXFUNC_CreateNewContext)
+(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+
+typedef void (*PGLXFUNC_DestroyPbuffer)(Display *dpy, GLXPbuffer pbuf);
+typedef void (*PGLXFUNC_DestroyPixmap)(Display *dpy, GLXPixmap pixmap);
+typedef void (*PGLXFUNC_DestroyWindow)(Display *dpy, GLXWindow win);
+typedef GLXDrawable (*PGLXFUNC_GetCurrentReadDrawable)(void);
+typedef int (*PGLXFUNC_GetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int *value);
+typedef GLXFBConfig * (*PGLXFUNC_GetFBConfigs)(Display *dpy, int screen, int *nelements);
+typedef void (*PGLXFUNC_GetSelectedEvent)(Display *dpy, GLXDrawable draw, unsigned long *event_mask);
+typedef XVisualInfo * (*PGLXFUNC_GetVisualFromFBConfig)(Display *dpy, GLXFBConfig config);
+typedef Bool (*PGLXFUNC_MakeContextCurrent)(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+typedef int (*PGLXFUNC_QueryContext)(Display *dpy, GLXContext ctx, int attribute, int *value);
+typedef void (*PGLXFUNC_QueryDrawable)(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
+typedef void (*PGLXFUNC_SelectEvent)(Display *dpy, GLXDrawable draw, unsigned long event_mask);
+
+#ifdef CR_EXT_texture_from_pixmap
+typedef void (*PGLXFUNC_BindTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer, const int *attrib_list);
+typedef void (*PGLXFUNC_ReleaseTexImageEXT)(Display *dpy, GLXDrawable draw, int buffer);
+#endif
+
+#endif /* GLX_EXTRAS */
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_glx_proto_h */
diff --git a/src/VBox/Additions/common/crOpenGL/icd_drv.c b/src/VBox/Additions/common/crOpenGL/icd_drv.c
new file mode 100644
index 00000000..3c54bb2f
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/icd_drv.c
@@ -0,0 +1,398 @@
+/* $Id: icd_drv.c $ */
+/** @file
+ * VBox OpenGL windows ICD driver functions
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "cr_error.h"
+#include "icd_drv.h"
+#include "cr_gl.h"
+#include "stub.h"
+#include "cr_mem.h"
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+# include <VBoxCrHgsmi.h>
+# include <VBoxUhgsmi.h>
+#endif
+
+#include <iprt/win/windows.h>
+
+/// @todo consider
+/* We can modify chronium dispatch table functions order to match the one required by ICD,
+ * but it'd render us incompatible with other chromium SPUs and require more changes.
+ * In current state, we can use unmodified binary chromium SPUs. Question is do we need it?
+*/
+
+#define GL_FUNC(func) cr_gl##func
+
+static ICDTABLE icdTable = { 336, {
+#define ICD_ENTRY(func) (PROC)GL_FUNC(func),
+#include "VBoxICDList.h"
+#undef ICD_ENTRY
+} };
+
+/* Currently host part will misbehave re-creating context with proper visual bits
+ * if contexts with alternative visual bits is requested.
+ * For now we just report a superset of all visual bits to avoid that.
+ * Better to it on the host side as well?
+ * We could also implement properly multiple pixel formats,
+ * which should be done by implementing offscreen rendering or multiple host contexts.
+ * */
+#define VBOX_CROGL_USE_VBITS_SUPERSET
+
+#ifdef VBOX_CROGL_USE_VBITS_SUPERSET
+static GLuint desiredVisual = CR_RGB_BIT | CR_ALPHA_BIT | CR_DEPTH_BIT | CR_STENCIL_BIT | CR_ACCUM_BIT | CR_DOUBLE_BIT;
+#else
+static GLuint desiredVisual = CR_RGB_BIT;
+#endif
+
+#ifndef VBOX_CROGL_USE_VBITS_SUPERSET
+/**
+ * Compute a mask of CR_*_BIT flags which reflects the attributes of
+ * the pixel format of the given hdc.
+ */
+static GLuint ComputeVisBits( HDC hdc )
+{
+ PIXELFORMATDESCRIPTOR pfd;
+ int iPixelFormat;
+ GLuint b = 0;
+
+ iPixelFormat = GetPixelFormat( hdc );
+
+ DescribePixelFormat( hdc, iPixelFormat, sizeof(pfd), &pfd );
+
+ if (pfd.cDepthBits > 0)
+ b |= CR_DEPTH_BIT;
+ if (pfd.cAccumBits > 0)
+ b |= CR_ACCUM_BIT;
+ if (pfd.cColorBits > 8)
+ b |= CR_RGB_BIT;
+ if (pfd.cStencilBits > 0)
+ b |= CR_STENCIL_BIT;
+ if (pfd.cAlphaBits > 0)
+ b |= CR_ALPHA_BIT;
+ if (pfd.dwFlags & PFD_DOUBLEBUFFER)
+ b |= CR_DOUBLE_BIT;
+ if (pfd.dwFlags & PFD_STEREO)
+ b |= CR_STEREO_BIT;
+
+ return b;
+}
+#endif
+
+void APIENTRY DrvReleaseContext(HGLRC hglrc)
+{
+ CR_DDI_PROLOGUE();
+ /*crDebug( "DrvReleaseContext(0x%x) called", hglrc );*/
+ stubMakeCurrent( NULL, NULL );
+}
+
+BOOL APIENTRY DrvValidateVersion(DWORD version)
+{
+ CR_DDI_PROLOGUE();
+ if (stubInit()) {
+ crDebug("DrvValidateVersion %x -> TRUE\n", version);
+ return TRUE;
+ }
+
+ crDebug("DrvValidateVersion %x -> FALSE, going to use system default opengl32.dll\n", version);
+ return FALSE;
+}
+
+//we're not going to change icdTable at runtime, so callback is unused
+PICDTABLE APIENTRY DrvSetContext(HDC hdc, HGLRC hglrc, void *callback)
+{
+ ContextInfo *pContext;
+ WindowInfo *pWindowInfo;
+ BOOL ret = false;
+
+ CR_DDI_PROLOGUE();
+
+ (void) (callback);
+
+ crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ pContext = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+ if (pContext)
+ {
+ pWindowInfo = stubGetWindowInfo(hdc);
+ if (pWindowInfo)
+ ret = stubMakeCurrent(pWindowInfo, pContext);
+ else
+ crError("no window info available.");
+ }
+ else
+ crError("No context found.");
+
+ crHashtableUnlock(stub.contextTable);
+ crHashtableUnlock(stub.windowTable);
+
+ return ret ? &icdTable : NULL;
+}
+
+BOOL APIENTRY DrvSetPixelFormat(HDC hdc, int iPixelFormat)
+{
+ CR_DDI_PROLOGUE();
+ crDebug( "DrvSetPixelFormat(0x%x, %i) called.", hdc, iPixelFormat );
+
+ if ( (iPixelFormat<1) || (iPixelFormat>2) ) {
+ crError( "wglSetPixelFormat: iPixelFormat=%d?", iPixelFormat );
+ }
+
+ return 1;
+}
+
+HGLRC APIENTRY DrvCreateContext(HDC hdc)
+{
+ char dpyName[MAX_DPY_NAME];
+ ContextInfo *context;
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ PVBOXUHGSMI pHgsmi = NULL;
+#endif
+
+ CR_DDI_PROLOGUE();
+
+ crDebug( "DrvCreateContext(0x%x) called.", hdc);
+
+ stubInit();
+
+ CRASSERT(stub.contextTable);
+
+ sprintf(dpyName, "%p", hdc);
+#ifndef VBOX_CROGL_USE_VBITS_SUPERSET
+ if (stub.haveNativeOpenGL)
+ desiredVisual |= ComputeVisBits( hdc );
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ pHgsmi = VBoxCrHgsmiCreate();
+#endif
+
+ context = stubNewContext(dpyName, desiredVisual, UNDECIDED, 0
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ if (!context)
+ return 0;
+
+ return (HGLRC) context->id;
+}
+
+HGLRC APIENTRY DrvCreateLayerContext(HDC hdc, int iLayerPlane)
+{
+ CR_DDI_PROLOGUE();
+ crDebug( "DrvCreateLayerContext(0x%x, %i) called.", hdc, iLayerPlane);
+ //We don't support more than 1 layers.
+ if (iLayerPlane == 0) {
+ return DrvCreateContext(hdc);
+ } else {
+ crError( "DrvCreateLayerContext (%x,%x): unsupported", hdc, iLayerPlane);
+ return NULL;
+ }
+
+}
+
+BOOL APIENTRY DrvDescribeLayerPlane(HDC hdc,int iPixelFormat,
+ int iLayerPlane, UINT nBytes,
+ LPLAYERPLANEDESCRIPTOR plpd)
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "DrvDescribeLayerPlane: unimplemented" );
+ CRASSERT(false);
+ return 0;
+}
+
+int APIENTRY DrvGetLayerPaletteEntries(HDC hdc, int iLayerPlane,
+ int iStart, int cEntries,
+ COLORREF *pcr)
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "DrvGetLayerPaletteEntries: unsupported" );
+ CRASSERT(false);
+ return 0;
+}
+
+int APIENTRY DrvDescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR pfd)
+{
+ CR_DDI_PROLOGUE();
+ if ( !pfd ) {
+ return 2;
+ }
+
+ if ( nBytes != sizeof(*pfd) ) {
+ crWarning( "DrvDescribePixelFormat: nBytes=%u?", nBytes );
+ return 2;
+ }
+
+ if (iPixelFormat==1)
+ {
+ crMemZero(pfd, sizeof(*pfd));
+
+ pfd->nSize = sizeof(*pfd);
+ pfd->nVersion = 1;
+ pfd->dwFlags = (PFD_DRAW_TO_WINDOW |
+ PFD_SUPPORT_OPENGL |
+ PFD_DOUBLEBUFFER);
+
+ pfd->dwFlags |= 0x8000; /* <- Needed for VSG Open Inventor to be happy */
+
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 32;
+ pfd->cRedBits = 8;
+ pfd->cRedShift = 24;
+ pfd->cGreenBits = 8;
+ pfd->cGreenShift = 16;
+ pfd->cBlueBits = 8;
+ pfd->cBlueShift = 8;
+ pfd->cAlphaBits = 8;
+ pfd->cAlphaShift = 0;
+ pfd->cAccumBits = 0;
+ pfd->cAccumRedBits = 0;
+ pfd->cAccumGreenBits = 0;
+ pfd->cAccumBlueBits = 0;
+ pfd->cAccumAlphaBits = 0;
+ pfd->cDepthBits = 32;
+ pfd->cStencilBits = 8;
+ pfd->cAuxBuffers = 0;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+ pfd->bReserved = 0;
+ pfd->dwLayerMask = 0;
+ pfd->dwVisibleMask = 0;
+ pfd->dwDamageMask = 0;
+ }
+ else
+ {
+ crMemZero(pfd, sizeof(*pfd));
+ pfd->nVersion = 1;
+ pfd->dwFlags = (PFD_DRAW_TO_WINDOW|
+ PFD_SUPPORT_OPENGL);
+
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 32;
+ pfd->cRedBits = 8;
+ pfd->cRedShift = 16;
+ pfd->cGreenBits = 8;
+ pfd->cGreenShift = 8;
+ pfd->cBlueBits = 8;
+ pfd->cBlueShift = 0;
+ pfd->cAlphaBits = 0;
+ pfd->cAlphaShift = 0;
+ pfd->cAccumBits = 64;
+ pfd->cAccumRedBits = 16;
+ pfd->cAccumGreenBits = 16;
+ pfd->cAccumBlueBits = 16;
+ pfd->cAccumAlphaBits = 0;
+ pfd->cDepthBits = 16;
+ pfd->cStencilBits = 8;
+ pfd->cAuxBuffers = 0;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+ pfd->bReserved = 0;
+ pfd->dwLayerMask = 0;
+ pfd->dwVisibleMask = 0;
+ pfd->dwDamageMask = 0;
+ }
+
+ /* the max PFD index */
+ return 2;
+}
+
+BOOL APIENTRY DrvDeleteContext(HGLRC hglrc)
+{
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ ContextInfo *pContext;
+ PVBOXUHGSMI pHgsmi = NULL;
+#endif
+
+ CR_DDI_PROLOGUE();
+ crDebug( "DrvDeleteContext(0x%x) called", hglrc );
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ crHashtableLock(stub.contextTable);
+
+ pContext = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+ if (pContext)
+ pHgsmi = pContext->pHgsmi;
+
+ crHashtableUnlock(stub.contextTable);
+#endif
+
+ stubDestroyContext( (unsigned long) hglrc );
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ if (pHgsmi)
+ VBoxCrHgsmiDestroy(pHgsmi);
+#endif
+
+ return true;
+}
+
+BOOL APIENTRY DrvCopyContext(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask)
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "DrvCopyContext: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglShareLists_prox( HGLRC hglrc1, HGLRC hglrc2 );
+
+BOOL APIENTRY DrvShareLists(HGLRC hglrc1, HGLRC hglrc2)
+{
+ return wglShareLists_prox(hglrc1, hglrc2);
+}
+
+int APIENTRY DrvSetLayerPaletteEntries(HDC hdc, int iLayerPlane,
+ int iStart, int cEntries,
+ CONST COLORREF *pcr)
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "DrvSetLayerPaletteEntries: unsupported" );
+ return 0;
+}
+
+
+BOOL APIENTRY DrvRealizeLayerPalette(HDC hdc, int iLayerPlane, BOOL bRealize)
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "DrvRealizeLayerPalette: unsupported" );
+ return 0;
+}
+
+BOOL APIENTRY DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
+{
+ CR_DDI_PROLOGUE();
+ if (fuPlanes == 1)
+ {
+ return DrvSwapBuffers(hdc);
+ }
+ else
+ {
+ crWarning( "DrvSwapLayerBuffers: unsupported" );
+ CRASSERT(false);
+ return 0;
+ }
+}
+
+BOOL APIENTRY DrvSwapBuffers(HDC hdc)
+{
+ WindowInfo *window;
+
+ CR_DDI_PROLOGUE();
+ /*crDebug( "DrvSwapBuffers(0x%x) called", hdc );*/
+ window = stubGetWindowInfo(hdc);
+ stubSwapBuffers( window, 0 );
+ return 1;
+}
+
diff --git a/src/VBox/Additions/common/crOpenGL/icd_drv.h b/src/VBox/Additions/common/crOpenGL/icd_drv.h
new file mode 100644
index 00000000..97004bdd
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/icd_drv.h
@@ -0,0 +1,55 @@
+/* $Id: icd_drv.h $ */
+/** @file
+ * VirtualBox Windows NT/2000/XP guest OpenGL ICD header
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_icd_drv_h
+#define GA_INCLUDED_SRC_common_crOpenGL_icd_drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/win/windows.h>
+
+typedef struct ICDTABLE
+{
+ DWORD size;
+ PROC table[336];
+} ICDTABLE, *PICDTABLE;
+
+void APIENTRY DrvReleaseContext(HGLRC hglrc);
+BOOL APIENTRY DrvValidateVersion(DWORD version);
+PICDTABLE APIENTRY DrvSetContext(HDC hdc, HGLRC hglrc, void *callback);
+BOOL APIENTRY DrvSetPixelFormat(HDC hdc, int iPixelFormat);
+HGLRC APIENTRY DrvCreateContext(HDC hdc);
+HGLRC APIENTRY DrvCreateLayerContext(HDC hdc, int iLayerPlane);
+BOOL APIENTRY DrvDescribeLayerPlane(HDC hdc,int iPixelFormat,
+ int iLayerPlane, UINT nBytes,
+ LPLAYERPLANEDESCRIPTOR plpd);
+int APIENTRY DrvGetLayerPaletteEntries(HDC hdc, int iLayerPlane,
+ int iStart, int cEntries,
+ COLORREF *pcr);
+int APIENTRY DrvDescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR pfd);
+BOOL APIENTRY DrvDeleteContext(HGLRC hglrc);
+BOOL APIENTRY DrvCopyContext(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask);
+BOOL APIENTRY DrvShareLists(HGLRC hglrc1, HGLRC hglrc2);
+int APIENTRY DrvSetLayerPaletteEntries(HDC hdc, int iLayerPlane,
+ int iStart, int cEntries,
+ CONST COLORREF *pcr);
+BOOL APIENTRY DrvRealizeLayerPalette(HDC hdc, int iLayerPlane, BOOL bRealize);
+BOOL APIENTRY DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes);
+BOOL APIENTRY DrvSwapBuffers(HDC hdc);
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_icd_drv_h */
diff --git a/src/VBox/Additions/common/crOpenGL/load.c b/src/VBox/Additions/common/crOpenGL/load.c
new file mode 100644
index 00000000..a4b0cf8c
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/load.c
@@ -0,0 +1,1526 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "cr_net.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "cr_net.h"
+#include "cr_environment.h"
+#include "cr_process.h"
+#include "cr_rand.h"
+#include "cr_netserver.h"
+#include "stub.h"
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <iprt/initterm.h>
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#ifndef WINDOWS
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#ifdef VBOX_WITH_WDDM
+#include <d3d9types.h>
+#include <D3dumddi.h>
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+# include <VBoxCrHgsmi.h>
+#endif
+
+/**
+ * If you change this, see the comments in tilesortspu_context.c
+ */
+#define MAGIC_CONTEXT_BASE 500
+
+#define CONFIG_LOOKUP_FILE ".crconfigs"
+
+#ifdef WINDOWS
+#define PYTHON_EXE "python.exe"
+#else
+#define PYTHON_EXE "python"
+#endif
+
+static bool stub_initialized = 0;
+#ifdef WINDOWS
+static CRmutex stub_init_mutex;
+#define STUB_INIT_LOCK() do { crLockMutex(&stub_init_mutex); } while (0)
+#define STUB_INIT_UNLOCK() do { crUnlockMutex(&stub_init_mutex); } while (0)
+#else
+#define STUB_INIT_LOCK() do { } while (0)
+#define STUB_INIT_UNLOCK() do { } while (0)
+#endif
+
+/* NOTE: 'SPUDispatchTable glim' is declared in NULLfuncs.py now */
+/* NOTE: 'SPUDispatchTable stubThreadsafeDispatch' is declared in tsfuncs.c */
+Stub stub;
+#ifdef CHROMIUM_THREADSAFE
+static bool g_stubIsCurrentContextTSDInited;
+CRtsd g_stubCurrentContextTSD;
+#endif
+
+
+#ifndef VBOX_NO_NATIVEGL
+static void stubInitNativeDispatch( void )
+{
+# define MAX_FUNCS 1000
+ SPUNamedFunctionTable gl_funcs[MAX_FUNCS];
+ int numFuncs;
+
+ numFuncs = crLoadOpenGL( &stub.wsInterface, gl_funcs );
+
+ stub.haveNativeOpenGL = (numFuncs > 0);
+
+ /* XXX call this after context binding */
+ numFuncs += crLoadOpenGLExtensions( &stub.wsInterface, gl_funcs + numFuncs );
+
+ CRASSERT(numFuncs < MAX_FUNCS);
+
+ crSPUInitDispatchTable( &stub.nativeDispatch );
+ crSPUInitDispatch( &stub.nativeDispatch, gl_funcs );
+ crSPUInitDispatchNops( &stub.nativeDispatch );
+# undef MAX_FUNCS
+}
+#endif /* !VBOX_NO_NATIVEGL */
+
+
+/** Pointer to the SPU's real glClear and glViewport functions */
+static ClearFunc_t origClear;
+static ViewportFunc_t origViewport;
+static SwapBuffersFunc_t origSwapBuffers;
+static DrawBufferFunc_t origDrawBuffer;
+static ScissorFunc_t origScissor;
+
+static void stubCheckWindowState(WindowInfo *window, GLboolean bFlushOnChange)
+{
+ bool bForceUpdate = false;
+ bool bChanged = false;
+
+#ifdef WINDOWS
+ /** @todo install hook and track for WM_DISPLAYCHANGE */
+ {
+ DEVMODE devMode;
+
+ devMode.dmSize = sizeof(DEVMODE);
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
+
+ if (devMode.dmPelsWidth!=window->dmPelsWidth || devMode.dmPelsHeight!=window->dmPelsHeight)
+ {
+ crDebug("Resolution changed(%d,%d), forcing window Pos/Size update", devMode.dmPelsWidth, devMode.dmPelsHeight);
+ window->dmPelsWidth = devMode.dmPelsWidth;
+ window->dmPelsHeight = devMode.dmPelsHeight;
+ bForceUpdate = true;
+ }
+ }
+#endif
+
+ bChanged = stubUpdateWindowGeometry(window, bForceUpdate) || bForceUpdate;
+
+#if defined(GLX) || defined (WINDOWS)
+ if (stub.trackWindowVisibleRgn)
+ {
+ bChanged = stubUpdateWindowVisibileRegions(window) || bChanged;
+ }
+#endif
+
+ if (stub.trackWindowVisibility && window->type == CHROMIUM && window->drawable) {
+ const int mapped = stubIsWindowVisible(window);
+ if (mapped != window->mapped) {
+ crDebug("Dispatched: WindowShow(%i, %i)", window->spuWindow, mapped);
+ stub.spu->dispatch_table.WindowShow(window->spuWindow, mapped);
+ window->mapped = mapped;
+ bChanged = true;
+ }
+ }
+
+ if (bFlushOnChange && bChanged)
+ {
+ stub.spu->dispatch_table.Flush();
+ }
+}
+
+static bool stubSystemWindowExist(WindowInfo *pWindow)
+{
+#ifdef WINDOWS
+ if (pWindow->hWnd!=WindowFromDC(pWindow->drawable))
+ {
+ return false;
+ }
+#else
+ Window root;
+ int x, y;
+ unsigned int border, depth, w, h;
+ Display *dpy;
+
+ dpy = stubGetWindowDisplay(pWindow);
+
+ XLOCK(dpy);
+ if (!XGetGeometry(dpy, pWindow->drawable, &root, &x, &y, &w, &h, &border, &depth))
+ {
+ XUNLOCK(dpy);
+ return false;
+ }
+ XUNLOCK(dpy);
+#endif
+
+ return true;
+}
+
+static void stubCheckWindowsCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *pWindow = (WindowInfo *) data1;
+ ContextInfo *pCtx = (ContextInfo *) data2;
+ (void)key;
+
+ if (pWindow == pCtx->currentDrawable
+ || pWindow->type!=CHROMIUM
+ || pWindow->pOwner!=pCtx)
+ {
+ return;
+ }
+
+ if (!stubSystemWindowExist(pWindow))
+ {
+#ifdef WINDOWS
+ stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->hWnd);
+#else
+ stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->drawable);
+#endif
+ return;
+ }
+
+ stubCheckWindowState(pWindow, GL_FALSE);
+}
+
+static void stubCheckWindowsState(void)
+{
+ ContextInfo *context = stubGetCurrentContext();
+
+ CRASSERT(stub.trackWindowSize || stub.trackWindowPos);
+
+ if (!context)
+ return;
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ if (stub.bRunningUnderWDDM)
+ return;
+#endif
+
+ /* Try to keep a consistent locking order. */
+ crHashtableLock(stub.windowTable);
+#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
+ crLockMutex(&stub.mutex);
+#endif
+
+ stubCheckWindowState(context->currentDrawable, GL_TRUE);
+ crHashtableWalkUnlocked(stub.windowTable, stubCheckWindowsCB, context);
+
+#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
+ crUnlockMutex(&stub.mutex);
+#endif
+ crHashtableUnlock(stub.windowTable);
+}
+
+
+/**
+ * Override the head SPU's glClear function.
+ * We're basically trapping this function so that we can poll the
+ * application window size at a regular interval.
+ */
+static void SPU_APIENTRY trapClear(GLbitfield mask)
+{
+ stubCheckWindowsState();
+ /* call the original SPU glClear function */
+ origClear(mask);
+}
+
+/**
+ * As above, but for glViewport. Most apps call glViewport before
+ * glClear when a window is resized.
+ */
+static void SPU_APIENTRY trapViewport(GLint x, GLint y, GLsizei w, GLsizei h)
+{
+ stubCheckWindowsState();
+ /* call the original SPU glViewport function */
+ origViewport(x, y, w, h);
+}
+
+/*static void SPU_APIENTRY trapSwapBuffers(GLint window, GLint flags)
+{
+ stubCheckWindowsState();
+ origSwapBuffers(window, flags);
+}
+
+static void SPU_APIENTRY trapDrawBuffer(GLenum buf)
+{
+ stubCheckWindowsState();
+ origDrawBuffer(buf);
+}*/
+
+#if 0 /* unused */
+static void SPU_APIENTRY trapScissor(GLint x, GLint y, GLsizei w, GLsizei h)
+{
+ int winX, winY;
+ unsigned int winW, winH;
+ WindowInfo *pWindow;
+ ContextInfo *context = stubGetCurrentContext();
+ (void)x; (void)y; (void)w; (void)h;
+
+ pWindow = context->currentDrawable;
+ stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
+ origScissor(0, 0, winW, winH);
+}
+#endif /* unused */
+
+/**
+ * Use the GL function pointers in \<spu\> to initialize the static glim
+ * dispatch table.
+ */
+static void stubInitSPUDispatch(SPU *spu)
+{
+ crSPUInitDispatchTable( &stub.spuDispatch );
+ crSPUCopyDispatchTable( &stub.spuDispatch, &(spu->dispatch_table) );
+
+ if (stub.trackWindowSize || stub.trackWindowPos || stub.trackWindowVisibleRgn) {
+ /* patch-in special glClear/Viewport function to track window sizing */
+ origClear = stub.spuDispatch.Clear;
+ origViewport = stub.spuDispatch.Viewport;
+ origSwapBuffers = stub.spuDispatch.SwapBuffers;
+ origDrawBuffer = stub.spuDispatch.DrawBuffer;
+ origScissor = stub.spuDispatch.Scissor;
+ stub.spuDispatch.Clear = trapClear;
+ stub.spuDispatch.Viewport = trapViewport;
+
+ /*stub.spuDispatch.SwapBuffers = trapSwapBuffers;
+ stub.spuDispatch.DrawBuffer = trapDrawBuffer;*/
+ }
+
+ crSPUCopyDispatchTable( &glim, &stub.spuDispatch );
+}
+
+#if 0 /** @todo stubSPUTearDown & stubSPUTearDownLocked are not referenced */
+
+// Callback function, used to destroy all created contexts
+static void hsWalkStubDestroyContexts(unsigned long key, void *data1, void *data2)
+{
+ (void)data1; (void)data2;
+ stubDestroyContext(key);
+}
+
+/**
+ * This is called when we exit.
+ * We call all the SPU's cleanup functions.
+ */
+static void stubSPUTearDownLocked(void)
+{
+ crDebug("stubSPUTearDownLocked");
+
+#ifdef WINDOWS
+# ifndef CR_NEWWINTRACK
+ stubUninstallWindowMessageHook();
+# endif
+#endif
+
+#ifdef CR_NEWWINTRACK
+ ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
+#endif
+
+ //delete all created contexts
+ stubMakeCurrent( NULL, NULL);
+
+ /* the lock order is windowTable->contextTable (see wglMakeCurrent_prox, glXMakeCurrent)
+ * this is why we need to take a windowTable lock since we will later do stub.windowTable access & locking */
+ crHashtableLock(stub.windowTable);
+ crHashtableWalk(stub.contextTable, hsWalkStubDestroyContexts, NULL);
+ crHashtableUnlock(stub.windowTable);
+
+ /* shutdown, now trap any calls to a NULL dispatcher */
+ crSPUCopyDispatchTable(&glim, &stubNULLDispatch);
+
+ crSPUUnloadChain(stub.spu);
+ stub.spu = NULL;
+
+#ifndef Linux
+ crUnloadOpenGL();
+#endif
+
+#ifndef WINDOWS
+ crNetTearDown();
+#endif
+
+#ifdef GLX
+ if (stub.xshmSI.shmid>=0)
+ {
+ shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
+ shmdt(stub.xshmSI.shmaddr);
+ }
+ crFreeHashtable(stub.pGLXPixmapsHash, crFree);
+#endif
+
+ crFreeHashtable(stub.windowTable, crFree);
+ crFreeHashtable(stub.contextTable, NULL);
+
+ crMemset(&stub, 0, sizeof(stub));
+
+}
+
+/**
+ * This is called when we exit.
+ * We call all the SPU's cleanup functions.
+ */
+static void stubSPUTearDown(void)
+{
+ STUB_INIT_LOCK();
+ if (stub_initialized)
+ {
+ stubSPUTearDownLocked();
+ stub_initialized = 0;
+ }
+ STUB_INIT_UNLOCK();
+}
+
+#endif /** @todo stubSPUTearDown & stubSPUTearDownLocked are not referenced */
+
+static void stubSPUSafeTearDown(void)
+{
+#ifdef CHROMIUM_THREADSAFE
+ CRmutex *mutex;
+#endif
+
+ if (!stub_initialized) return;
+ stub_initialized = 0;
+
+#ifdef CHROMIUM_THREADSAFE
+ mutex = &stub.mutex;
+ crLockMutex(mutex);
+#endif
+ crDebug("stubSPUSafeTearDown");
+
+#ifdef WINDOWS
+# ifndef CR_NEWWINTRACK
+ stubUninstallWindowMessageHook();
+# endif
+#endif
+
+#if defined(CR_NEWWINTRACK)
+ crUnlockMutex(mutex);
+# if defined(WINDOWS)
+ if (stub.hSyncThread && RTThreadGetState(stub.hSyncThread)!=RTTHREADSTATE_TERMINATED)
+ {
+ HANDLE hNative;
+ DWORD ec=0;
+
+ hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE,
+ false, RTThreadGetNative(stub.hSyncThread));
+ if (!hNative)
+ {
+ crWarning("Failed to get handle for sync thread(%#x)", GetLastError());
+ }
+ else
+ {
+ crDebug("Got handle %p for thread %#x", hNative, RTThreadGetNative(stub.hSyncThread));
+ }
+
+ ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
+
+ if (PostThreadMessage(RTThreadGetNative(stub.hSyncThread), WM_QUIT, 0, 0))
+ {
+ RTThreadWait(stub.hSyncThread, 1000, NULL);
+
+ /*Same issue as on linux, RTThreadWait exits before system thread is terminated, which leads
+ * to issues as our dll goes to be unloaded.
+ *@todo
+ *We usually call this function from DllMain which seems to be holding some lock and thus we have to
+ * kill thread via TerminateThread.
+ */
+ if (WaitForSingleObject(hNative, 100)==WAIT_TIMEOUT)
+ {
+ crDebug("Wait failed, terminating");
+ if (!TerminateThread(hNative, 1))
+ {
+ crDebug("TerminateThread failed");
+ }
+ }
+ if (GetExitCodeThread(hNative, &ec))
+ {
+ crDebug("Thread %p exited with ec=%i", hNative, ec);
+ }
+ else
+ {
+ crDebug("GetExitCodeThread failed(%#x)", GetLastError());
+ }
+ }
+ else
+ {
+ crDebug("Sync thread killed before DLL_PROCESS_DETACH");
+ }
+
+ if (hNative)
+ {
+ CloseHandle(hNative);
+ }
+ }
+#else
+ if (stub.hSyncThread!=NIL_RTTHREAD)
+ {
+ ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
+ {
+ int rc = RTThreadWait(stub.hSyncThread, RT_INDEFINITE_WAIT, NULL);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("RTThreadWait_join failed %i", rc));
+ }
+ }
+ }
+#endif
+ crLockMutex(mutex);
+#endif
+
+#ifndef WINDOWS
+ crNetTearDown();
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(mutex);
+ crFreeMutex(mutex);
+#endif
+ crMemset(&stub, 0, sizeof(stub));
+}
+
+
+static void stubExitHandler(void)
+{
+ stubSPUSafeTearDown();
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+}
+
+/**
+ * Called when we receive a SIGTERM signal.
+ */
+static void stubSignalHandler(int signo)
+{
+ (void)signo;
+ stubSPUSafeTearDown();
+ exit(0); /* this causes stubExitHandler() to be called */
+}
+
+#ifndef RT_OS_WINDOWS
+# ifdef CHROMIUM_THREADSAFE
+static void stubThreadTlsDtor(void *pvValue)
+{
+ ContextInfo *pCtx = (ContextInfo*)pvValue;
+ VBoxTlsRefRelease(pCtx);
+}
+# endif
+#endif
+
+
+/**
+ * Init variables in the stub structure, install signal handler.
+ */
+static void stubInitVars(void)
+{
+ WindowInfo *defaultWin;
+
+#ifdef CHROMIUM_THREADSAFE
+ crInitMutex(&stub.mutex);
+#endif
+
+ /* At the very least we want CR_RGB_BIT. */
+ stub.haveNativeOpenGL = GL_FALSE;
+ stub.spu = NULL;
+ stub.appDrawCursor = 0;
+ stub.minChromiumWindowWidth = 0;
+ stub.minChromiumWindowHeight = 0;
+ stub.maxChromiumWindowWidth = 0;
+ stub.maxChromiumWindowHeight = 0;
+ stub.matchChromiumWindowCount = 0;
+ stub.matchChromiumWindowID = NULL;
+ stub.matchWindowTitle = NULL;
+ stub.ignoreFreeglutMenus = 0;
+ stub.threadSafe = GL_FALSE;
+ stub.trackWindowSize = 0;
+ stub.trackWindowPos = 0;
+ stub.trackWindowVisibility = 0;
+ stub.trackWindowVisibleRgn = 0;
+ stub.mothershipPID = 0;
+ stub.spu_dir = NULL;
+
+ stub.freeContextNumber = MAGIC_CONTEXT_BASE;
+ stub.contextTable = crAllocHashtable();
+#ifndef RT_OS_WINDOWS
+# ifdef CHROMIUM_THREADSAFE
+ if (!g_stubIsCurrentContextTSDInited)
+ {
+ crInitTSDF(&g_stubCurrentContextTSD, stubThreadTlsDtor);
+ g_stubIsCurrentContextTSDInited = true;
+ }
+# endif
+#endif
+ stubSetCurrentContext(NULL);
+
+ stub.windowTable = crAllocHashtable();
+
+#ifdef CR_NEWWINTRACK
+ stub.bShutdownSyncThread = false;
+ stub.hSyncThread = NIL_RTTHREAD;
+#endif
+
+ defaultWin = (WindowInfo *) crCalloc(sizeof(WindowInfo));
+ defaultWin->type = CHROMIUM;
+ defaultWin->spuWindow = 0; /* window 0 always exists */
+#ifdef WINDOWS
+ defaultWin->hVisibleRegion = INVALID_HANDLE_VALUE;
+#elif defined(GLX)
+ defaultWin->pVisibleRegions = NULL;
+ defaultWin->cVisibleRegions = 0;
+#endif
+ crHashtableAdd(stub.windowTable, 0, defaultWin);
+
+#if 1
+ atexit(stubExitHandler);
+ signal(SIGTERM, stubSignalHandler);
+ signal(SIGINT, stubSignalHandler);
+#ifndef WINDOWS
+ signal(SIGPIPE, SIG_IGN); /* the networking code should catch this */
+#endif
+#else
+ (void) stubExitHandler;
+ (void) stubSignalHandler;
+#endif
+}
+
+
+#if 0 /* unused */
+
+/**
+ * Return a free port number for the mothership to use, or -1 if we
+ * can't find one.
+ */
+static int
+GenerateMothershipPort(void)
+{
+ const int MAX_PORT = 10100;
+ unsigned short port;
+
+ /* generate initial port number randomly */
+ crRandAutoSeed();
+ port = (unsigned short) crRandInt(10001, MAX_PORT);
+
+#ifdef WINDOWS
+ /* XXX should implement a free port check here */
+ return port;
+#else
+ /*
+ * See if this port number really is free, try another if needed.
+ */
+ {
+ struct sockaddr_in servaddr;
+ int so_reuseaddr = 1;
+ int sock, k;
+
+ /* create socket */
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ CRASSERT(sock > 2);
+
+ /* deallocate socket/port when we exit */
+ k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &so_reuseaddr, sizeof(so_reuseaddr));
+ CRASSERT(k == 0);
+
+ /* initialize the servaddr struct */
+ crMemset(&servaddr, 0, sizeof(servaddr) );
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ while (port < MAX_PORT) {
+ /* Bind to the given port number, return -1 if we fail */
+ servaddr.sin_port = htons((unsigned short) port);
+ k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
+ if (k) {
+ /* failed to create port. try next one. */
+ port++;
+ }
+ else {
+ /* free the socket/port now so mothership can make it */
+ close(sock);
+ return port;
+ }
+ }
+ }
+#endif /* WINDOWS */
+ return -1;
+}
+
+
+/**
+ * Try to determine which mothership configuration to use for this program.
+ */
+static char **
+LookupMothershipConfig(const char *procName)
+{
+ const int procNameLen = crStrlen(procName);
+ FILE *f;
+ const char *home;
+ char configPath[1000];
+
+ /* first, check if the CR_CONFIG env var is set */
+ {
+ const char *conf = crGetenv("CR_CONFIG");
+ if (conf && crStrlen(conf) > 0)
+ return crStrSplit(conf, " ");
+ }
+
+ /* second, look up config name from config file */
+ home = crGetenv("HOME");
+ if (home)
+ sprintf(configPath, "%s/%s", home, CONFIG_LOOKUP_FILE);
+ else
+ crStrcpy(configPath, CONFIG_LOOKUP_FILE); /* from current dir */
+ /* Check if the CR_CONFIG_PATH env var is set. */
+ {
+ const char *conf = crGetenv("CR_CONFIG_PATH");
+ if (conf)
+ crStrcpy(configPath, conf); /* from env var */
+ }
+
+ f = fopen(configPath, "r");
+ if (!f) {
+ return NULL;
+ }
+
+ while (!feof(f)) {
+ char line[1000];
+ char **args;
+ fgets(line, 999, f);
+ line[crStrlen(line) - 1] = 0; /* remove trailing newline */
+ if (crStrncmp(line, procName, procNameLen) == 0 &&
+ (line[procNameLen] == ' ' || line[procNameLen] == '\t'))
+ {
+ crWarning("Using Chromium configuration for %s from %s",
+ procName, configPath);
+ args = crStrSplit(line + procNameLen + 1, " ");
+ return args;
+ }
+ }
+ fclose(f);
+ return NULL;
+}
+
+
+static int Mothership_Awake = 0;
+
+
+/**
+ * Signal handler to determine when mothership is ready.
+ */
+static void
+MothershipPhoneHome(int signo)
+{
+ crDebug("Got signal %d: mothership is awake!", signo);
+ Mothership_Awake = 1;
+}
+
+#endif /* 0 */
+
+static void stubSetDefaultConfigurationOptions(void)
+{
+ unsigned char key[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ stub.appDrawCursor = 0;
+ stub.minChromiumWindowWidth = 0;
+ stub.minChromiumWindowHeight = 0;
+ stub.maxChromiumWindowWidth = 0;
+ stub.maxChromiumWindowHeight = 0;
+ stub.matchChromiumWindowID = NULL;
+ stub.numIgnoreWindowID = 0;
+ stub.matchWindowTitle = NULL;
+ stub.ignoreFreeglutMenus = 0;
+ stub.trackWindowSize = 1;
+ stub.trackWindowPos = 1;
+ stub.trackWindowVisibility = 1;
+ stub.trackWindowVisibleRgn = 1;
+ stub.matchChromiumWindowCount = 0;
+ stub.spu_dir = NULL;
+ crNetSetRank(0);
+ crNetSetContextRange(32, 35);
+ crNetSetNodeRange("iam0", "iamvis20");
+ crNetSetKey(key,sizeof(key));
+ stub.force_pbuffers = 0;
+
+#ifdef WINDOWS
+# ifdef VBOX_WITH_WDDM
+ stub.bRunningUnderWDDM = false;
+# endif
+#endif
+}
+
+#ifdef CR_NEWWINTRACK
+# ifdef VBOX_WITH_WDDM
+static void stubDispatchVisibleRegions(WindowInfo *pWindow)
+{
+ DWORD dwCount;
+ LPRGNDATA lpRgnData;
+
+ dwCount = GetRegionData(pWindow->hVisibleRegion, 0, NULL);
+ lpRgnData = crAlloc(dwCount);
+
+ if (lpRgnData)
+ {
+ GetRegionData(pWindow->hVisibleRegion, dwCount, lpRgnData);
+ crDebug("Dispatched WindowVisibleRegion (%i, cRects=%i)", pWindow->spuWindow, lpRgnData->rdh.nCount);
+ stub.spuDispatch.WindowVisibleRegion(pWindow->spuWindow, lpRgnData->rdh.nCount, (GLint*) lpRgnData->Buffer);
+ crFree(lpRgnData);
+ }
+ else crWarning("GetRegionData failed, VisibleRegions update failed");
+}
+
+# endif /* VBOX_WITH_WDDM */
+
+static void stubSyncTrCheckWindowsCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *pWindow = (WindowInfo *) data1;
+ (void)key; (void) data2;
+
+ if (pWindow->type!=CHROMIUM || pWindow->spuWindow==0)
+ {
+ return;
+ }
+
+ stub.spu->dispatch_table.VBoxPackSetInjectID(pWindow->u32ClientID);
+
+ if (!stubSystemWindowExist(pWindow))
+ {
+#ifdef WINDOWS
+ stubDestroyWindow(0, (GLint)pWindow->hWnd);
+#else
+ stubDestroyWindow(0, (GLint)pWindow->drawable);
+#endif
+ /*No need to flush here as crWindowDestroy does it*/
+ return;
+ }
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ if (stub.bRunningUnderWDDM)
+ return;
+#endif
+ stubCheckWindowState(pWindow, GL_TRUE);
+}
+
+static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser)
+{
+#ifdef WINDOWS
+ MSG msg;
+# ifdef VBOX_WITH_WDDM
+ HMODULE hVBoxD3D = NULL;
+ GLint spuConnection = 0;
+# endif
+#endif
+
+ (void) pvUser;
+
+ crDebug("Sync thread started");
+#ifdef WINDOWS
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+# ifdef VBOX_WITH_WDDM
+ hVBoxD3D = NULL;
+ if (!GetModuleHandleEx(0, VBOX_MODNAME_DISPD3D, &hVBoxD3D))
+ {
+ crDebug("GetModuleHandleEx failed err %d", GetLastError());
+ hVBoxD3D = NULL;
+ }
+
+ if (hVBoxD3D)
+ {
+ crDebug("running with " VBOX_MODNAME_DISPD3D);
+ stub.trackWindowVisibleRgn = 0;
+ stub.bRunningUnderWDDM = true;
+ }
+# endif /* VBOX_WITH_WDDM */
+#endif /* WINDOWS */
+
+ crLockMutex(&stub.mutex);
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ spuConnection =
+#endif
+ stub.spu->dispatch_table.VBoxPackSetInjectThread(NULL);
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ if (stub.bRunningUnderWDDM && !spuConnection)
+ {
+ crError("VBoxPackSetInjectThread failed!");
+ }
+#endif
+ crUnlockMutex(&stub.mutex);
+
+ RTThreadUserSignal(ThreadSelf);
+
+ while(!stub.bShutdownSyncThread)
+ {
+#ifdef WINDOWS
+ if (!PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
+ {
+# ifdef VBOX_WITH_WDDM
+ if (stub.bRunningUnderWDDM)
+ {
+
+ }
+ else
+# endif
+ {
+ crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL);
+ RTThreadSleep(50);
+ }
+ }
+ else
+ {
+ if (WM_QUIT==msg.message)
+ {
+ crDebug("Sync thread got WM_QUIT");
+ break;
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+#else
+ /* Try to keep a consistent locking order. */
+ crHashtableLock(stub.windowTable);
+ crLockMutex(&stub.mutex);
+ crHashtableWalkUnlocked(stub.windowTable, stubSyncTrCheckWindowsCB, NULL);
+ crUnlockMutex(&stub.mutex);
+ crHashtableUnlock(stub.windowTable);
+ RTThreadSleep(50);
+#endif
+ }
+
+#ifdef VBOX_WITH_WDDM
+ if (spuConnection)
+ {
+ stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
+ }
+ if (hVBoxD3D)
+ {
+ FreeLibrary(hVBoxD3D);
+ }
+#endif
+ crDebug("Sync thread stopped");
+ return 0;
+}
+#endif /* CR_NEWWINTRACK */
+
+/**
+ * Do one-time initializations for the faker.
+ * Returns TRUE on success, FALSE otherwise.
+ */
+static bool
+stubInitLocked(void)
+{
+ /* Here is where we contact the mothership to find out what we're supposed
+ * to be doing. Networking code in a DLL initializer. I sure hope this
+ * works :)
+ *
+ * HOW can I pass the mothership address to this if I already know it?
+ */
+
+ char response[1024];
+ char **spuchain;
+ int num_spus;
+ int *spu_ids;
+ char **spu_names;
+ const char *app_id;
+ int i;
+ int disable_sync = 0;
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ HMODULE hVBoxD3D = NULL;
+#endif
+
+ stubInitVars();
+
+ crGetProcName(response, 1024);
+ crDebug("Stub launched for %s", response);
+
+#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
+ /** @todo when vm boots with compiz turned on, new code causes hang in xcb_wait_for_reply in the sync thread
+ * as at the start compiz runs our code under XGrabServer.
+ */
+ if (!crStrcmp(response, "compiz") || !crStrcmp(response, "compiz_real") || !crStrcmp(response, "compiz.real")
+ || !crStrcmp(response, "compiz-bin"))
+ {
+ disable_sync = 1;
+ }
+#endif
+
+ /** @todo check if it'd be of any use on other than guests, no use for windows */
+ app_id = crGetenv( "CR_APPLICATION_ID_NUMBER" );
+
+ crNetInit( NULL, NULL );
+
+#ifndef WINDOWS
+ {
+ CRNetServer ns;
+
+ ns.name = "vboxhgcm://host:0";
+ ns.buffer_size = 1024;
+ crNetServerConnect(&ns
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , NULL
+#endif
+ );
+ if (!ns.conn)
+ {
+ crWarning("Failed to connect to host. Make sure 3D acceleration is enabled for this VM.");
+# ifdef VBOXOGL_FAKEDRI
+ return false;
+# else
+ exit(1);
+# endif
+ }
+ else
+ {
+ crNetFreeConnection(ns.conn);
+ }
+ }
+#endif
+
+ strcpy(response, "2 0 feedback 1 pack");
+ spuchain = crStrSplit( response, " " );
+ num_spus = crStrToInt( spuchain[0] );
+ spu_ids = (int *) crAlloc( num_spus * sizeof( *spu_ids ) );
+ spu_names = (char **) crAlloc( num_spus * sizeof( *spu_names ) );
+ for (i = 0 ; i < num_spus ; i++)
+ {
+ spu_ids[i] = crStrToInt( spuchain[2*i+1] );
+ spu_names[i] = crStrdup( spuchain[2*i+2] );
+ crDebug( "SPU %d/%d: (%d) \"%s\"", i+1, num_spus, spu_ids[i], spu_names[i] );
+ }
+
+ stubSetDefaultConfigurationOptions();
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ hVBoxD3D = NULL;
+ if (!GetModuleHandleEx(0, VBOX_MODNAME_DISPD3D, &hVBoxD3D))
+ {
+ crDebug("GetModuleHandleEx failed err %d", GetLastError());
+ hVBoxD3D = NULL;
+ }
+
+ if (hVBoxD3D)
+ {
+ disable_sync = 1;
+ crDebug("running with %s", VBOX_MODNAME_DISPD3D);
+ stub.trackWindowVisibleRgn = 0;
+ /** @todo should we enable that? */
+ stub.trackWindowSize = 0;
+ stub.trackWindowPos = 0;
+ stub.trackWindowVisibility = 0;
+ stub.bRunningUnderWDDM = true;
+ }
+#endif
+
+ stub.spu = crSPULoadChain( num_spus, spu_ids, spu_names, stub.spu_dir, NULL );
+
+ crFree( spuchain );
+ crFree( spu_ids );
+ for (i = 0; i < num_spus; ++i)
+ crFree(spu_names[i]);
+ crFree( spu_names );
+
+ // spu chain load failed somewhere
+ if (!stub.spu) {
+ return false;
+ }
+
+ crSPUInitDispatchTable( &glim );
+
+ /* This is unlikely to change -- We still want to initialize our dispatch
+ * table with the functions of the first SPU in the chain. */
+ stubInitSPUDispatch( stub.spu );
+
+ /* we need to plug one special stub function into the dispatch table */
+ glim.GetChromiumParametervCR = stub_GetChromiumParametervCR;
+
+#if !defined(VBOX_NO_NATIVEGL)
+ /* Load pointers to native OpenGL functions into stub.nativeDispatch */
+ stubInitNativeDispatch();
+#endif
+
+/*crDebug("stub init");
+raise(SIGINT);*/
+
+#ifdef WINDOWS
+# ifndef CR_NEWWINTRACK
+ stubInstallWindowMessageHook();
+# endif
+#endif
+
+#ifdef CR_NEWWINTRACK
+ {
+ int rc;
+
+ RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE);
+
+ if (!disable_sync)
+ {
+ crDebug("Starting sync thread");
+
+ rc = RTThreadCreate(&stub.hSyncThread, stubSyncThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Sync");
+ if (RT_FAILURE(rc))
+ {
+ crError("Failed to start sync thread! (%x)", rc);
+ }
+ RTThreadUserWait(stub.hSyncThread, 60 * 1000);
+ RTThreadUserReset(stub.hSyncThread);
+
+ crDebug("Going on");
+ }
+ }
+#endif
+
+#ifdef GLX
+ stub.xshmSI.shmid = -1;
+ stub.bShmInitFailed = GL_FALSE;
+ stub.pGLXPixmapsHash = crAllocHashtable();
+
+ stub.bXExtensionsChecked = GL_FALSE;
+ stub.bHaveXComposite = GL_FALSE;
+ stub.bHaveXFixes = GL_FALSE;
+#endif
+
+ return true;
+}
+
+/**
+ * Do one-time initializations for the faker.
+ * Returns TRUE on success, FALSE otherwise.
+ */
+bool
+stubInit(void)
+{
+ bool bRc = true;
+ /* we need to serialize the initialization, otherwise racing is possible
+ * for XPDM-based d3d when a d3d switcher is testing the gl lib in two or more threads
+ * NOTE: the STUB_INIT_LOCK/UNLOCK is a NOP for non-win currently */
+ STUB_INIT_LOCK();
+ if (!stub_initialized)
+ bRc = stub_initialized = stubInitLocked();
+ STUB_INIT_UNLOCK();
+ return bRc;
+}
+
+/* Sigh -- we can't do initialization at load time, since Windows forbids
+ * the loading of other libraries from DLLMain. */
+
+#ifdef WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#if 1//def DEBUG_misha
+ /* debugging: this is to be able to catch first-chance notifications
+ * for exceptions other than EXCEPTION_BREAKPOINT in kernel debugger */
+# define VDBG_VEHANDLER
+#endif
+
+#ifdef VDBG_VEHANDLER
+# include <dbghelp.h>
+# include <cr_string.h>
+static PVOID g_VBoxVehHandler = NULL;
+static DWORD g_VBoxVehEnable = 0;
+
+/* generate a crash dump on exception */
+#define VBOXVEH_F_DUMP 0x00000001
+/* generate a debugger breakpoint exception */
+#define VBOXVEH_F_BREAK 0x00000002
+/* exit on exception */
+#define VBOXVEH_F_EXIT 0x00000004
+
+static DWORD g_VBoxVehFlags = 0;
+
+typedef BOOL WINAPI FNVBOXDBG_MINIDUMPWRITEDUMP(HANDLE hProcess,
+ DWORD ProcessId,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+typedef FNVBOXDBG_MINIDUMPWRITEDUMP *PFNVBOXDBG_MINIDUMPWRITEDUMP;
+
+static HMODULE g_hVBoxMdDbgHelp = NULL;
+static PFNVBOXDBG_MINIDUMPWRITEDUMP g_pfnVBoxMdMiniDumpWriteDump = NULL;
+static size_t g_cVBoxMdFilePrefixLen = 0;
+static WCHAR g_aszwVBoxMdFilePrefix[MAX_PATH];
+static WCHAR g_aszwVBoxMdDumpCount = 0;
+static MINIDUMP_TYPE g_enmVBoxMdDumpType = MiniDumpNormal
+ | MiniDumpWithDataSegs
+ | MiniDumpWithFullMemory
+ | MiniDumpWithHandleData
+//// | MiniDumpFilterMemory
+//// | MiniDumpScanMemory
+// | MiniDumpWithUnloadedModules
+//// | MiniDumpWithIndirectlyReferencedMemory
+//// | MiniDumpFilterModulePaths
+// | MiniDumpWithProcessThreadData
+// | MiniDumpWithPrivateReadWriteMemory
+//// | MiniDumpWithoutOptionalData
+// | MiniDumpWithFullMemoryInfo
+// | MiniDumpWithThreadInfo
+// | MiniDumpWithCodeSegs
+// | MiniDumpWithFullAuxiliaryState
+// | MiniDumpWithPrivateWriteCopyMemory
+// | MiniDumpIgnoreInaccessibleMemory
+// | MiniDumpWithTokenInformation
+//// | MiniDumpWithModuleHeaders
+//// | MiniDumpFilterTriage
+ ;
+
+
+
+#define VBOXMD_DUMP_DIR_DEFAULT "C:\\dumps"
+#define VBOXMD_DUMP_NAME_PREFIX_W L"VBoxDmp_"
+
+static HMODULE loadSystemDll(const char *pszName)
+{
+#ifndef DEBUG
+ char szPath[MAX_PATH];
+ UINT cchPath = GetSystemDirectoryA(szPath, sizeof(szPath));
+ size_t cbName = strlen(pszName) + 1;
+ if (cchPath + 1 + cbName > sizeof(szPath))
+ {
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+ return NULL;
+ }
+ szPath[cchPath] = '\\';
+ memcpy(&szPath[cchPath + 1], pszName, cbName);
+ return LoadLibraryA(szPath);
+#else
+ return LoadLibraryA(pszName);
+#endif
+}
+
+static DWORD vboxMdMinidumpCreate(struct _EXCEPTION_POINTERS *pExceptionInfo)
+{
+ WCHAR aszwMdFileName[MAX_PATH];
+ HANDLE hProcess = GetCurrentProcess();
+ DWORD ProcessId = GetCurrentProcessId();
+ MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo;
+ HANDLE hFile;
+ DWORD winErr = ERROR_SUCCESS;
+
+ if (!g_pfnVBoxMdMiniDumpWriteDump)
+ {
+ if (!g_hVBoxMdDbgHelp)
+ {
+ g_hVBoxMdDbgHelp = loadSystemDll("DbgHelp.dll");
+ if (!g_hVBoxMdDbgHelp)
+ return GetLastError();
+ }
+
+ g_pfnVBoxMdMiniDumpWriteDump = (PFNVBOXDBG_MINIDUMPWRITEDUMP)GetProcAddress(g_hVBoxMdDbgHelp, "MiniDumpWriteDump");
+ if (!g_pfnVBoxMdMiniDumpWriteDump)
+ return GetLastError();
+ }
+
+ ++g_aszwVBoxMdDumpCount;
+
+ memcpy(aszwMdFileName, g_aszwVBoxMdFilePrefix, g_cVBoxMdFilePrefixLen * sizeof (g_aszwVBoxMdFilePrefix[0]));
+ swprintf(aszwMdFileName + g_cVBoxMdFilePrefixLen, RT_ELEMENTS(aszwMdFileName) - g_cVBoxMdFilePrefixLen, L"%d_%d.dmp", ProcessId, g_aszwVBoxMdDumpCount);
+
+ hFile = CreateFileW(aszwMdFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return GetLastError();
+
+ ExceptionInfo.ThreadId = GetCurrentThreadId();
+ ExceptionInfo.ExceptionPointers = pExceptionInfo;
+ ExceptionInfo.ClientPointers = FALSE;
+
+ if (!g_pfnVBoxMdMiniDumpWriteDump(hProcess, ProcessId, hFile, g_enmVBoxMdDumpType, &ExceptionInfo, NULL, NULL))
+ winErr = GetLastError();
+
+ CloseHandle(hFile);
+ return winErr;
+}
+
+LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo)
+{
+ PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+ PCONTEXT pContextRecord = pExceptionInfo->ContextRecord;
+ switch (pExceptionRecord->ExceptionCode)
+ {
+ case EXCEPTION_BREAKPOINT:
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_STACK_OVERFLOW:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ if (g_VBoxVehFlags & VBOXVEH_F_BREAK)
+ {
+ BOOL fBreak = TRUE;
+#ifndef DEBUG_misha
+ if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
+ {
+ HANDLE hProcess = GetCurrentProcess();
+ BOOL fDebuggerPresent = FALSE;
+ /* we do not want to generate breakpoint exceptions recursively, so do it only when running under debugger */
+ if (CheckRemoteDebuggerPresent(hProcess, &fDebuggerPresent))
+ fBreak = !!fDebuggerPresent;
+ else
+ fBreak = FALSE; /* <- the function has failed, don't break for sanity */
+ }
+#endif
+
+ if (fBreak)
+ {
+ RT_BREAKPOINT();
+ }
+ }
+
+ if (g_VBoxVehFlags & VBOXVEH_F_DUMP)
+ vboxMdMinidumpCreate(pExceptionInfo);
+
+ if (g_VBoxVehFlags & VBOXVEH_F_EXIT)
+ exit(1);
+ break;
+ default:
+ break;
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void vboxVDbgVEHandlerRegister()
+{
+ CRASSERT(!g_VBoxVehHandler);
+ g_VBoxVehHandler = AddVectoredExceptionHandler(1,vboxVDbgVectoredHandler);
+ CRASSERT(g_VBoxVehHandler);
+}
+
+void vboxVDbgVEHandlerUnregister()
+{
+ ULONG uResult;
+ if (g_VBoxVehHandler)
+ {
+ uResult = RemoveVectoredExceptionHandler(g_VBoxVehHandler);
+ CRASSERT(uResult);
+ g_VBoxVehHandler = NULL;
+ }
+}
+#endif
+
+/* Windows crap */
+BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ (void) lpvReserved;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ CRNetServer ns;
+ const char * env;
+#if defined(DEBUG_misha)
+ HMODULE hCrUtil;
+ char aName[MAX_PATH];
+
+ GetModuleFileNameA(hDLLInst, aName, RT_ELEMENTS(aName));
+ crDbgCmdSymLoadPrint(aName, hDLLInst);
+
+ hCrUtil = GetModuleHandleA("VBoxOGLcrutil.dll");
+ Assert(hCrUtil);
+ crDbgCmdSymLoadPrint("VBoxOGLcrutil.dll", hCrUtil);
+#endif
+#ifdef CHROMIUM_THREADSAFE
+ crInitTSD(&g_stubCurrentContextTSD);
+#endif
+
+ crInitMutex(&stub_init_mutex);
+
+#ifdef VDBG_VEHANDLER
+ env = crGetenv("CR_DBG_VEH_ENABLE");
+ g_VBoxVehEnable = crStrParseI32(env,
+# ifdef DEBUG_misha
+ 1
+# else
+ 0
+# endif
+ );
+
+ if (g_VBoxVehEnable)
+ {
+ char procName[1024];
+ size_t cProcName;
+ size_t cChars;
+
+ env = crGetenv("CR_DBG_VEH_FLAGS");
+ g_VBoxVehFlags = crStrParseI32(env,
+ 0
+# ifdef DEBUG_misha
+ | VBOXVEH_F_BREAK
+# else
+ | VBOXVEH_F_DUMP
+# endif
+ );
+
+ env = crGetenv("CR_DBG_VEH_DUMP_DIR");
+ if (!env)
+ env = VBOXMD_DUMP_DIR_DEFAULT;
+
+ g_cVBoxMdFilePrefixLen = strlen(env);
+
+ if (RT_ELEMENTS(g_aszwVBoxMdFilePrefix) <= g_cVBoxMdFilePrefixLen + 26 + (sizeof (VBOXMD_DUMP_NAME_PREFIX_W) - sizeof (WCHAR)) / sizeof (WCHAR))
+ {
+ g_cVBoxMdFilePrefixLen = 0;
+ env = "";
+ }
+
+ mbstowcs_s(&cChars, g_aszwVBoxMdFilePrefix, g_cVBoxMdFilePrefixLen + 1, env, _TRUNCATE);
+
+ Assert(cChars == g_cVBoxMdFilePrefixLen + 1);
+
+ g_cVBoxMdFilePrefixLen = cChars - 1;
+
+ if (g_cVBoxMdFilePrefixLen && g_aszwVBoxMdFilePrefix[g_cVBoxMdFilePrefixLen - 1] != L'\\')
+ g_aszwVBoxMdFilePrefix[g_cVBoxMdFilePrefixLen++] = L'\\';
+
+ memcpy(g_aszwVBoxMdFilePrefix + g_cVBoxMdFilePrefixLen, VBOXMD_DUMP_NAME_PREFIX_W, sizeof (VBOXMD_DUMP_NAME_PREFIX_W) - sizeof (WCHAR));
+ g_cVBoxMdFilePrefixLen += (sizeof (VBOXMD_DUMP_NAME_PREFIX_W) - sizeof (WCHAR)) / sizeof (WCHAR);
+
+ crGetProcName(procName, RT_ELEMENTS(procName));
+ cProcName = strlen(procName);
+
+ if (RT_ELEMENTS(g_aszwVBoxMdFilePrefix) > g_cVBoxMdFilePrefixLen + cProcName + 1 + 26)
+ {
+ mbstowcs_s(&cChars, g_aszwVBoxMdFilePrefix + g_cVBoxMdFilePrefixLen, cProcName + 1, procName, _TRUNCATE);
+ Assert(cChars == cProcName + 1);
+ g_cVBoxMdFilePrefixLen += cChars - 1;
+ g_aszwVBoxMdFilePrefix[g_cVBoxMdFilePrefixLen++] = L'_';
+ }
+
+ /* sanity */
+ g_aszwVBoxMdFilePrefix[g_cVBoxMdFilePrefixLen] = L'\0';
+
+ env = crGetenv("CR_DBG_VEH_DUMP_TYPE");
+
+ g_enmVBoxMdDumpType = crStrParseI32(env,
+ MiniDumpNormal
+ | MiniDumpWithDataSegs
+ | MiniDumpWithFullMemory
+ | MiniDumpWithHandleData
+ //// | MiniDumpFilterMemory
+ //// | MiniDumpScanMemory
+ // | MiniDumpWithUnloadedModules
+ //// | MiniDumpWithIndirectlyReferencedMemory
+ //// | MiniDumpFilterModulePaths
+ // | MiniDumpWithProcessThreadData
+ // | MiniDumpWithPrivateReadWriteMemory
+ //// | MiniDumpWithoutOptionalData
+ // | MiniDumpWithFullMemoryInfo
+ // | MiniDumpWithThreadInfo
+ // | MiniDumpWithCodeSegs
+ // | MiniDumpWithFullAuxiliaryState
+ // | MiniDumpWithPrivateWriteCopyMemory
+ // | MiniDumpIgnoreInaccessibleMemory
+ // | MiniDumpWithTokenInformation
+ //// | MiniDumpWithModuleHeaders
+ //// | MiniDumpFilterTriage
+ );
+
+ vboxVDbgVEHandlerRegister();
+ }
+#endif
+
+ crNetInit(NULL, NULL);
+ ns.name = "vboxhgcm://host:0";
+ ns.buffer_size = 1024;
+ crNetServerConnect(&ns
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , NULL
+#endif
+);
+ if (!ns.conn)
+ {
+ crDebug("Failed to connect to host (is guest 3d acceleration enabled?), aborting ICD load.");
+#ifdef VDBG_VEHANDLER
+ if (g_VBoxVehEnable)
+ vboxVDbgVEHandlerUnregister();
+#endif
+ return FALSE;
+ }
+ else
+ {
+ crNetFreeConnection(ns.conn);
+ }
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ VBoxCrHgsmiInit();
+#endif
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ /* do exactly the same thing as for DLL_THREAD_DETACH since
+ * DLL_THREAD_DETACH is not called for the thread doing DLL_PROCESS_DETACH according to msdn docs */
+ stubSetCurrentContext(NULL);
+ if (stub_initialized)
+ {
+ CRASSERT(stub.spu);
+ stub.spu->dispatch_table.VBoxDetachThread();
+ }
+
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ VBoxCrHgsmiTerm();
+#endif
+
+ stubSPUSafeTearDown();
+
+#ifdef CHROMIUM_THREADSAFE
+ crFreeTSD(&g_stubCurrentContextTSD);
+#endif
+
+#ifdef VDBG_VEHANDLER
+ if (g_VBoxVehEnable)
+ vboxVDbgVEHandlerUnregister();
+#endif
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ {
+ if (stub_initialized)
+ {
+ CRASSERT(stub.spu);
+ stub.spu->dispatch_table.VBoxAttachThread();
+ }
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ {
+ stubSetCurrentContext(NULL);
+ if (stub_initialized)
+ {
+ CRASSERT(stub.spu);
+ stub.spu->dispatch_table.VBoxDetachThread();
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+#endif
diff --git a/src/VBox/Additions/common/crOpenGL/pack/Makefile.kup b/src/VBox/Additions/common/crOpenGL/pack/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/Makefile.kup
diff --git a/src/VBox/Additions/common/crOpenGL/pack/pack.def b/src/VBox/Additions/common/crOpenGL/pack/pack.def
new file mode 100644
index 00000000..9edc7163
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/pack.def
@@ -0,0 +1,6 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
diff --git a/src/VBox/Additions/common/crOpenGL/pack/pack.py b/src/VBox/Additions/common/crOpenGL/pack/pack.py
new file mode 100755
index 00000000..e25066be
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/pack.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY pack.py SCRIPT */
+#include <stdio.h>
+#include "cr_string.h"
+#include "cr_spu.h"
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "packspu_proto.h"
+""")
+
+num_funcs = len(keys) - len(apiutil.AllSpecials('packspu_unimplemented'))
+print('SPUNamedFunctionTable _cr_pack_table[%d];' % (num_funcs+1))
+
+print("""
+static void __fillin(int offset, char *name, SPUGenericFunction func)
+{
+ _cr_pack_table[offset].name = crStrdup(name);
+ _cr_pack_table[offset].fn = func;
+}""")
+
+pack_specials = []
+
+for func_name in keys:
+ if ("get" in apiutil.Properties(func_name) or
+ apiutil.FindSpecial( "packspu", func_name ) or
+ apiutil.FindSpecial( "packspu_flush", func_name ) or
+ apiutil.FindSpecial( "packspu_vertex", func_name )):
+ pack_specials.append( func_name )
+
+print('\nvoid packspuCreateFunctions( void )')
+print('{')
+for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.FindSpecial( "packspu_unimplemented", func_name ):
+ continue
+ if func_name in pack_specials:
+ print('\t__fillin(%3d, "%s", (SPUGenericFunction) packspu_%s);' % (index, func_name, func_name ))
+ else:
+ print('\t__fillin(%3d, "%s", (SPUGenericFunction) (pack_spu.swap ? crPack%sSWAP : crPack%s));' % (index, func_name, func_name, func_name ))
+print('\t__fillin(%3d, NULL, NULL);' % num_funcs)
+print('}')
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu.h b/src/VBox/Additions/common/crOpenGL/pack/packspu.h
new file mode 100644
index 00000000..9774e35b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu.h
@@ -0,0 +1,185 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_pack_packspu_h
+#define GA_INCLUDED_SRC_common_crOpenGL_pack_packspu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef WINDOWS
+#define PACKSPU_APIENTRY __stdcall
+#else
+#define PACKSPU_APIENTRY
+#endif
+
+#include "cr_glstate.h"
+#include "cr_netserver.h"
+#include "cr_pack.h"
+#include "cr_spu.h"
+#include "cr_threads.h"
+#include "state/cr_client.h"
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+# include "cr_dump.h"
+#endif
+
+extern uint32_t g_u32VBoxHostCaps;
+
+typedef struct thread_info_t ThreadInfo;
+typedef struct context_info_t ContextInfo;
+typedef struct zvabuffer_info_t ZvaBufferInfo;
+
+struct zvabuffer_info_t
+{
+ /* GL_ARRAY_BUFFER_ARB buffer */
+ GLuint idBuffer;
+ /* buffer length */
+ GLuint cbBuffer;
+ /* number of values stored in the buffer currently */
+ GLuint cValues;
+ /* current buffer value */
+ union
+ {
+ GLfloat f[4];
+ GLuint ui[4];
+ GLubyte ub[4];
+ GLshort s[4];
+ GLushort us[4];
+ } Value;
+};
+
+struct thread_info_t {
+ unsigned long id;
+ CRNetServer netServer;
+ CRPackBuffer buffer;
+ CRPackBuffer normBuffer;
+ CRPackBuffer BeginEndBuffer;
+ GLenum BeginEndMode;
+ int BeginEndState;
+ ContextInfo *currentContext;
+ CRPackContext *packer;
+ int writeback;
+ GLboolean bInjectThread;
+ GLboolean inUse;
+};
+
+struct context_info_t {
+ CRContext *clientState; /* used to store client-side GL state */
+ GLint serverCtx; /* context ID returned by server */
+ GLboolean fAutoFlush;
+ GLboolean fCheckZerroVertAttr;
+ ThreadInfo *currentThread;
+ ZvaBufferInfo zvaBufferInfo;
+ GLubyte glVersion[100]; /* GL_VERSION string */
+ GLubyte pszRealVendor[100];
+ GLubyte pszRealVersion[100];
+ GLubyte pszRealRenderer[100];
+};
+
+typedef struct {
+ int id;
+ int swap;
+
+ /* config options */
+ int emit_GATHER_POST_SWAPBUFFERS;
+ int swapbuffer_sync;
+
+ int ReadPixels;
+
+ char *name;
+ int buffer_size;
+
+ int numThreads; /*number of used threads in the next array, doesn't need to be cont*/
+ ThreadInfo thread[MAX_THREADS];
+ int idxThreadInUse; /*index of any used thread*/
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ bool bIsWDDMCrHgsmi;
+#endif
+
+ SPUDispatchTable self;
+
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ CR_RECORDER Recorder;
+ CR_DBGPRINT_DUMPER Dumper;
+#endif
+
+ int numContexts;
+ ContextInfo context[CR_MAX_CONTEXTS];
+} PackSPU;
+
+extern PackSPU pack_spu;
+
+#define THREAD_OFFSET_MAGIC 2000
+
+#ifdef CHROMIUM_THREADSAFE
+extern CRmutex _PackMutex;
+extern CRtsd _PackTSD;
+#define GET_THREAD_VAL() (crGetTSD(&_PackTSD))
+#define GET_THREAD_IDX(_id) ((_id) - THREAD_OFFSET_MAGIC)
+#define GET_THREAD_VAL_ID(_id) (&(pack_spu.thread[GET_THREAD_IDX(_id)]))
+#else
+#define GET_THREAD_VAL() (&(pack_spu.thread[0]))
+#endif
+#define GET_THREAD(T) ThreadInfo *T = GET_THREAD_VAL()
+#define GET_THREAD_ID(T, _id) ThreadInfo *T = GET_THREAD_VAL_ID(_id)
+
+
+
+#define GET_CONTEXT(C) \
+ GET_THREAD(thread); \
+ ContextInfo *C = thread->currentContext
+
+#ifdef DEBUG_misha
+# define CRPACKSPU_WRITEBACK_ASSERT_ZERO(_writeback) Assert(!(_writeback))
+#else
+# define CRPACKSPU_WRITEBACK_ASSERT_ZERO(_writeback) do {} while (0)
+#endif
+
+#define CRPACKSPU_WRITEBACK_WAIT(_thread, _writeback) do {\
+ if (g_u32VBoxHostCaps & CR_VBOX_CAP_CMDVBVA) { \
+ CRPACKSPU_WRITEBACK_ASSERT_ZERO(_writeback); \
+ (_writeback) = 0; \
+ break; \
+ } \
+ CR_WRITEBACK_WAIT((_thread)->netServer.conn, _writeback); \
+ } while (0)
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM) && defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+# define CRPACKSPU_IS_WDDM_CRHGSMI() (pack_spu.bIsWDDMCrHgsmi)
+#else
+# define CRPACKSPU_IS_WDDM_CRHGSMI() (GL_FALSE)
+#endif
+
+extern void packspuCreateFunctions( void );
+extern void packspuSetVBoxConfiguration( const SPU *child_spu );
+extern void packspuConnectToServer( CRNetServer *server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , struct VBOXUHGSMI *pHgsmi
+#endif
+ );
+extern void packspuFlush( void *arg );
+extern void packspuHuge( CROpcode opcode, void *buf );
+
+extern void packspuInitStrings(void);
+
+extern GLboolean packspuSyncOnFlushes(void);
+
+extern ThreadInfo *packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ struct VBOXUHGSMI *pHgsmi
+#else
+ void
+#endif
+ );
+
+extern ThreadInfo *packspuNewCtxThread( struct VBOXUHGSMI *pHgsmi );
+
+
+
+#define MAGIC_OFFSET 3000
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_pack_packspu_h */
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu.rc b/src/VBox/Additions/common/crOpenGL/pack/packspu.rc
new file mode 100644
index 00000000..b120f236
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu.rc
@@ -0,0 +1,69 @@
+/* $Id: packspu.rc $ */
+/** @file
+ * VBoxOGLpackspu - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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_DRV
+ FILESUBTYPE VFT2_DRV_DISPLAY
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxOGLpackspu\0"
+#ifdef VBOX_WDDM_WOW64
+ VALUE "OriginalFilename", "VBoxOGLpackspu-x86.dll\0"
+#else
+ VALUE "OriginalFilename", "VBoxOGLpackspu.dll\0"
+#endif
+ 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
+
+1 RCDATA
+BEGIN
+// Machine dependent parameters
+ 17, // Height of vertical thumb
+ 17, // Width of horizontal thumb
+ 2, // Icon horiz compression factor
+ 2, // Icon vert compression factor
+ 1, // Cursor horz compression factor
+ 1, // Cursor vert compression factor
+ 0, // Kanji window height
+ 1, // cxBorder (thickness of vertical lines)
+ 1 // cyBorder (thickness of horizontal lines)
+END
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_beginend.py b/src/VBox/Additions/common/crOpenGL/pack/packspu_beginend.py
new file mode 100755
index 00000000..2195cab5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_beginend.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""/* DO NOT EDIT - AUTOMATICALLY GENERATED BY packspu_beginend.py */
+#include "packspu.h"
+#include "assert.h"
+#include "cr_packfunctions.h"
+#include "packspu_proto.h"
+
+void PACKSPU_APIENTRY packspu_Begin( GLenum mode )
+{
+ CRPackBuffer *buf;
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+#else
+ GET_THREAD(thread);
+#endif
+
+ buf = &thread->BeginEndBuffer;
+
+ /* XXX comparing mode >= 0 here is not needed since mode is unsigned */
+ CRASSERT( /*mode >= GL_POINTS && */mode <= GL_POLYGON );
+
+#if CR_ARB_vertex_buffer_object
+ {
+ GLboolean serverArrays = GL_FALSE;
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object)
+ serverArrays = crStateUseServerArrays();
+ if (serverArrays) {
+ CRClientState *clientState = &(ctx->clientState->client);
+ if (clientState->array.locked && !clientState->array.synced)
+ {
+ crPackLockArraysEXT(clientState->array.lockFirst, clientState->array.lockCount);
+ clientState->array.synced = GL_TRUE;
+ }
+ }
+ }
+#endif
+
+ if (pack_spu.swap)
+ {
+ crPackBeginSWAP( mode );
+ }
+ else
+ {
+ crPackBegin( mode );
+ }
+
+ if ( thread->netServer.conn->Barf ) {
+ thread->BeginEndMode = mode;
+ thread->BeginEndState = -1;
+ if ( mode == GL_LINES || mode == GL_TRIANGLES || mode == GL_QUADS || mode == GL_POLYGON )
+ {
+ CRASSERT(!buf->pack);
+
+ crPackReleaseBuffer( thread->packer );
+ buf->pack = crNetAlloc( thread->netServer.conn );
+ crPackInitBuffer( buf, buf->pack, thread->netServer.conn->buffer_size, thread->netServer.conn->mtu );
+ buf->holds_BeginEnd = 1;
+ buf->in_BeginEnd = 1;
+ crPackSetBuffer( thread->packer, buf );
+
+ thread->BeginEndState = 0;
+ }
+ }
+}
+
+void PACKSPU_APIENTRY packspu_End( void )
+{
+ GET_THREAD(thread);
+ CRPackBuffer *buf = &thread->BeginEndBuffer;
+
+ if ( thread->netServer.conn->Barf &&
+ (thread->BeginEndMode == GL_LINES
+ || thread->BeginEndMode == GL_TRIANGLES
+ || thread->BeginEndMode == GL_QUADS
+ || thread->BeginEndMode == GL_POLYGON ) )
+ {
+ CRASSERT(buf->pack);
+
+ crPackReleaseBuffer( thread->packer );
+ crPackSetBuffer( thread->packer, &thread->normBuffer );
+ if ( !crPackCanHoldBuffer( buf ) )
+ packspuFlush( (void *) thread );
+
+ crPackAppendBuffer( buf );
+ crNetFree( thread->netServer.conn, buf->pack );
+ buf->pack = NULL;
+ }
+
+ if (pack_spu.swap)
+ {
+ crPackEndSWAP();
+ }
+ else
+ {
+ crPackEnd();
+ }
+}
+
+static void DoVertex( void )
+{
+ GET_THREAD(thread);
+ CRPackBuffer *buf = &thread->BeginEndBuffer;
+ CRPackBuffer *gbuf = &thread->normBuffer;
+ int num_data;
+ int num_opcode;
+
+ /*crDebug( "really doing Vertex" );*/
+ crPackReleaseBuffer( thread->packer );
+ num_data = buf->data_current - buf->data_start;
+ num_opcode = buf->opcode_start - buf->opcode_current;
+ crPackSetBuffer( thread->packer, gbuf );
+ if ( !crPackCanHoldBuffer( buf ) )
+ /* doesn't hold, first flush gbuf*/
+ packspuFlush( (void *) thread );
+
+ crPackAppendBuffer( buf );
+ crPackReleaseBuffer( thread->packer );
+ crPackSetBuffer( thread->packer, buf );
+ crPackResetPointers(thread->packer);
+}
+
+static void RunState( void )
+{
+ GET_THREAD(thread);
+ if (! thread->netServer.conn->Barf ) return;
+ if (thread->BeginEndState == -1) return;
+ switch(thread->BeginEndMode) {
+ case GL_POLYGON:
+ return;
+ case GL_LINES:
+ thread->BeginEndState = (thread->BeginEndState + 1) % 2;
+ if (thread->BeginEndState)
+ return;
+ break;
+ case GL_TRIANGLES:
+ thread->BeginEndState = (thread->BeginEndState + 1) % 3;
+ if (thread->BeginEndState)
+ return;
+ break;
+ case GL_QUADS:
+ thread->BeginEndState = (thread->BeginEndState + 1) % 4;
+ if (thread->BeginEndState)
+ return;
+ break;
+ }
+ DoVertex();
+}
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in apiutil.AllSpecials( "packspu_vertex" ):
+ params = apiutil.Parameters(func_name)
+ print('void PACKSPU_APIENTRY packspu_%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params) ))
+ print('{')
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tcrPack%sSWAP(%s);' % ( func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\telse')
+ print('\t{')
+ print('\t\tcrPack%s(%s);' % ( func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\tRunState();')
+ print('}')
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c
new file mode 100644
index 00000000..65f64bd7
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c
@@ -0,0 +1,160 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+
+static void packspu_GetHostBufferSubDataARB( GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ crPackGetBufferSubDataARB(target, offset, size, data, &writeback);
+
+ packspuFlush((void *) thread);
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+}
+
+void * PACKSPU_APIENTRY
+packspu_MapBufferARB( GLenum target, GLenum access )
+{
+ GET_CONTEXT(ctx);
+ void *buffer;
+ CRBufferObject *pBufObj;
+
+ CRASSERT(GL_TRUE == ctx->clientState->bufferobject.retainBufferData);
+ buffer = crStateMapBufferARB(target, access);
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (buffer)
+ {
+ pBufObj = crStateGetBoundBufferObject(target, &ctx->clientState->bufferobject);
+ CRASSERT(pBufObj);
+
+ if (pBufObj->bResyncOnRead &&
+ access != GL_WRITE_ONLY_ARB)
+ {
+ /*fetch data from host side*/
+ packspu_GetHostBufferSubDataARB(target, 0, pBufObj->size, buffer);
+ }
+ }
+#endif
+
+ return buffer;
+}
+
+void PACKSPU_APIENTRY packspu_GetBufferSubDataARB( GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data )
+{
+ GET_CONTEXT(ctx);
+
+#ifdef CR_ARB_pixel_buffer_object
+ CRBufferObject *pBufObj;
+
+ pBufObj = crStateGetBoundBufferObject(target, &ctx->clientState->bufferobject);
+
+ if (pBufObj && pBufObj->bResyncOnRead)
+ {
+ packspu_GetHostBufferSubDataARB(target, offset, size, data);
+ return;
+ }
+#endif
+
+ crStateGetBufferSubDataARB(target, offset, size, data);
+}
+
+
+GLboolean PACKSPU_APIENTRY
+packspu_UnmapBufferARB( GLenum target )
+{
+ GET_CONTEXT(ctx);
+
+#if CR_ARB_vertex_buffer_object
+ CRBufferObject *bufObj;
+
+ bufObj = crStateGetBoundBufferObject(target, &ctx->clientState->bufferobject);
+
+ /* send new buffer contents to server */
+ crPackBufferDataARB( target, bufObj->size, bufObj->pointer, bufObj->usage );
+#endif
+
+ CRASSERT(GL_TRUE == ctx->clientState->bufferobject.retainBufferData);
+ crStateUnmapBufferARB( target );
+
+ return GL_TRUE;
+}
+
+
+void PACKSPU_APIENTRY
+packspu_BufferDataARB(GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage)
+{
+ /*crDebug("packspu_BufferDataARB size:%d", size);*/
+ crStateBufferDataARB(target, size, data, usage);
+ crPackBufferDataARB(target, size, data, usage);
+}
+
+void PACKSPU_APIENTRY
+packspu_BufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data)
+{
+ /*crDebug("packspu_BufferSubDataARB size:%d", size);*/
+ crStateBufferSubDataARB(target, offset, size, data);
+ crPackBufferSubDataARB(target, offset, size, data);
+}
+
+
+void PACKSPU_APIENTRY
+packspu_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
+{
+ crStateGetBufferPointervARB( target, pname, params );
+}
+
+
+void PACKSPU_APIENTRY
+packspu_GetBufferParameterivARB( GLenum target, GLenum pname, GLint * params )
+{
+ crStateGetBufferParameterivARB( target, pname, params );
+}
+
+/*
+ * Need to update our local state for vertex arrays.
+ */
+void PACKSPU_APIENTRY
+packspu_BindBufferARB( GLenum target, GLuint buffer )
+{
+ crStateBindBufferARB(target, buffer);
+ crPackBindBufferARB(target, buffer);
+}
+
+void PACKSPU_APIENTRY packspu_GenBuffersARB( GLsizei n, GLuint * buffer )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GenBuffersARB doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGenBuffersARBSWAP( n, buffer, &writeback );
+ }
+ else
+ {
+ crPackGenBuffersARB( n, buffer, &writeback );
+ }
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateRegBuffers(n, buffer);
+}
+
+void PACKSPU_APIENTRY packspu_DeleteBuffersARB( GLsizei n, const GLuint * buffer )
+{
+ crStateDeleteBuffersARB( n, buffer );
+ crPackDeleteBuffersARB(n, buffer);
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_client.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_client.c
new file mode 100644
index 00000000..19c11880
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_client.c
@@ -0,0 +1,910 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_glstate.h"
+#include "packspu_proto.h"
+#include "cr_mem.h"
+
+void PACKSPU_APIENTRY packspu_FogCoordPointerEXT( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackFogCoordPointerEXTSWAP( type, stride, pointer );
+ else
+ crPackFogCoordPointerEXT( type, stride, pointer );
+ }
+#endif
+ crStateFogCoordPointerEXT( type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_ColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackColorPointerSWAP( size, type, stride, pointer );
+ else
+ crPackColorPointer( size, type, stride, pointer );
+ }
+#endif
+ crStateColorPointer( size, type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_SecondaryColorPointerEXT( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackSecondaryColorPointerEXTSWAP( size, type, stride, pointer );
+ else
+ crPackSecondaryColorPointerEXT( size, type, stride, pointer );
+ }
+#endif
+ crStateSecondaryColorPointerEXT( size, type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_VertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ CRASSERT(ctx->clientState->extensions.ARB_vertex_buffer_object);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackVertexPointerSWAP( size, type, stride, pointer );
+ else
+ crPackVertexPointer( size, type, stride, pointer );
+ }
+#endif
+ crStateVertexPointer( size, type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_TexCoordPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackTexCoordPointerSWAP( size, type, stride, pointer );
+ else
+ crPackTexCoordPointer( size, type, stride, pointer );
+ }
+#endif
+ crStateTexCoordPointer( size, type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_NormalPointer( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackNormalPointerSWAP( type, stride, pointer );
+ else
+ crPackNormalPointer( type, stride, pointer );
+ }
+#endif
+ crStateNormalPointer( type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_EdgeFlagPointer( GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackEdgeFlagPointerSWAP( stride, pointer );
+ else
+ crPackEdgeFlagPointer( stride, pointer );
+ }
+#endif
+ crStateEdgeFlagPointer( stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_VertexAttribPointerARB( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackVertexAttribPointerARBSWAP( index, size, type, normalized, stride, pointer );
+ else
+ crPackVertexAttribPointerARB( index, size, type, normalized, stride, pointer );
+ }
+#endif
+ crStateVertexAttribPointerARB( index, size, type, normalized, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_VertexAttribPointerNV( GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackVertexAttribPointerNVSWAP( index, size, type, stride, pointer );
+ else
+ crPackVertexAttribPointerNV( index, size, type, stride, pointer );
+ }
+#endif
+ crStateVertexAttribPointerNV( index, size, type, stride, pointer );
+}
+
+void PACKSPU_APIENTRY packspu_IndexPointer( GLenum type, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackIndexPointerSWAP( type, stride, pointer );
+ else
+ crPackIndexPointer( type, stride, pointer );
+ }
+#endif
+ crStateIndexPointer(type, stride, pointer);
+}
+
+void PACKSPU_APIENTRY packspu_GetPointerv( GLenum pname, GLvoid **params )
+{
+ crStateGetPointerv( pname, params );
+}
+
+void PACKSPU_APIENTRY packspu_InterleavedArrays( GLenum format, GLsizei stride, const GLvoid *pointer )
+{
+#if CR_ARB_vertex_buffer_object
+ GET_CONTEXT(ctx);
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object) {
+ if (pack_spu.swap)
+ crPackInterleavedArraysSWAP( format, stride, pointer );
+ else
+ crPackInterleavedArrays( format, stride, pointer );
+ }
+#endif
+
+ /*crDebug("packspu_InterleavedArrays");*/
+
+ crStateInterleavedArrays( format, stride, pointer );
+}
+
+#ifdef DEBUG_misha
+/* debugging */
+//# define CR_FORCE_ZVA_SERVER_ARRAY
+#endif
+# define CR_FORCE_ZVA_EXPAND
+
+
+static GLboolean packspuZvaCreate(ContextInfo *pCtx, const GLfloat *pValue, GLuint cValues)
+{
+ ZvaBufferInfo *pInfo = &pCtx->zvaBufferInfo;
+ GLuint cbValue = 4 * sizeof (*pValue);
+ GLuint cbValues = cValues * cbValue;
+ GLfloat *pBuffer;
+ uint8_t *pu8Buf;
+ GLuint i;
+
+ /* quickly sort out if we can use the current value */
+ if (pInfo->idBuffer
+ && pInfo->cValues >= cValues
+ && !crMemcmp(pValue, &pInfo->Value, cbValue))
+ return GL_FALSE;
+
+ pBuffer = (GLfloat*)crAlloc(cbValues);
+ if (!pBuffer)
+ {
+ WARN(("crAlloc for pBuffer failed"));
+ return GL_FALSE;
+ }
+
+ pu8Buf = (uint8_t *)pBuffer;
+ for (i = 0; i < cValues; ++i)
+ {
+ crMemcpy(pu8Buf, pValue, cbValue);
+ pu8Buf += cbValue;
+ }
+
+ /* */
+ if (!pInfo->idBuffer)
+ {
+ pack_spu.self.GenBuffersARB(1, &pInfo->idBuffer);
+ Assert(pInfo->idBuffer);
+ }
+
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, pInfo->idBuffer);
+
+ if (pInfo->cbBuffer < cbValues)
+ {
+ pack_spu.self.BufferDataARB(GL_ARRAY_BUFFER_ARB, cbValues, pBuffer, GL_DYNAMIC_DRAW_ARB);
+ pInfo->cbBuffer = cbValues;
+ }
+ else
+ {
+ pack_spu.self.BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, cbValues, pBuffer);
+ }
+
+ pInfo->cValues = cValues;
+ crMemcpy(&pInfo->Value, pValue, cbValue);
+
+ crFree(pBuffer);
+
+ return GL_TRUE;
+}
+
+typedef struct
+{
+ ContextInfo *pCtx;
+ GLuint idBuffer;
+ CRClientPointer cp;
+} CR_ZVA_RESTORE_CTX;
+
+static void packspuZvaEnable(ContextInfo *pCtx, const GLfloat *pValue, GLuint cValues, CR_ZVA_RESTORE_CTX *pRestoreCtx)
+{
+ CRContext *g = pCtx->clientState;
+
+ Assert(0);
+
+#ifdef DEBUG
+ {
+ CRContext *pCurState = crStateGetCurrent();
+
+ Assert(g == pCurState);
+ }
+#endif
+
+ pRestoreCtx->pCtx = pCtx;
+ pRestoreCtx->idBuffer = g->bufferobject.arrayBuffer ? g->bufferobject.arrayBuffer->id : 0;
+ pRestoreCtx->cp = g->client.array.a[0];
+
+ Assert(!pRestoreCtx->cp.enabled);
+
+ /* buffer ref count mechanism does not work actually atm,
+ * still ensure the buffer does not get destroyed if we fix it in the future */
+ if (pRestoreCtx->cp.buffer)
+ pRestoreCtx->cp.buffer->refCount++;
+
+ packspuZvaCreate(pCtx, pValue, cValues);
+
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, pCtx->zvaBufferInfo.idBuffer);
+
+ pack_spu.self.VertexAttribPointerARB(0, 4, GL_FLOAT,
+ GL_FALSE, /*normalized*/
+ 0, /*stride*/
+ NULL /*addr*/);
+
+ pack_spu.self.EnableVertexAttribArrayARB(0);
+}
+
+static void packspuZvaDisable(CR_ZVA_RESTORE_CTX *pRestoreCtx)
+{
+ if (pRestoreCtx->cp.buffer)
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, pRestoreCtx->cp.buffer->id);
+ else
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+
+ pack_spu.self.VertexAttribPointerARB(0, pRestoreCtx->cp.size, pRestoreCtx->cp.type,
+ pRestoreCtx->cp.normalized, /*normalized*/
+ pRestoreCtx->cp.stride, /*stride*/
+ pRestoreCtx->cp.p);
+
+ if (pRestoreCtx->cp.enabled)
+ pack_spu.self.EnableVertexAttribArrayARB(0);
+ else
+ pack_spu.self.DisableVertexAttribArrayARB(0);
+
+ if (pRestoreCtx->cp.buffer)
+ {
+ if (pRestoreCtx->cp.buffer->id != pRestoreCtx->idBuffer)
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, pRestoreCtx->idBuffer);
+
+ /* we have increased the refcount above, decrease it back */
+ pRestoreCtx->cp.buffer->refCount--;
+ }
+ else
+ {
+ if (pRestoreCtx->idBuffer)
+ pack_spu.self.BindBufferARB(GL_ARRAY_BUFFER_ARB, pRestoreCtx->idBuffer);
+ }
+
+#ifdef DEBUG
+ {
+ CRContext *g = pRestoreCtx->pCtx->clientState;
+ CRContext *pCurState = crStateGetCurrent();
+
+ Assert(g == pCurState);
+
+ Assert(pRestoreCtx->cp.p == g->client.array.a[0].p);
+ Assert(pRestoreCtx->cp.size == g->client.array.a[0].size);
+ Assert(pRestoreCtx->cp.type == g->client.array.a[0].type);
+ Assert(pRestoreCtx->cp.stride == g->client.array.a[0].stride);
+ Assert(pRestoreCtx->cp.enabled == g->client.array.a[0].enabled);
+ Assert(pRestoreCtx->cp.normalized == g->client.array.a[0].normalized);
+ Assert(pRestoreCtx->cp.bytesPerIndex == g->client.array.a[0].bytesPerIndex);
+# ifdef CR_ARB_vertex_buffer_object
+ Assert(pRestoreCtx->cp.buffer == g->client.array.a[0].buffer);
+# endif
+# ifdef CR_EXT_compiled_vertex_array
+ Assert(pRestoreCtx->cp.locked == g->client.array.a[0].locked);
+# endif
+ Assert(pRestoreCtx->idBuffer == (g->bufferobject.arrayBuffer ? g->bufferobject.arrayBuffer->id : 0));
+ }
+#endif
+}
+
+void PACKSPU_APIENTRY
+packspu_ArrayElement( GLint index )
+{
+/** @todo cash guest/host pointers calculation and use appropriate path here without crStateUseServerArrays call*/
+#if 1
+ GLboolean serverArrays = GL_FALSE;
+ GLuint cZvaValues = 0;
+ GLfloat aAttrib[4];
+
+#if CR_ARB_vertex_buffer_object
+ {
+ GET_CONTEXT(ctx);
+ /*crDebug("packspu_ArrayElement index:%i", index);*/
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object)
+ {
+ serverArrays = crStateUseServerArrays();
+ if (ctx->fCheckZerroVertAttr)
+ cZvaValues = crStateNeedDummyZeroVertexArray(thread->currentContext->clientState, &thread->packer->current, aAttrib);
+ }
+ }
+#endif
+
+ if (serverArrays
+#ifdef CR_FORCE_ZVA_EXPAND
+ && !cZvaValues
+#endif
+ ) {
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ Assert(cZvaValues < UINT32_MAX/2);
+
+ /* LockArraysEXT can not be executed between glBegin/glEnd pair, it also
+ * leads to vertexpointers being adjusted on the host side between glBegin/glEnd calls which
+ * produces unpredictable results. Locking is done before the glBegin call instead.
+ */
+ CRASSERT(!clientState->array.locked || clientState->array.synced);
+
+ if (cZvaValues)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+
+ /* Send the DrawArrays command over the wire */
+ if (pack_spu.swap)
+ crPackArrayElementSWAP( index );
+ else
+ crPackArrayElement( index );
+
+ if (cZvaValues)
+ packspuZvaDisable(&RestoreCtx);
+ }
+ else {
+ /* evaluate locally */
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+#endif
+
+ if (pack_spu.swap)
+ crPackExpandArrayElementSWAP( index, clientState, cZvaValues ? aAttrib : NULL );
+ else
+ crPackExpandArrayElement( index, clientState, cZvaValues ? aAttrib : NULL );
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaDisable(&RestoreCtx);
+#endif
+ }
+#else
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+ crPackExpandArrayElement(index, clientState, NULL);
+#endif
+}
+
+/*#define CR_USE_LOCKARRAYS*/
+#ifdef CR_USE_LOCKARRAYS
+# error "check Zero Vertex Attrib hack is supported properly!"
+#endif
+
+void PACKSPU_APIENTRY
+packspu_DrawElements( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices )
+{
+ GLboolean serverArrays = GL_FALSE;
+ GLuint cZvaValues = 0;
+ GLfloat aAttrib[4];
+
+#if CR_ARB_vertex_buffer_object
+ GLboolean lockedArrays = GL_FALSE;
+ CRBufferObject *elementsBuffer;
+ {
+ GET_CONTEXT(ctx);
+ elementsBuffer = crStateGetCurrent()->bufferobject.elementsBuffer;
+ /*crDebug("DrawElements count=%d, indices=%p", count, indices);*/
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object)
+ {
+ serverArrays = crStateUseServerArrays();
+ if (ctx->fCheckZerroVertAttr)
+ cZvaValues = crStateNeedDummyZeroVertexArray(thread->currentContext->clientState, &thread->packer->current, aAttrib);
+ }
+ }
+
+# ifdef CR_USE_LOCKARRAYS
+ if (!serverArrays && !ctx->clientState->client.array.locked && (count>3)
+ && (!elementsBuffer || !elementsBuffer->id))
+ {
+ GLuint min, max;
+ GLsizei i;
+
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ {
+ GLubyte *pIdx = (GLubyte *)indices;
+ min = max = pIdx[0];
+ for (i=0; i<count; ++i)
+ {
+ if (pIdx[i]<min) min = pIdx[i];
+ else if (pIdx[i]>max) max = pIdx[i];
+ }
+ break;
+ }
+ case GL_UNSIGNED_SHORT:
+ {
+ GLushort *pIdx = (GLushort *)indices;
+ min = max = pIdx[0];
+ for (i=0; i<count; ++i)
+ {
+ if (pIdx[i]<min) min = pIdx[i];
+ else if (pIdx[i]>max) max = pIdx[i];
+ }
+ break;
+ }
+ case GL_UNSIGNED_INT:
+ {
+ GLuint *pIdx = (GLuint *)indices;
+ min = max = pIdx[0];
+ for (i=0; i<count; ++i)
+ {
+ if (pIdx[i]<min) min = pIdx[i];
+ else if (pIdx[i]>max) max = pIdx[i];
+ }
+ break;
+ }
+ default: crError("Unknown type 0x%x", type);
+ }
+
+ if ((max-min)<(GLuint)(2*count))
+ {
+ crStateLockArraysEXT(min, max-min+1);
+
+ serverArrays = crStateUseServerArrays();
+ if (serverArrays)
+ {
+ lockedArrays = GL_TRUE;
+ }
+ else
+ {
+ crStateUnlockArraysEXT();
+ }
+ }
+ }
+# endif
+#endif
+
+ if (serverArrays
+#ifdef CR_FORCE_ZVA_EXPAND
+ && !cZvaValues
+#endif
+ ) {
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ Assert(cZvaValues < UINT32_MAX/2);
+
+ if (cZvaValues)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+
+ /*Note the comment in packspu_LockArraysEXT*/
+ if (clientState->array.locked && !clientState->array.synced)
+ {
+ crPackLockArraysEXT(clientState->array.lockFirst, clientState->array.lockCount);
+ clientState->array.synced = GL_TRUE;
+ }
+
+ /* Send the DrawArrays command over the wire */
+ if (pack_spu.swap)
+ crPackDrawElementsSWAP( mode, count, type, indices );
+ else
+ crPackDrawElements( mode, count, type, indices );
+
+ if (cZvaValues)
+ packspuZvaDisable(&RestoreCtx);
+ }
+ else {
+ /* evaluate locally */
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+#endif
+
+ if (pack_spu.swap)
+ crPackExpandDrawElementsSWAP( mode, count, type, indices, clientState, cZvaValues ? aAttrib : NULL );
+ else
+ {
+ //packspu_Begin(mode);
+ crPackExpandDrawElements( mode, count, type, indices, clientState, cZvaValues ? aAttrib : NULL );
+ //packspu_End();
+ }
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaDisable(&RestoreCtx);
+#endif
+ }
+
+#if CR_ARB_vertex_buffer_object
+ if (lockedArrays)
+ {
+ packspu_UnlockArraysEXT();
+ }
+#endif
+}
+
+
+void PACKSPU_APIENTRY
+packspu_DrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices )
+{
+ GLboolean serverArrays = GL_FALSE;
+ GLuint cZvaValues = 0;
+ GLfloat aAttrib[4];
+
+#if CR_ARB_vertex_buffer_object
+ {
+ GET_CONTEXT(ctx);
+ /*crDebug("DrawRangeElements count=%d", count);*/
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object)
+ {
+ serverArrays = crStateUseServerArrays();
+ if (ctx->fCheckZerroVertAttr)
+ cZvaValues = crStateNeedDummyZeroVertexArray(thread->currentContext->clientState, &thread->packer->current, aAttrib);
+ }
+ }
+#endif
+
+ if (serverArrays
+#ifdef CR_FORCE_ZVA_EXPAND
+ && !cZvaValues
+#endif
+ ) {
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ Assert(cZvaValues < UINT32_MAX/2);
+
+ if (cZvaValues)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+
+ /*Note the comment in packspu_LockArraysEXT*/
+ if (clientState->array.locked && !clientState->array.synced)
+ {
+ crPackLockArraysEXT(clientState->array.lockFirst, clientState->array.lockCount);
+ clientState->array.synced = GL_TRUE;
+ }
+
+ /* Send the DrawRangeElements command over the wire */
+ if (pack_spu.swap)
+ crPackDrawRangeElementsSWAP( mode, start, end, count, type, indices );
+ else
+ crPackDrawRangeElements( mode, start, end, count, type, indices );
+
+ if (cZvaValues)
+ packspuZvaDisable(&RestoreCtx);
+ }
+ else {
+ /* evaluate locally */
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+#endif
+
+ if (pack_spu.swap)
+ crPackExpandDrawRangeElementsSWAP( mode, start, end, count, type, indices, clientState, cZvaValues ? aAttrib : NULL );
+ else
+ {
+ crPackExpandDrawRangeElements( mode, start, end, count, type, indices, clientState, cZvaValues ? aAttrib : NULL );
+ }
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaDisable(&RestoreCtx);
+#endif
+ }
+}
+
+
+void PACKSPU_APIENTRY
+packspu_DrawArrays( GLenum mode, GLint first, GLsizei count )
+{
+ GLboolean serverArrays = GL_FALSE;
+ GLuint cZvaValues = 0;
+ GLfloat aAttrib[4];
+
+#if CR_ARB_vertex_buffer_object
+ GLboolean lockedArrays = GL_FALSE;
+ {
+ GET_CONTEXT(ctx);
+ /*crDebug("DrawArrays count=%d", count);*/
+ if (ctx->clientState->extensions.ARB_vertex_buffer_object)
+ {
+ serverArrays = crStateUseServerArrays();
+ if (ctx->fCheckZerroVertAttr)
+ cZvaValues = crStateNeedDummyZeroVertexArray(thread->currentContext->clientState, &thread->packer->current, aAttrib);
+ }
+ }
+
+# ifdef CR_USE_LOCKARRAYS
+ if (!serverArrays && !ctx->clientState->client.array.locked && (count>3))
+ {
+ crStateLockArraysEXT(first, count);
+ serverArrays = crStateUseServerArrays();
+ if (serverArrays)
+ {
+ lockedArrays = GL_TRUE;
+ }
+ else
+ {
+ crStateUnlockArraysEXT();
+ }
+ }
+# endif
+#endif
+
+ if (serverArrays
+#ifdef CR_FORCE_ZVA_EXPAND
+ && !cZvaValues
+#endif
+ )
+ {
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ Assert(cZvaValues < UINT32_MAX/2);
+
+ if (cZvaValues)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+
+ /*Note the comment in packspu_LockArraysEXT*/
+ if (clientState->array.locked && !clientState->array.synced)
+ {
+ crPackLockArraysEXT(clientState->array.lockFirst, clientState->array.lockCount);
+ clientState->array.synced = GL_TRUE;
+ }
+
+ /* Send the DrawArrays command over the wire */
+ if (pack_spu.swap)
+ crPackDrawArraysSWAP( mode, first, count );
+ else
+ crPackDrawArrays( mode, first, count );
+
+ if (cZvaValues)
+ packspuZvaDisable(&RestoreCtx);
+ }
+ else
+ {
+ /* evaluate locally */
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ CR_ZVA_RESTORE_CTX RestoreCtx;
+
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaEnable(ctx, aAttrib, cZvaValues, &RestoreCtx);
+#endif
+
+ if (pack_spu.swap)
+ crPackExpandDrawArraysSWAP( mode, first, count, clientState, cZvaValues ? aAttrib : NULL );
+ else
+ crPackExpandDrawArrays( mode, first, count, clientState, cZvaValues ? aAttrib : NULL );
+
+#ifdef CR_FORCE_ZVA_SERVER_ARRAY
+ if (cZvaValues && cZvaValues < UINT32_MAX/2)
+ packspuZvaDisable(&RestoreCtx);
+#endif
+
+ }
+
+#if CR_ARB_vertex_buffer_object
+ if (lockedArrays)
+ {
+ packspu_UnlockArraysEXT();
+ }
+#endif
+}
+
+
+#ifdef CR_EXT_multi_draw_arrays
+void PACKSPU_APIENTRY packspu_MultiDrawArraysEXT( GLenum mode, GLint *first, GLsizei *count, GLsizei primcount )
+{
+ GLint i;
+ for (i = 0; i < primcount; i++) {
+ if (count[i] > 0) {
+ packspu_DrawArrays(mode, first[i], count[i]);
+ }
+ }
+}
+
+void PACKSPU_APIENTRY packspu_MultiDrawElementsEXT( GLenum mode, const GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount )
+{
+ GLint i;
+ for (i = 0; i < primcount; i++) {
+ if (count[i] > 0) {
+ packspu_DrawElements(mode, count[i], type, indices[i]);
+ }
+ }
+}
+#endif
+
+
+void PACKSPU_APIENTRY packspu_EnableClientState( GLenum array )
+{
+ crStateEnableClientState(array);
+ crPackEnableClientState(array);
+}
+
+void PACKSPU_APIENTRY packspu_DisableClientState( GLenum array )
+{
+ crStateDisableClientState(array);
+ crPackDisableClientState(array);
+}
+
+void PACKSPU_APIENTRY packspu_ClientActiveTextureARB( GLenum texUnit )
+{
+ crStateClientActiveTextureARB(texUnit);
+ crPackClientActiveTextureARB(texUnit);
+}
+
+void PACKSPU_APIENTRY packspu_EnableVertexAttribArrayARB(GLuint index)
+{
+ crStateEnableVertexAttribArrayARB(index);
+ crPackEnableVertexAttribArrayARB(index);
+}
+
+
+void PACKSPU_APIENTRY packspu_DisableVertexAttribArrayARB(GLuint index)
+{
+ crStateDisableVertexAttribArrayARB(index);
+ crPackDisableVertexAttribArrayARB(index);
+}
+
+void PACKSPU_APIENTRY packspu_Enable( GLenum cap )
+{
+ if (cap!=GL_LIGHT_MODEL_TWO_SIDE)
+ {
+ crStateEnable(cap);
+
+ if (pack_spu.swap)
+ crPackEnableSWAP(cap);
+ else
+ crPackEnable(cap);
+ }
+ else
+ {
+ static int g_glmts1_warn=0;
+ if (!g_glmts1_warn)
+ {
+ crWarning("glEnable(GL_LIGHT_MODEL_TWO_SIDE) converted to valid glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1)");
+ g_glmts1_warn=1;
+ }
+ crStateLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
+ crPackLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
+ }
+}
+
+
+void PACKSPU_APIENTRY packspu_Disable( GLenum cap )
+{
+ if (cap!=GL_LIGHT_MODEL_TWO_SIDE)
+ {
+ crStateDisable(cap);
+
+ if (pack_spu.swap)
+ crPackDisableSWAP(cap);
+ else
+ crPackDisable(cap);
+ }
+ else
+ {
+ static int g_glmts0_warn=0;
+ if (!g_glmts0_warn)
+ {
+ crWarning("glDisable(GL_LIGHT_MODEL_TWO_SIDE) converted to valid glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)");
+ g_glmts0_warn=1;
+ }
+ crStateLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
+ crPackLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
+ }
+}
+
+GLboolean PACKSPU_APIENTRY packspu_IsEnabled(GLenum cap)
+{
+ GLboolean res = crStateIsEnabled(cap);
+#ifdef DEBUG
+ {
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLboolean return_val = (GLboolean) 0;
+ crPackIsEnabled(cap, &return_val, &writeback);
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ CRASSERT(return_val==res);
+ }
+#endif
+
+ return res;
+}
+
+void PACKSPU_APIENTRY packspu_PushClientAttrib( GLbitfield mask )
+{
+ crStatePushClientAttrib(mask);
+ crPackPushClientAttrib(mask);
+}
+
+void PACKSPU_APIENTRY packspu_PopClientAttrib( void )
+{
+ crStatePopClientAttrib();
+ crPackPopClientAttrib();
+}
+
+void PACKSPU_APIENTRY packspu_LockArraysEXT(GLint first, GLint count)
+{
+ if (first>=0 && count>0)
+ {
+ crStateLockArraysEXT(first, count);
+ /*Note: this is a workaround for quake3 based apps.
+ It's modifying vertex data between glLockArraysEXT and glDrawElements calls,
+ so we'd pass data to host right before the glDrawSomething or glBegin call.
+ */
+ /*crPackLockArraysEXT(first, count);*/
+ }
+ else crDebug("Ignoring packspu_LockArraysEXT: first:%i, count:%i", first, count);
+}
+
+void PACKSPU_APIENTRY packspu_UnlockArraysEXT()
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (clientState->array.locked && clientState->array.synced)
+ {
+ crPackUnlockArraysEXT();
+ }
+
+ crStateUnlockArraysEXT();
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_config.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_config.c
new file mode 100644
index 00000000..70d6f01c
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_config.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "packspu.h"
+#include "cr_string.h"
+#include "cr_error.h"
+#include "cr_spu.h"
+#include "cr_mem.h"
+
+#include <stdio.h>
+
+static void __setDefaults( void )
+{
+ crMemZero(pack_spu.context, CR_MAX_CONTEXTS * sizeof(ContextInfo));
+ pack_spu.numContexts = 0;
+
+ crMemZero(pack_spu.thread, MAX_THREADS * sizeof(ThreadInfo));
+ pack_spu.numThreads = 0;
+}
+
+
+static void set_emit( void *foo, const char *response )
+{
+ RT_NOREF(foo);
+ sscanf( response, "%d", &(pack_spu.emit_GATHER_POST_SWAPBUFFERS) );
+}
+
+static void set_swapbuffer_sync( void *foo, const char *response )
+{
+ RT_NOREF(foo);
+ sscanf( response, "%d", &(pack_spu.swapbuffer_sync) );
+}
+
+
+
+/* No SPU options yet. Well.. not really..
+ */
+SPUOptions packSPUOptions[] = {
+ { "emit_GATHER_POST_SWAPBUFFERS", CR_BOOL, 1, "0", NULL, NULL,
+ "Emit a parameter after SwapBuffers", (SPUOptionCB)set_emit },
+
+ { "swapbuffer_sync", CR_BOOL, 1, "1", NULL, NULL,
+ "Sync on SwapBuffers", (SPUOptionCB) set_swapbuffer_sync },
+
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+};
+
+
+void packspuSetVBoxConfiguration( const SPU *child_spu )
+{
+ RT_NOREF(child_spu);
+ __setDefaults();
+ pack_spu.emit_GATHER_POST_SWAPBUFFERS = 0;
+ pack_spu.swapbuffer_sync = 0;
+ pack_spu.name = crStrdup("vboxhgcm://llp:7000");
+ pack_spu.buffer_size = 5 * 1024 * 1024;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c
new file mode 100644
index 00000000..26e81dbd
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c
@@ -0,0 +1,617 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "packspu.h"
+#include "cr_mem.h"
+#include "cr_packfunctions.h"
+#include "cr_string.h"
+#include "packspu_proto.h"
+
+/*
+ * Allocate a new ThreadInfo structure, setup a connection to the
+ * server, allocate/init a packer context, bind this ThreadInfo to
+ * the calling thread with crSetTSD().
+ * We'll always call this function at least once even if we're not
+ * using threads.
+ */
+ThreadInfo *packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ struct VBOXUHGSMI *pHgsmi
+#else
+ void
+#endif
+)
+{
+ ThreadInfo *thread=NULL;
+ int i;
+
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_PackMutex);
+#else
+ CRASSERT(pack_spu.numThreads == 0);
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ CRASSERT(!CRPACKSPU_IS_WDDM_CRHGSMI() == !pHgsmi);
+#endif
+
+ CRASSERT(pack_spu.numThreads < MAX_THREADS);
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (!pack_spu.thread[i].inUse)
+ {
+ thread = &pack_spu.thread[i];
+ break;
+ }
+ }
+ CRASSERT(thread);
+
+ thread->inUse = GL_TRUE;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ thread->id = crThreadID();
+ else
+ thread->id = THREAD_OFFSET_MAGIC + i;
+ thread->currentContext = NULL;
+ thread->bInjectThread = GL_FALSE;
+
+ /* connect to the server */
+ thread->netServer.name = crStrdup( pack_spu.name );
+ thread->netServer.buffer_size = pack_spu.buffer_size;
+ packspuConnectToServer( &(thread->netServer)
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ CRASSERT(thread->netServer.conn);
+ /* packer setup */
+ CRASSERT(thread->packer == NULL);
+ thread->packer = crPackNewContext( pack_spu.swap );
+ CRASSERT(thread->packer);
+ crPackInitBuffer( &(thread->buffer), crNetAlloc(thread->netServer.conn),
+ thread->netServer.conn->buffer_size, thread->netServer.conn->mtu );
+ thread->buffer.canBarf = thread->netServer.conn->Barf ? GL_TRUE : GL_FALSE;
+ crPackSetBuffer( thread->packer, &thread->buffer );
+ crPackFlushFunc( thread->packer, packspuFlush );
+ crPackFlushArg( thread->packer, (void *) thread );
+ crPackSendHugeFunc( thread->packer, packspuHuge );
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ crPackSetContext( thread->packer );
+ }
+
+
+#ifdef CHROMIUM_THREADSAFE
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ crSetTSD(&_PackTSD, thread);
+ }
+#endif
+
+ pack_spu.numThreads++;
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_PackMutex);
+#endif
+ return thread;
+}
+
+GLint PACKSPU_APIENTRY
+packspu_VBoxConCreate(struct VBOXUHGSMI *pHgsmi)
+{
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ ThreadInfo * thread;
+ CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI());
+ CRASSERT(pHgsmi);
+
+ thread = packspuNewThread(pHgsmi);
+
+ if (thread)
+ {
+ CRASSERT(thread->id);
+ CRASSERT(thread->id - THREAD_OFFSET_MAGIC < RT_ELEMENTS(pack_spu.thread)
+ && GET_THREAD_VAL_ID(thread->id) == thread);
+ return thread->id;
+ }
+ crError("packspuNewThread failed");
+#else
+ RT_NOREF(pHgsmi);
+#endif
+ return 0;
+}
+
+void PACKSPU_APIENTRY
+packspu_VBoxConFlush(GLint con)
+{
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ GET_THREAD_ID(thread, con);
+ CRASSERT(con);
+ CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI());
+ CRASSERT(thread->packer);
+ packspuFlush((void *) thread);
+#else
+ RT_NOREF(con);
+ crError("VBoxConFlush not implemented!");
+#endif
+}
+
+void PACKSPU_APIENTRY
+packspu_VBoxConDestroy(GLint con)
+{
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ GET_THREAD_ID(thread, con);
+ CRASSERT(con);
+ CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI());
+ CRASSERT(pack_spu.numThreads>0);
+ CRASSERT(thread->packer);
+ packspuFlush((void *) thread);
+
+ crLockMutex(&_PackMutex);
+
+ crPackDeleteContext(thread->packer);
+
+ if (thread->buffer.pack)
+ {
+ crNetFree(thread->netServer.conn, thread->buffer.pack);
+ thread->buffer.pack = NULL;
+ }
+
+ crNetFreeConnection(thread->netServer.conn);
+
+ if (thread->netServer.name)
+ crFree(thread->netServer.name);
+
+ pack_spu.numThreads--;
+ /*note can't shift the array here, because other threads have TLS references to array elements*/
+ crMemZero(thread, sizeof(ThreadInfo));
+
+#if 0
+ if (&pack_spu.thread[pack_spu.idxThreadInUse]==thread)
+ {
+ int i;
+ crError("Should not be here since idxThreadInUse should be always 0 for the dummy connection created in packSPUInit!");
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse)
+ {
+ pack_spu.idxThreadInUse=i;
+ break;
+ }
+ }
+ }
+#endif
+ crUnlockMutex(&_PackMutex);
+#else
+ RT_NOREF(con);
+#endif
+}
+
+GLvoid PACKSPU_APIENTRY
+packspu_VBoxConChromiumParameteriCR(GLint con, GLenum param, GLint value)
+{
+ GET_THREAD(thread);
+ CRPackContext * curPacker = crPackGetContext();
+ ThreadInfo *curThread = thread;
+
+ CRASSERT(!curThread == !curPacker);
+ CRASSERT(!curThread || !curPacker || curThread->packer == curPacker);
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_PackMutex);
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ CRASSERT(!con == !CRPACKSPU_IS_WDDM_CRHGSMI());
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (!con)
+ {
+ crError("connection should be specified!");
+ return;
+ }
+ thread = GET_THREAD_VAL_ID(con);
+ }
+ else
+ {
+ CRASSERT(!con);
+ if (!thread)
+ {
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+ }
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->packer);
+
+ crPackSetContext( thread->packer );
+
+ packspu_ChromiumParameteriCR(param, value);
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_PackMutex);
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ /* restore the packer context to the tls */
+ crPackSetContext(curPacker);
+ }
+}
+
+GLvoid PACKSPU_APIENTRY
+packspu_VBoxConChromiumParametervCR(GLint con, GLenum target, GLenum type, GLsizei count, const GLvoid *values)
+{
+ GET_THREAD(thread);
+ CRPackContext * curPacker = crPackGetContext();
+ ThreadInfo *curThread = thread;
+
+ CRASSERT(!curThread == !curPacker);
+ CRASSERT(!curThread || !curPacker || curThread->packer == curPacker);
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_PackMutex);
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ CRASSERT(!con == !CRPACKSPU_IS_WDDM_CRHGSMI());
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (!con)
+ {
+ crError("connection should be specified!");
+ return;
+ }
+ thread = GET_THREAD_VAL_ID(con);
+ }
+ else
+ {
+ CRASSERT(!con);
+ if (!thread)
+ {
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+ }
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->packer);
+
+ crPackSetContext( thread->packer );
+
+ packspu_ChromiumParametervCR(target, type, count, values);
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_PackMutex);
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ /* restore the packer context to the tls */
+ crPackSetContext(curPacker);
+ }
+}
+
+GLint PACKSPU_APIENTRY
+packspu_VBoxCreateContext( GLint con, const char *dpyName, GLint visual, GLint shareCtx )
+{
+ GET_THREAD(thread);
+ CRPackContext * curPacker = crPackGetContext();
+ ThreadInfo *curThread = thread;
+ int writeback = 1;
+ GLint serverCtx = (GLint) -1;
+ int slot;
+
+ CRASSERT(!curThread == !curPacker);
+ CRASSERT(!curThread || !curPacker || curThread->packer == curPacker);
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_PackMutex);
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ CRASSERT(!con == !CRPACKSPU_IS_WDDM_CRHGSMI());
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (!con)
+ {
+ crError("connection should be specified!");
+ return -1;
+ }
+ thread = GET_THREAD_VAL_ID(con);
+ }
+ else
+ {
+ CRASSERT(!con);
+ if (!thread)
+ {
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+ }
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->packer);
+
+ if (shareCtx > 0) {
+ /* translate to server ctx id */
+ shareCtx -= MAGIC_OFFSET;
+ if (shareCtx >= 0 && shareCtx < pack_spu.numContexts) {
+ shareCtx = pack_spu.context[shareCtx].serverCtx;
+ }
+ }
+
+ crPackSetContext( thread->packer );
+
+ /* Pack the command */
+ if (pack_spu.swap)
+ crPackCreateContextSWAP( dpyName, visual, shareCtx, &serverCtx, &writeback );
+ else
+ crPackCreateContext( dpyName, visual, shareCtx, &serverCtx, &writeback );
+
+ /* Flush buffer and get return value */
+ packspuFlush(thread);
+ if (!(thread->netServer.conn->actual_network))
+ {
+ /* HUMUNGOUS HACK TO MATCH SERVER NUMBERING
+ *
+ * The hack exists solely to make file networking work for now. This
+ * is totally gross, but since the server expects the numbers to start
+ * from 5000, we need to write them out this way. This would be
+ * marginally less gross if the numbers (500 and 5000) were maybe
+ * some sort of #define'd constants somewhere so the client and the
+ * server could be aware of how each other were numbering things in
+ * cases like file networking where they actually
+ * care.
+ *
+ * -Humper
+ *
+ */
+ serverCtx = 5000;
+ }
+ else {
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (pack_spu.swap) {
+ serverCtx = (GLint) SWAP32(serverCtx);
+ }
+ if (serverCtx < 0) {
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_PackMutex);
+#endif
+ crWarning("Failure in packspu_CreateContext");
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ /* restore the packer context to the tls */
+ crPackSetContext(curPacker);
+ }
+ return -1; /* failed */
+ }
+ }
+
+ /* find an empty context slot */
+ for (slot = 0; slot < pack_spu.numContexts; slot++) {
+ if (!pack_spu.context[slot].clientState) {
+ /* found empty slot */
+ break;
+ }
+ }
+ if (slot == pack_spu.numContexts) {
+ pack_spu.numContexts++;
+ }
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ thread->currentContext = &pack_spu.context[slot];
+ pack_spu.context[slot].currentThread = thread;
+ }
+
+ /* Fill in the new context info */
+ /* XXX fix-up sharedCtx param here */
+ pack_spu.context[slot].clientState = crStateCreateContext(NULL, visual, NULL);
+ pack_spu.context[slot].clientState->bufferobject.retainBufferData = GL_TRUE;
+ pack_spu.context[slot].serverCtx = serverCtx;
+
+#ifdef CHROMIUM_THREADSAFE
+ crUnlockMutex(&_PackMutex);
+#endif
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ /* restore the packer context to the tls */
+ crPackSetContext(curPacker);
+ }
+
+ return MAGIC_OFFSET + slot;
+}
+
+GLint PACKSPU_APIENTRY
+packspu_CreateContext( const char *dpyName, GLint visual, GLint shareCtx )
+{
+ return packspu_VBoxCreateContext( 0, dpyName, visual, shareCtx );
+}
+
+
+void PACKSPU_APIENTRY packspu_DestroyContext( GLint ctx )
+{
+ GET_THREAD(thread);
+ ThreadInfo *curThread = thread;
+ const int slot = ctx - MAGIC_OFFSET;
+ ContextInfo *context, *curContext;
+ CRPackContext * curPacker = crPackGetContext();
+
+ CRASSERT(slot >= 0);
+ CRASSERT(slot < pack_spu.numContexts);
+
+ context = (slot >= 0 && slot < pack_spu.numContexts) ? &(pack_spu.context[slot]) : NULL;
+ curContext = curThread ? curThread->currentContext : NULL;
+
+ if (context)
+ {
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ thread = context->currentThread;
+ if (thread)
+ {
+ crPackSetContext(thread->packer);
+ CRASSERT(!(thread->packer == curPacker) == !(thread == curThread));
+ }
+ }
+
+ if (pack_spu.swap)
+ crPackDestroyContextSWAP( context->serverCtx );
+ else
+ crPackDestroyContext( context->serverCtx );
+
+ crStateDestroyContext( context->clientState );
+
+ context->clientState = NULL;
+ context->serverCtx = 0;
+ context->currentThread = NULL;
+
+ crMemset (&context->zvaBufferInfo, 0, sizeof (context->zvaBufferInfo));
+ }
+
+ if (curContext == context)
+ {
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ curThread->currentContext = NULL;
+ }
+ else
+ {
+ CRASSERT(thread == curThread);
+ crSetTSD(&_PackTSD, NULL);
+ crPackSetContext(NULL);
+ }
+ crStateMakeCurrent( NULL );
+ }
+ else
+ {
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ crPackSetContext(curPacker);
+ }
+ }
+}
+
+void PACKSPU_APIENTRY packspu_MakeCurrent( GLint window, GLint nativeWindow, GLint ctx )
+{
+ ThreadInfo *thread = NULL;
+ GLint serverCtx;
+ ContextInfo *newCtx = NULL;
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ thread = GET_THREAD_VAL();
+ if (!thread) {
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->packer);
+ }
+
+ if (ctx) {
+ const int slot = ctx - MAGIC_OFFSET;
+
+ CRASSERT(slot >= 0);
+ CRASSERT(slot < pack_spu.numContexts);
+
+ newCtx = &pack_spu.context[slot];
+ CRASSERT(newCtx);
+ CRASSERT(newCtx->clientState); /* verify valid */
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ thread = newCtx->currentThread;
+ CRASSERT(thread);
+ crSetTSD(&_PackTSD, thread);
+ crPackSetContext( thread->packer );
+ }
+ else
+ {
+ CRASSERT(thread);
+ if (newCtx->fAutoFlush)
+ {
+ if (newCtx->currentThread && newCtx->currentThread != thread)
+ {
+ crLockMutex(&_PackMutex);
+ /* do a flush for the previously assigned thread
+ * to ensure all commands issued there are submitted */
+ if (newCtx->currentThread
+ && newCtx->currentThread->inUse
+ && newCtx->currentThread->netServer.conn
+ && newCtx->currentThread->packer && newCtx->currentThread->packer->currentBuffer)
+ {
+ packspuFlush((void *) newCtx->currentThread);
+ }
+ crUnlockMutex(&_PackMutex);
+ }
+ newCtx->currentThread = thread;
+ }
+
+ if (thread->currentContext && newCtx != thread->currentContext && thread->currentContext->fCheckZerroVertAttr)
+ crStateCurrentRecoverNew(thread->currentContext->clientState, &thread->packer->current);
+
+ thread->currentContext = newCtx;
+ crPackSetContext( thread->packer );
+ }
+
+ crStateMakeCurrent( newCtx->clientState );
+ //crStateSetCurrentPointers(newCtx->clientState, &thread->packer->current);
+ serverCtx = pack_spu.context[slot].serverCtx;
+ }
+ else {
+ crStateMakeCurrent( NULL );
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ thread = GET_THREAD_VAL();
+ if (!thread)
+ {
+ CRASSERT(crPackGetContext() == NULL);
+ return;
+ }
+ CRASSERT(thread->currentContext);
+ CRASSERT(thread->packer == crPackGetContext());
+ }
+ else
+ {
+ thread->currentContext = NULL;
+ }
+ newCtx = NULL;
+ serverCtx = 0;
+ }
+
+ if (pack_spu.swap)
+ crPackMakeCurrentSWAP( window, nativeWindow, serverCtx );
+ else
+ crPackMakeCurrent( window, nativeWindow, serverCtx );
+
+ if (serverCtx)
+ {
+ packspuInitStrings();
+ }
+
+ {
+ GET_THREAD(t);
+ (void) t;
+ CRASSERT(t);
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_flush.py b/src/VBox/Additions/common/crOpenGL/pack/packspu_flush.py
new file mode 100755
index 00000000..5742bcb0
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_flush.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - this file generated by packspu_flush.py script */
+
+/* These are otherwise ordinary functions which require that the buffer be
+ * flushed immediately after packing the function.
+ */
+#include "cr_glstate.h"
+#include "cr_packfunctions.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in apiutil.AllSpecials( "packspu_flush" ):
+ params = apiutil.Parameters(func_name)
+ print('void PACKSPU_APIENTRY packspu_%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params)))
+ print('{')
+ print('\tGET_THREAD(thread);')
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tcrPack%sSWAP(%s);' % ( func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\telse')
+ print('\t{')
+ print('\t\tcrPack%s(%s);' % ( func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\tpackspuFlush( (void *) thread );')
+ print('}\n')
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_flush_special b/src/VBox/Additions/common/crOpenGL/pack/packspu_flush_special
new file mode 100644
index 00000000..a4896826
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_flush_special
@@ -0,0 +1,9 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+BarrierCreateCR
+BarrierExecCR
+SemaphoreCreateCR
+SemaphorePCR
+SemaphoreVCR
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c
new file mode 100644
index 00000000..e94c0444
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c
@@ -0,0 +1,143 @@
+/* $Id: packspu_framebuffer.c $ */
+
+/** @file
+ * VBox OpenGL FBO related functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_net.h"
+#include "packspu_proto.h"
+
+void PACKSPU_APIENTRY
+packspu_FramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+{
+ crStateFramebufferTexture1DEXT(target, attachment, textarget, texture, level);
+ crPackFramebufferTexture1DEXT(target, attachment, textarget, texture, level);
+}
+
+void PACKSPU_APIENTRY
+packspu_FramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+{
+ crStateFramebufferTexture2DEXT(target, attachment, textarget, texture, level);
+ crPackFramebufferTexture2DEXT(target, attachment, textarget, texture, level);
+}
+
+void PACKSPU_APIENTRY
+packspu_FramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
+{
+ crStateFramebufferTexture3DEXT(target, attachment, textarget, texture, level, zoffset);
+ crPackFramebufferTexture3DEXT(target, attachment, textarget, texture, level, zoffset);
+}
+
+void PACKSPU_APIENTRY
+packspu_BindFramebufferEXT(GLenum target, GLuint framebuffer)
+{
+ crStateBindFramebufferEXT(target, framebuffer);
+ crPackBindFramebufferEXT(target, framebuffer);
+}
+
+void PACKSPU_APIENTRY
+packspu_DeleteFramebuffersEXT(GLsizei n, const GLuint * framebuffers)
+{
+ crStateDeleteFramebuffersEXT(n, framebuffers);
+ crPackDeleteFramebuffersEXT(n, framebuffers);
+}
+
+void PACKSPU_APIENTRY
+packspu_DeleteRenderbuffersEXT(GLsizei n, const GLuint * renderbuffers)
+{
+ crStateDeleteRenderbuffersEXT(n, renderbuffers);
+ crPackDeleteRenderbuffersEXT(n, renderbuffers);
+}
+
+void PACKSPU_APIENTRY
+packspu_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+{
+ crStateFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer);
+ crPackFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer);
+}
+
+void PACKSPU_APIENTRY
+packspu_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
+{
+ crStateBindRenderbufferEXT(target, renderbuffer);
+ crPackBindRenderbufferEXT(target, renderbuffer);
+}
+
+GLenum PACKSPU_APIENTRY
+packspu_CheckFramebufferStatusEXT(GLenum target)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLenum status = crStateCheckFramebufferStatusEXT(target);
+
+ if (status!=GL_FRAMEBUFFER_UNDEFINED)
+ {
+ return status;
+ }
+
+ crPackCheckFramebufferStatusEXT(target, &status, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateSetFramebufferStatus(target, status);
+ return status;
+}
+
+void PACKSPU_APIENTRY packspu_GenFramebuffersEXT( GLsizei n, GLuint * framebuffers )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GenFramebuffersEXT doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGenFramebuffersEXTSWAP( n, framebuffers, &writeback );
+ }
+ else
+ {
+ crPackGenFramebuffersEXT( n, framebuffers, &writeback );
+ }
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateRegFramebuffers(n, framebuffers);
+}
+
+void PACKSPU_APIENTRY packspu_GenRenderbuffersEXT( GLsizei n, GLuint * renderbuffers )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GenRenderbuffersEXT doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGenRenderbuffersEXTSWAP( n, renderbuffers, &writeback );
+ }
+ else
+ {
+ crPackGenRenderbuffersEXT( n, renderbuffers, &writeback );
+ }
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateRegRenderbuffers(n, renderbuffers);
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py b/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py
new file mode 100755
index 00000000..371abeaa
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py
@@ -0,0 +1,250 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY packspu_get.py SCRIPT */
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_net.h"
+#include "cr_mem.h"
+#include "packspu_proto.h"
+""")
+
+print("""
+static GLboolean crPackIsPixelStoreParm(GLenum pname)
+{
+ if (pname == GL_UNPACK_ALIGNMENT
+ || pname == GL_UNPACK_ROW_LENGTH
+ || pname == GL_UNPACK_SKIP_PIXELS
+ || pname == GL_UNPACK_LSB_FIRST
+ || pname == GL_UNPACK_SWAP_BYTES
+#ifdef CR_OPENGL_VERSION_1_2
+ || pname == GL_UNPACK_IMAGE_HEIGHT
+#endif
+ || pname == GL_UNPACK_SKIP_ROWS
+ || pname == GL_PACK_ALIGNMENT
+ || pname == GL_PACK_ROW_LENGTH
+ || pname == GL_PACK_SKIP_PIXELS
+ || pname == GL_PACK_LSB_FIRST
+ || pname == GL_PACK_SWAP_BYTES
+#ifdef CR_OPENGL_VERSION_1_2
+ || pname == GL_PACK_IMAGE_HEIGHT
+#endif
+ || pname == GL_PACK_SKIP_ROWS)
+ {
+ return GL_TRUE;
+ }
+ return GL_FALSE;
+}
+""")
+
+from get_sizes import *
+
+easy_swaps = {
+ 'GenTextures': '(unsigned int) n',
+ 'GetClipPlane': '4',
+ 'GetPolygonStipple': '0'
+}
+
+simple_funcs = [ 'GetIntegerv', 'GetFloatv', 'GetDoublev', 'GetBooleanv' ]
+simple_swaps = [ 'SWAP32', 'SWAPFLOAT', 'SWAPDOUBLE', '(GLboolean) SWAP32' ]
+
+vertattr_get_funcs = [ 'GetVertexAttribdv' 'GetVertexAttribfv' 'GetVertexAttribiv' ]
+
+hard_funcs = {
+ 'GetLightfv': 'SWAPFLOAT',
+ 'GetLightiv': 'SWAP32',
+ 'GetMaterialfv': 'SWAPFLOAT',
+ 'GetMaterialiv': 'SWAP32',
+ 'GetTexEnvfv': 'SWAPFLOAT',
+ 'GetTexEnviv': 'SWAP32',
+ 'GetTexGendv': 'SWAPDOUBLE',
+ 'GetTexGenfv': 'SWAPFLOAT',
+ 'GetTexGeniv': 'SWAP32',
+ 'GetTexLevelParameterfv': 'SWAPFLOAT',
+ 'GetTexLevelParameteriv': 'SWAP32',
+ 'GetTexParameterfv': 'SWAPFLOAT',
+ 'GetTexParameteriv': 'SWAP32' }
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ params = apiutil.Parameters(func_name)
+ return_type = apiutil.ReturnType(func_name)
+ if apiutil.FindSpecial( "packspu", func_name ):
+ continue
+
+ if "get" in apiutil.Properties(func_name):
+ print('%s PACKSPU_APIENTRY packspu_%s(%s)' % ( return_type, func_name, apiutil.MakeDeclarationString( params ) ))
+ print('{')
+ print('\tGET_THREAD(thread);')
+ print('\tint writeback = 1;')
+ if return_type != 'void':
+ print('\t%s return_val = (%s) 0;' % (return_type, return_type))
+ params.append( ("&return_val", "foo", 0) )
+ if (func_name in easy_swaps and easy_swaps[func_name] != '0') or func_name in simple_funcs or func_name in hard_funcs:
+ print('\tunsigned int i;')
+ print('\tif (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))')
+ print('\t{')
+ print('\t\tcrError( "packspu_%s doesn\'t work when there\'s no actual network involved!\\nTry using the simplequery SPU in your chain!" );' % func_name)
+ print('\t}')
+ if func_name in simple_funcs:
+ print("""
+ if (crPackIsPixelStoreParm(pname)
+ || pname == GL_DRAW_BUFFER
+#ifdef CR_OPENGL_VERSION_1_3
+ || pname == GL_ACTIVE_TEXTURE
+#endif
+#ifdef CR_ARB_multitexture
+ || pname == GL_ACTIVE_TEXTURE_ARB
+#endif
+ || pname == GL_TEXTURE_BINDING_1D
+ || pname == GL_TEXTURE_BINDING_2D
+#ifdef CR_NV_texture_rectangle
+ || pname == GL_TEXTURE_BINDING_RECTANGLE_NV
+#endif
+#ifdef CR_ARB_texture_cube_map
+ || pname == GL_TEXTURE_BINDING_CUBE_MAP_ARB
+#endif
+#ifdef CR_ARB_vertex_program
+ || pname == GL_MAX_VERTEX_ATTRIBS_ARB
+#endif
+#ifdef GL_EXT_framebuffer_object
+ || pname == GL_FRAMEBUFFER_BINDING_EXT
+ || pname == GL_READ_FRAMEBUFFER_BINDING_EXT
+ || pname == GL_DRAW_FRAMEBUFFER_BINDING_EXT
+#endif
+ || pname == GL_ARRAY_BUFFER_BINDING
+ || pname == GL_ELEMENT_ARRAY_BUFFER_BINDING
+ || pname == GL_PIXEL_PACK_BUFFER_BINDING
+ || pname == GL_PIXEL_UNPACK_BUFFER_BINDING
+ )
+ {
+#ifdef DEBUG
+ if (!crPackIsPixelStoreParm(pname)
+#ifdef CR_ARB_vertex_program
+ && (pname!=GL_MAX_VERTEX_ATTRIBS_ARB)
+#endif
+ )
+ {
+ %s localparams;
+ localparams = (%s) crAlloc(__numValues(pname) * sizeof(*localparams));
+ crState%s(pname, localparams);
+ crPack%s(%s, &writeback);
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ for (i=0; i<__numValues(pname); ++i)
+ {
+ if (localparams[i] != params[i])
+ {
+ crWarning("Incorrect local state in %s for %%x param %%i", pname, i);
+ crWarning("Expected %%i but got %%i", (int)localparams[i], (int)params[i]);
+ }
+ }
+ crFree(localparams);
+ return;
+ }
+ else
+#endif
+ {
+ crState%s(pname, params);
+ return;
+ }
+
+ }
+ """ % (params[-1][1], params[-1][1], func_name, func_name, apiutil.MakeCallString(params), func_name, func_name))
+
+ if func_name in vertattr_get_funcs:
+ print("""
+ if (pname != GL_CURRENT_VERTEX_ATTRIB_ARB)
+ {
+#ifdef DEBUG
+ %s localparams;
+ localparams = (%s) crAlloc(__numValues(pname) * sizeof(*localparams));
+ crState%s(index, pname, localparams);
+ crPack%s(index, %s, &writeback);
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ for (i=0; i<crStateHlpComponentsCount(pname); ++i)
+ {
+ if (localparams[i] != params[i])
+ {
+ crWarning("Incorrect local state in %s for %%x param %%i", pname, i);
+ crWarning("Expected %%i but got %%i", (int)localparams[i], (int)params[i]);
+ }
+ }
+ crFree(localparams);
+#else
+ crState%s(pname, params);
+#endif
+ return;
+ }
+ """ % (params[-1][1], params[-1][1], func_name, func_name, apiutil.MakeCallString(params), func_name, func_name))
+
+ params.append( ("&writeback", "foo", 0) )
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tcrPack%sSWAP(%s);' % (func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\telse')
+ print('\t{')
+ print('\t\tcrPack%s(%s);' % (func_name, apiutil.MakeCallString( params ) ))
+ print('\t}')
+ print('\tpackspuFlush( (void *) thread );')
+ print('\tCRPACKSPU_WRITEBACK_WAIT(thread, writeback);')
+
+
+
+ lastParamName = params[-2][0]
+ if return_type != 'void':
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\treturn_val = (%s) SWAP32(return_val);' % return_type)
+ print('\t}')
+ print('\treturn return_val;')
+ if func_name in easy_swaps and easy_swaps[func_name] != '0':
+ limit = easy_swaps[func_name]
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tfor (i = 0; i < %s; i++)' % limit)
+ print('\t\t{')
+ if params[-2][1].find( "double" ) > -1:
+ print('\t\t\t%s[i] = SWAPDOUBLE(%s[i]);' % (lastParamName, lastParamName))
+ else:
+ print('\t\t\t%s[i] = SWAP32(%s[i]);' % (lastParamName, lastParamName))
+ print('\t\t}')
+ print('\t}')
+ for index in range(len(simple_funcs)):
+ if simple_funcs[index] == func_name:
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tfor (i = 0; i < __numValues(pname); i++)')
+ print('\t\t{')
+ if simple_swaps[index] == 'SWAPDOUBLE':
+ print('\t\t\t%s[i] = %s(%s[i]);' % (lastParamName, simple_swaps[index], lastParamName))
+ else:
+ print('\t\t\t((GLuint *) %s)[i] = %s(%s[i]);' % (lastParamName, simple_swaps[index], lastParamName))
+ print('\t\t}')
+ print('\t}')
+ if func_name in hard_funcs:
+ print('\tif (pack_spu.swap)')
+ print('\t{')
+ print('\t\tfor (i = 0; i < crStateHlpComponentsCount(pname); i++)')
+ print('\t\t{')
+ if hard_funcs[func_name] == 'SWAPDOUBLE':
+ print('\t\t\t%s[i] = %s(%s[i]);' % (lastParamName, hard_funcs[func_name], lastParamName))
+ else:
+ print('\t\t\t((GLuint *) %s)[i] = %s(%s[i]);' % (lastParamName, hard_funcs[func_name], lastParamName))
+ print('\t\t}')
+ print('\t}')
+ print('}\n')
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c
new file mode 100644
index 00000000..97150222
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c
@@ -0,0 +1,214 @@
+/* $Id: packspu_getshaders.c $ */
+
+/** @file
+ * VBox OpenGL GLSL related functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_net.h"
+#include "packspu_proto.h"
+#include "cr_mem.h"
+#include <iprt/assert.h>
+
+/** @todo combine with the one from server_getshaders.c*/
+typedef struct _crGetActive_t
+{
+ GLsizei length;
+ GLint size;
+ GLenum type;
+} crGetActive_t;
+
+void PACKSPU_APIENTRY packspu_GetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, char * name)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ crGetActive_t *pLocal;
+
+ if (!size || !type || !name) return;
+
+ pLocal = (crGetActive_t*) crAlloc(bufSize+sizeof(crGetActive_t));
+ if (!pLocal) return;
+
+ crPackGetActiveAttrib(program, index, bufSize, (GLsizei*)pLocal, NULL, NULL, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (length) *length = pLocal->length;
+ *size = pLocal->size;
+ *type = pLocal->type;
+ crMemcpy(name, (char*)&pLocal[1], pLocal->length+1);
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, char * name)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ crGetActive_t *pLocal;
+
+ if (!size || !type || !name) return;
+
+ pLocal = (crGetActive_t*) crAlloc(bufSize+sizeof(crGetActive_t));
+ if (!pLocal) return;
+
+ crPackGetActiveUniform(program, index, bufSize, (GLsizei*)pLocal, NULL, NULL, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (length) *length = pLocal->length;
+ *size = pLocal->size;
+ *type = pLocal->type;
+ crMemcpy(name, &pLocal[1], pLocal->length+1);
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!shaders) return;
+
+ pLocal = (GLsizei*) crAlloc(maxCount*sizeof(GLuint)+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetAttachedShaders(program, maxCount, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (count) *count=*pLocal;
+ crMemcpy(shaders, &pLocal[1], *pLocal*sizeof(GLuint));
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetAttachedObjectsARB(VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * count, VBoxGLhandleARB * obj)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!obj) return;
+
+ pLocal = (GLsizei*) crAlloc(maxCount*sizeof(VBoxGLhandleARB)+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetAttachedObjectsARB(containerObj, maxCount, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (count) *count=*pLocal;
+ crMemcpy(obj, &pLocal[1], *pLocal*sizeof(VBoxGLhandleARB));
+ crFree(pLocal);
+}
+
+AssertCompile(sizeof(GLsizei) == 4);
+
+void PACKSPU_APIENTRY packspu_GetInfoLogARB(VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!infoLog) return;
+
+ pLocal = (GLsizei*) crAlloc(maxLength+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetInfoLogARB(obj, maxLength, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ CRASSERT((pLocal[0]) <= maxLength);
+
+ if (length) *length=*pLocal;
+ crMemcpy(infoLog, &pLocal[1], (maxLength >= (pLocal[0])) ? pLocal[0] : maxLength);
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei * length, char * infoLog)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!infoLog) return;
+
+ pLocal = (GLsizei*) crAlloc(bufSize+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetProgramInfoLog(program, bufSize, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (length) *length=*pLocal;
+ crMemcpy(infoLog, &pLocal[1], (bufSize >= pLocal[0]) ? pLocal[0] : bufSize);
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei * length, char * infoLog)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!infoLog) return;
+
+ pLocal = (GLsizei*) crAlloc(bufSize+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetShaderInfoLog(shader, bufSize, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (length) *length=*pLocal;
+ crMemcpy(infoLog, &pLocal[1], (bufSize >= pLocal[0]) ? pLocal[0] : bufSize);
+ crFree(pLocal);
+}
+
+void PACKSPU_APIENTRY packspu_GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei * length, char * source)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei *pLocal;
+
+ if (!source) return;
+
+ pLocal = (GLsizei*) crAlloc(bufSize+sizeof(GLsizei));
+ if (!pLocal) return;
+
+ crPackGetShaderSource(shader, bufSize, pLocal, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (length) *length=*pLocal;
+ crMemcpy(source, &pLocal[1], (bufSize >= pLocal[0]) ? pLocal[0] : bufSize);
+
+ if (bufSize > pLocal[0])
+ {
+ source[pLocal[0]] = 0;
+ }
+
+ crFree(pLocal);
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_getstring.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_getstring.c
new file mode 100644
index 00000000..c5890bfe
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_getstring.c
@@ -0,0 +1,211 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "state/cr_statefuncs.h"
+#include "cr_string.h"
+#include "packspu_proto.h"
+#include "cr_mem.h"
+#include <locale.h>
+
+static GLubyte gpszExtensions[10000];
+#ifdef CR_OPENGL_VERSION_2_0
+static GLubyte gpszShadingVersion[255]="";
+#endif
+
+static void GetString(GLenum name, GLubyte *pszStr)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ crPackGetStringSWAP(name, pszStr, &writeback);
+ else
+ crPackGetString(name, pszStr, &writeback);
+ packspuFlush( (void *) thread );
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+}
+
+static GLfloat
+GetVersionString(void)
+{
+ static GLboolean fInitialized = GL_FALSE;
+ static GLfloat version = 0.;
+
+ if (!fInitialized)
+ {
+ GLubyte return_value[100];
+
+ GetString(GL_VERSION, return_value);
+ CRASSERT(crStrlen((char *)return_value) < 100);
+
+ version = crStrToFloat((char *) return_value);
+ version = crStateComputeVersion(version);
+
+ fInitialized = GL_TRUE;
+ }
+
+ return version;
+}
+
+static const GLubyte *
+GetExtensions(void)
+{
+ static GLboolean fInitialized = GL_FALSE;
+ if (!fInitialized)
+ {
+ GLubyte return_value[10*1000];
+ const GLubyte *extensions, *ext;
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ {
+ crPackGetStringSWAP( GL_EXTENSIONS, return_value, &writeback );
+ }
+ else
+ {
+ crPackGetString( GL_EXTENSIONS, return_value, &writeback );
+ }
+ packspuFlush( (void *) thread );
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ CRASSERT(crStrlen((char *)return_value) < 10*1000);
+
+ /* OK, we got the result from the server. Now we have to
+ * intersect is with the set of extensions that Chromium understands
+ * and tack on the Chromium-specific extensions.
+ */
+ extensions = return_value;
+ ext = crStateMergeExtensions(1, &extensions);
+
+#ifdef Linux
+ /** @todo
+ *That's a hack to allow running Unity, it uses libnux which is calling extension functions
+ *without checking if it's being supported/exported.
+ *glActiveStencilFaceEXT seems to be actually supported but the extension string isn't exported (for ex. on ATI HD4870),
+ *which leads to libglew setting function pointer to NULL and crashing Unity.
+ */
+ sprintf((char*)gpszExtensions, "%s GL_EXT_stencil_two_side", ext);
+#else
+ sprintf((char*)gpszExtensions, "%s", ext);
+#endif
+ fInitialized = GL_TRUE;
+ }
+
+ return gpszExtensions;
+}
+
+#ifdef WINDOWS
+static bool packspuRunningUnderWine(void)
+{
+ return NULL != GetModuleHandle("wined3d.dll") || NULL != GetModuleHandle("wined3dwddm.dll") || NULL != GetModuleHandle("wined3dwddm-x86.dll");
+}
+#endif
+
+const GLubyte * PACKSPU_APIENTRY packspu_GetString( GLenum name )
+{
+ GET_CONTEXT(ctx);
+
+ switch(name)
+ {
+ case GL_EXTENSIONS:
+ return GetExtensions();
+ case GL_VERSION:
+#if 0 && defined(WINDOWS)
+ if (packspuRunningUnderWine())
+ {
+ GetString(GL_REAL_VERSION, ctx->pszRealVersion);
+ return ctx->pszRealVersion;
+ }
+ else
+#endif
+ {
+ char *oldlocale;
+ float version;
+
+ oldlocale = setlocale(LC_NUMERIC, NULL);
+ oldlocale = crStrdup(oldlocale);
+ setlocale(LC_NUMERIC, "C");
+
+ version = GetVersionString();
+ sprintf((char*)ctx->glVersion, "%.1f Chromium %s", version, CR_VERSION_STRING);
+
+ if (oldlocale)
+ {
+ setlocale(LC_NUMERIC, oldlocale);
+ crFree(oldlocale);
+ }
+
+ return ctx->glVersion;
+ }
+ case GL_VENDOR:
+#ifdef WINDOWS
+ if (packspuRunningUnderWine())
+ {
+ GetString(GL_REAL_VENDOR, ctx->pszRealVendor);
+ return ctx->pszRealVendor;
+ }
+ else
+#endif
+ {
+ return crStateGetString(name);
+ }
+ case GL_RENDERER:
+#ifdef WINDOWS
+ if (packspuRunningUnderWine())
+ {
+ GetString(GL_REAL_RENDERER, ctx->pszRealRenderer);
+ return ctx->pszRealRenderer;
+ }
+ else
+#endif
+ {
+ return crStateGetString(name);
+ }
+
+#ifdef CR_OPENGL_VERSION_2_0
+ case GL_SHADING_LANGUAGE_VERSION:
+ {
+ static GLboolean fInitialized = GL_FALSE;
+ if (!fInitialized)
+ {
+ GetString(GL_SHADING_LANGUAGE_VERSION, gpszShadingVersion);
+ fInitialized = GL_TRUE;
+ }
+ return gpszShadingVersion;
+ }
+#endif
+#ifdef GL_CR_real_vendor_strings
+ case GL_REAL_VENDOR:
+ GetString(GL_REAL_VENDOR, ctx->pszRealVendor);
+ return ctx->pszRealVendor;
+ case GL_REAL_VERSION:
+ GetString(GL_REAL_VERSION, ctx->pszRealVersion);
+ return ctx->pszRealVersion;
+ case GL_REAL_RENDERER:
+ GetString(GL_REAL_RENDERER, ctx->pszRealRenderer);
+ return ctx->pszRealRenderer;
+#endif
+ default:
+ return crStateGetString(name);
+ }
+}
+
+void packspuInitStrings()
+{
+ static GLboolean fInitialized = GL_FALSE;
+
+ if (!fInitialized)
+ {
+ packspu_GetString(GL_EXTENSIONS);
+ packspu_GetString(GL_VERSION);
+ fInitialized = GL_TRUE;
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c
new file mode 100644
index 00000000..1116b7d5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c
@@ -0,0 +1,275 @@
+/* $Id: packspu_glsl.c $ */
+
+/** @file
+ * VBox OpenGL GLSL related functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_net.h"
+#include "packspu_proto.h"
+#include "cr_mem.h"
+
+
+GLuint PACKSPU_APIENTRY packspu_CreateProgram(void)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLuint return_val = (GLuint) 0;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError("packspu_CreateProgram doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!");
+ }
+ if (pack_spu.swap)
+ {
+ crPackCreateProgramSWAP(&return_val, &writeback);
+ }
+ else
+ {
+ crPackCreateProgram(&return_val, &writeback);
+ }
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ if (pack_spu.swap)
+ {
+ return_val = (GLuint) SWAP32(return_val);
+ }
+
+ crStateCreateProgram(return_val);
+
+ return return_val;
+}
+
+static GLint packspu_GetUniformLocationUncached(GLuint program, const char * name)
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLint return_val = (GLint) 0;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError("packspu_GetUniformLocation doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!");
+ }
+ if (pack_spu.swap)
+ {
+ crPackGetUniformLocationSWAP(program, name, &return_val, &writeback);
+ }
+ else
+ {
+ crPackGetUniformLocation(program, name, &return_val, &writeback);
+ }
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ if (pack_spu.swap)
+ {
+ return_val = (GLint) SWAP32(return_val);
+ }
+ return return_val;
+}
+
+GLint PACKSPU_APIENTRY packspu_GetUniformLocation(GLuint program, const char * name)
+{
+ if (!crStateIsProgramUniformsCached(program))
+ {
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei maxcbData;
+ GLsizei *pData;
+ GLint mu;
+
+ packspu_GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &mu);
+ maxcbData = 16*mu*sizeof(char);
+
+ pData = (GLsizei *) crAlloc(maxcbData+sizeof(GLsizei));
+ if (!pData)
+ {
+ crWarning("packspu_GetUniformLocation: not enough memory, fallback to single query");
+ return packspu_GetUniformLocationUncached(program, name);
+ }
+
+ crPackGetUniformsLocations(program, maxcbData, pData, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateGLSLProgramCacheUniforms(program, pData[0], &pData[1]);
+
+ CRASSERT(crStateIsProgramUniformsCached(program));
+
+ crFree(pData);
+ }
+
+ /*crDebug("packspu_GetUniformLocation(%d, %s)=%i", program, name, crStateGetUniformLocation(program, name));*/
+ return crStateGetUniformLocation(program, name);
+}
+
+static GLint PACKSPU_APIENTRY packspu_GetAttribLocationUnchached( GLuint program, const char * name )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLint return_val = (GLint) 0;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GetAttribLocation doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGetAttribLocationSWAP( program, name, &return_val, &writeback );
+ }
+ else
+ {
+ crPackGetAttribLocation( program, name, &return_val, &writeback );
+ }
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ if (pack_spu.swap)
+ {
+ return_val = (GLint) SWAP32(return_val);
+ }
+ return return_val;
+}
+
+GLint PACKSPU_APIENTRY packspu_GetAttribLocation(GLuint program, const char * name)
+{
+ if (!(CR_VBOX_CAP_GETATTRIBSLOCATIONS & g_u32VBoxHostCaps))
+ return packspu_GetAttribLocationUnchached(program, name);
+
+ if (!crStateIsProgramAttribsCached(program))
+ {
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLsizei maxcbData;
+ GLsizei *pData;
+ GLint mu;
+
+ packspu_GetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mu);
+ maxcbData = 4*32*mu*sizeof(char);
+
+ pData = (GLsizei *) crAlloc(maxcbData+sizeof(GLsizei));
+ if (!pData)
+ {
+ crWarning("packspu_GetAttribLocation: not enough memory, fallback to single query");
+ return packspu_GetAttribLocationUnchached(program, name);
+ }
+
+ crPackGetAttribsLocations(program, maxcbData, pData, NULL, &writeback);
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ crStateGLSLProgramCacheAttribs(program, pData[0], &pData[1]);
+
+ CRASSERT(crStateIsProgramAttribsCached(program));
+
+ crFree(pData);
+ }
+
+ /*crDebug("packspu_GetAttribLocation(%d, %s)=%i", program, name, crStateGetAttribLocation(program, name));*/
+ return crStateGetAttribLocation(program, name);
+}
+
+void PACKSPU_APIENTRY packspu_GetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData)
+{
+ (void) program;
+ (void) maxcbData;
+ (void) cbData;
+ (void) pData;
+ WARN(("packspu_GetUniformsLocations shouldn't be called directly"));
+}
+
+void PACKSPU_APIENTRY packspu_GetAttribsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData)
+{
+ (void) program;
+ (void) maxcbData;
+ (void) cbData;
+ (void) pData;
+ WARN(("packspu_GetAttribsLocations shouldn't be called directly"));
+}
+
+void PACKSPU_APIENTRY packspu_DeleteProgram(GLuint program)
+{
+ crStateDeleteProgram(program);
+ crPackDeleteProgram(program);
+}
+
+void PACK_APIENTRY packspu_DeleteObjectARB(VBoxGLhandleARB obj)
+{
+ GLuint hwid = crStateGetProgramHWID(obj);
+
+ CRASSERT(obj);
+
+ /* we do not track shader creation inside guest since it is not needed currently.
+ * this is why we only care about programs here */
+ if (hwid)
+ {
+ crStateDeleteProgram(obj);
+ }
+
+ crPackDeleteObjectARB(obj);
+}
+
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+static void packspu_RecCheckInitRec()
+{
+ if (pack_spu.Recorder.pDumper)
+ return;
+
+ crDmpDbgPrintInit(&pack_spu.Dumper);
+
+ crRecInit(&pack_spu.Recorder, NULL /*pBlitter: we do not support blitter operations here*/, &pack_spu.self, &pack_spu.Dumper.Base);
+}
+#endif
+
+void PACKSPU_APIENTRY packspu_LinkProgram(GLuint program)
+{
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ GLint linkStatus = 0;
+#endif
+
+ crStateLinkProgram(program);
+ crPackLinkProgram(program);
+
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ pack_spu.self.GetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linkStatus);
+ Assert(linkStatus);
+ if (!linkStatus)
+ {
+ CRContext *ctx = crStateGetCurrent();
+ packspu_RecCheckInitRec();
+ crRecDumpProgram(&pack_spu.Recorder, ctx, program, program);
+ }
+#endif
+}
+
+void PACKSPU_APIENTRY packspu_CompileShader(GLuint shader)
+{
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ GLint compileStatus = 0;
+#endif
+
+// crStateCompileShader(shader);
+ crPackCompileShader(shader);
+
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ pack_spu.self.GetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus);
+ Assert(compileStatus);
+ if (!compileStatus)
+ {
+ CRContext *ctx = crStateGetCurrent();
+ packspu_RecCheckInitRec();
+ crRecDumpShader(&pack_spu.Recorder, ctx, shader, shader);
+ }
+#endif
+}
+
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c
new file mode 100644
index 00000000..d5219950
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c
@@ -0,0 +1,152 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_mem.h"
+#include "cr_spu.h"
+#include "cr_glstate.h"
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include <stdio.h>
+
+extern SPUNamedFunctionTable _cr_pack_table[];
+
+SPUFunctions pack_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_pack_table /* THE ACTUAL FUNCTIONS */
+};
+
+PackSPU pack_spu;
+
+#ifdef CHROMIUM_THREADSAFE
+CRtsd _PackTSD;
+CRmutex _PackMutex;
+#endif
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+# include <VBoxCrHgsmi.h>
+# include <VBoxUhgsmi.h>
+#endif
+
+#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_WDDM)
+static bool isVBoxWDDMCrHgsmi(void)
+{
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ PVBOXUHGSMI pHgsmi = VBoxCrHgsmiCreate();
+ if (pHgsmi)
+ {
+ VBoxCrHgsmiDestroy(pHgsmi);
+ return true;
+ }
+#endif
+ return false;
+}
+#endif /* RT_OS_WINDOWS && VBOX_WITH_WDDM */
+
+static SPUFunctions *
+packSPUInit( int id, SPU *child, SPU *self,
+ unsigned int context_id,
+ unsigned int num_contexts )
+{
+ ThreadInfo *thread;
+
+ (void) context_id;
+ (void) num_contexts;
+ (void) child;
+ (void) self;
+
+#if defined(CHROMIUM_THREADSAFE) && !defined(WINDOWS)
+ crInitMutex(&_PackMutex);
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+ crInitTSD(&_PackerTSD);
+ crInitTSD(&_PackTSD);
+#endif
+
+ pack_spu.id = id;
+
+ packspuSetVBoxConfiguration( child );
+
+#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
+ pack_spu.bIsWDDMCrHgsmi = isVBoxWDDMCrHgsmi();
+#endif
+
+#ifdef VBOX_WITH_CRPACKSPU_DUMPER
+ memset(&pack_spu.Dumper, 0, sizeof (pack_spu.Dumper));
+#endif
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ /* This connects to the server, sets up the packer, etc. */
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+
+ if (!thread) {
+ return NULL;
+ }
+ CRASSERT( thread == &(pack_spu.thread[0]) );
+ pack_spu.idxThreadInUse = 0;
+ }
+
+ packspuCreateFunctions();
+ crStateInit();
+
+ return &pack_functions;
+}
+
+static void
+packSPUSelfDispatch(SPUDispatchTable *self)
+{
+ crSPUInitDispatchTable( &(pack_spu.self) );
+ crSPUCopyDispatchTable( &(pack_spu.self), self );
+}
+
+static int
+packSPUCleanup(void)
+{
+ int i;
+#ifdef CHROMIUM_THREADSAFE
+ crLockMutex(&_PackMutex);
+#endif
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse && pack_spu.thread[i].packer)
+ {
+ crPackDeleteContext(pack_spu.thread[i].packer);
+ }
+ }
+
+#ifdef CHROMIUM_THREADSAFE
+ crFreeTSD(&_PackerTSD);
+ crFreeTSD(&_PackTSD);
+ crUnlockMutex(&_PackMutex);
+# ifndef WINDOWS
+ crFreeMutex(&_PackMutex);
+# endif
+#endif /* CHROMIUM_THREADSAFE */
+ return 1;
+}
+
+extern SPUOptions packSPUOptions[];
+
+int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
+ SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
+ SPUOptionsPtr *options, int *flags )
+{
+ *name = "pack";
+ *super = NULL;
+ *init = packSPUInit;
+ *self = packSPUSelfDispatch;
+ *cleanup = packSPUCleanup;
+ *options = packSPUOptions;
+ *flags = (SPU_HAS_PACKER|SPU_IS_TERMINAL|SPU_MAX_SERVERS_ONE);
+
+ return 1;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c
new file mode 100644
index 00000000..0b65be90
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c
@@ -0,0 +1,856 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_packfunctions.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+#include "cr_mem.h"
+
+void PACKSPU_APIENTRY packspu_ChromiumParametervCR(GLenum target, GLenum type, GLsizei count, const GLvoid *values)
+{
+
+ CRMessage msg;
+ int len;
+ GLint ai32ServerValues[2];
+ GLboolean fFlush = GL_FALSE;
+ GET_THREAD(thread);
+
+
+ switch(target)
+ {
+ case GL_GATHER_PACK_CR:
+ /* flush the current pack buffer */
+ packspuFlush( (void *) thread );
+
+ /* the connection is thread->server.conn */
+ msg.header.type = CR_MESSAGE_GATHER;
+ msg.gather.offset = 69;
+ len = sizeof(CRMessageGather);
+ crNetSend(thread->netServer.conn, NULL, &msg, len);
+ return;
+
+ case GL_SHARE_LISTS_CR:
+ {
+ ContextInfo *pCtx[2];
+ GLint *ai32Values;
+ int i;
+ if (count != 2)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid cound %d", count));
+ return;
+ }
+
+ if (type != GL_UNSIGNED_INT && type != GL_INT)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid type %d", type));
+ return;
+ }
+
+ ai32Values = (GLint*)values;
+
+ for (i = 0; i < 2; ++i)
+ {
+ const int slot = ai32Values[i] - MAGIC_OFFSET;
+
+ if (slot < 0 || slot >= pack_spu.numContexts)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid value[%d] %d", i, ai32Values[i]));
+ return;
+ }
+
+ pCtx[i] = &pack_spu.context[slot];
+ if (!pCtx[i]->clientState)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid pCtx1 for value[%d] %d", i, ai32Values[i]));
+ return;
+ }
+
+ ai32ServerValues[i] = pCtx[i]->serverCtx;
+ }
+
+ crStateShareLists(pCtx[0]->clientState, pCtx[1]->clientState);
+
+ values = ai32ServerValues;
+
+ fFlush = GL_TRUE;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (pack_spu.swap)
+ crPackChromiumParametervCRSWAP(target, type, count, values);
+ else
+ crPackChromiumParametervCR(target, type, count, values);
+
+ if (fFlush)
+ packspuFlush( (void *) thread );
+}
+
+GLboolean packspuSyncOnFlushes(void)
+{
+#if 1 /*Seems to still cause issues, always sync for now*/
+ return 1;
+#else
+ GLint buffer;
+
+ crStateGetIntegerv(GL_DRAW_BUFFER, &buffer);
+ /*Usually buffer==GL_BACK, so put this extra check to simplify boolean eval on runtime*/
+ return (buffer != GL_BACK)
+ && (buffer == GL_FRONT_LEFT
+ || buffer == GL_FRONT_RIGHT
+ || buffer == GL_FRONT
+ || buffer == GL_FRONT_AND_BACK
+ || buffer == GL_LEFT
+ || buffer == GL_RIGHT);
+#endif
+}
+
+void PACKSPU_APIENTRY packspu_DrawBuffer(GLenum mode)
+{
+ GLboolean hadtoflush;
+
+ hadtoflush = packspuSyncOnFlushes();
+
+ crStateDrawBuffer(mode);
+ crPackDrawBuffer(mode);
+
+ if (hadtoflush && !packspuSyncOnFlushes())
+ packspu_Flush();
+}
+
+void PACKSPU_APIENTRY packspu_Finish( void )
+{
+ GET_THREAD(thread);
+ GLint writeback = CRPACKSPU_IS_WDDM_CRHGSMI() ? 1 : pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network;
+
+ if (pack_spu.swap)
+ {
+ crPackFinishSWAP();
+ }
+ else
+ {
+ crPackFinish();
+ }
+
+ if (packspuSyncOnFlushes())
+ {
+ if (writeback)
+ {
+ if (pack_spu.swap)
+ crPackWritebackSWAP(&writeback);
+ else
+ crPackWriteback(&writeback);
+
+ packspuFlush( (void *) thread );
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+ }
+}
+
+void PACKSPU_APIENTRY packspu_Flush( void )
+{
+ GET_THREAD(thread);
+ int writeback=1;
+ int found=0;
+
+ if (!thread->bInjectThread)
+ {
+ crPackFlush();
+ if (packspuSyncOnFlushes())
+ {
+ crPackWriteback(&writeback);
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+ }
+ else
+ {
+ int i;
+
+ crLockMutex(&_PackMutex);
+
+ /*Make sure we process commands in order they should appear, so flush other threads first*/
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse
+ && (thread != &pack_spu.thread[i]) && pack_spu.thread[i].netServer.conn
+ && pack_spu.thread[i].packer && pack_spu.thread[i].packer->currentBuffer)
+ {
+ packspuFlush((void *) &pack_spu.thread[i]);
+
+ if (pack_spu.thread[i].netServer.conn->u32ClientID == thread->netServer.conn->u32InjectClientID)
+ {
+ found=1;
+ }
+
+ }
+ }
+
+ if (!found)
+ {
+ /*Thread we're supposed to inject commands for has been detached,
+ so there's nothing to sync with and we should just pass commands through our own connection.
+ */
+ thread->netServer.conn->u32InjectClientID=0;
+ }
+
+ packspuFlush((void *) thread);
+
+ crUnlockMutex(&_PackMutex);
+ }
+}
+
+void PACKSPU_APIENTRY packspu_NewList(GLuint list, GLenum mode)
+{
+ crStateNewList(list, mode);
+ crPackNewList(list, mode);
+}
+
+void PACKSPU_APIENTRY packspu_EndList()
+{
+ crStateEndList();
+ crPackEndList();
+}
+
+void PACKSPU_APIENTRY packspu_VBoxWindowDestroy( GLint con, GLint window )
+{
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ GET_THREAD(thread);
+ if (con)
+ {
+ CRPackContext * curPacker = crPackGetContext();
+ CRASSERT(!thread || !thread->bInjectThread);
+ thread = GET_THREAD_VAL_ID(con);
+ crPackSetContext(thread->packer);
+ crPackWindowDestroy(window);
+ if (curPacker != thread->packer)
+ crPackSetContext(curPacker);
+ return;
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->bInjectThread);
+ }
+ crPackWindowDestroy(window);
+}
+
+GLint PACKSPU_APIENTRY packspu_VBoxWindowCreate( GLint con, const char *dpyName, GLint visBits )
+{
+ GET_THREAD(thread);
+ static int num_calls = 0;
+ int writeback = CRPACKSPU_IS_WDDM_CRHGSMI() ? 1 : pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network;
+ GLint return_val = (GLint) 0;
+ ThreadInfo *curThread = thread;
+ GLint retVal;
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (!con)
+ {
+ crError("connection expected!");
+ return 0;
+ }
+ thread = GET_THREAD_VAL_ID(con);
+ }
+ else
+ {
+ CRASSERT(!con);
+ if (!thread) {
+ thread = packspuNewThread(
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ NULL
+#endif
+ );
+ }
+ }
+ CRASSERT(thread);
+ CRASSERT(thread->packer);
+ CRASSERT(crPackGetContext() == (curThread ? curThread->packer : NULL));
+
+ crPackSetContext(thread->packer);
+
+ if (pack_spu.swap)
+ {
+ crPackWindowCreateSWAP( dpyName, visBits, &return_val, &writeback );
+ }
+ else
+ {
+ crPackWindowCreate( dpyName, visBits, &return_val, &writeback );
+ }
+ packspuFlush(thread);
+ if (!(thread->netServer.conn->actual_network))
+ {
+ retVal = num_calls++;
+ }
+ else
+ {
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ if (pack_spu.swap)
+ {
+ return_val = (GLint) SWAP32(return_val);
+ }
+ retVal = return_val;
+ }
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (thread != curThread)
+ {
+ if (curThread)
+ crPackSetContext(curThread->packer);
+ else
+ crPackSetContext(NULL);
+ }
+ }
+
+ return retVal;
+}
+
+GLint PACKSPU_APIENTRY packspu_WindowCreate( const char *dpyName, GLint visBits )
+{
+ return packspu_VBoxWindowCreate( 0, dpyName, visBits );
+}
+
+GLboolean PACKSPU_APIENTRY
+packspu_AreTexturesResident( GLsizei n, const GLuint * textures,
+ GLboolean * residences )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLboolean return_val = GL_TRUE;
+ GLsizei i;
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_AreTexturesResident doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+
+ if (pack_spu.swap)
+ {
+ crPackAreTexturesResidentSWAP( n, textures, residences, &return_val, &writeback );
+ }
+ else
+ {
+ crPackAreTexturesResident( n, textures, residences, &return_val, &writeback );
+ }
+ packspuFlush( (void *) thread );
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ /* Since the Chromium packer/unpacker can't return both 'residences'
+ * and the function's return value, compute the return value here.
+ */
+ for (i = 0; i < n; i++) {
+ if (!residences[i]) {
+ return_val = GL_FALSE;
+ break;
+ }
+ }
+
+ return return_val;
+}
+
+
+GLboolean PACKSPU_APIENTRY
+packspu_AreProgramsResidentNV( GLsizei n, const GLuint * ids,
+ GLboolean * residences )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLboolean return_val = GL_TRUE;
+ GLsizei i;
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_AreProgramsResidentNV doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackAreProgramsResidentNVSWAP( n, ids, residences, &return_val, &writeback );
+ }
+ else
+ {
+ crPackAreProgramsResidentNV( n, ids, residences, &return_val, &writeback );
+ }
+ packspuFlush( (void *) thread );
+
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ /* Since the Chromium packer/unpacker can't return both 'residences'
+ * and the function's return value, compute the return value here.
+ */
+ for (i = 0; i < n; i++) {
+ if (!residences[i]) {
+ return_val = GL_FALSE;
+ break;
+ }
+ }
+
+ return return_val;
+}
+
+void PACKSPU_APIENTRY packspu_GetPolygonStipple( GLubyte * mask )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ {
+ crPackGetPolygonStippleSWAP( mask, &writeback );
+ }
+ else
+ {
+ crPackGetPolygonStipple( mask, &writeback );
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+void PACKSPU_APIENTRY packspu_GetPixelMapfv( GLenum map, GLfloat * values )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ {
+ crPackGetPixelMapfvSWAP( map, values, &writeback );
+ }
+ else
+ {
+ crPackGetPixelMapfv( map, values, &writeback );
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+void PACKSPU_APIENTRY packspu_GetPixelMapuiv( GLenum map, GLuint * values )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ {
+ crPackGetPixelMapuivSWAP( map, values, &writeback );
+ }
+ else
+ {
+ crPackGetPixelMapuiv( map, values, &writeback );
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+void PACKSPU_APIENTRY packspu_GetPixelMapusv( GLenum map, GLushort * values )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (pack_spu.swap)
+ {
+ crPackGetPixelMapusvSWAP( map, values, &writeback );
+ }
+ else
+ {
+ crPackGetPixelMapusv( map, values, &writeback );
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+static void packspuFluchOnThreadSwitch(GLboolean fEnable)
+{
+ GET_THREAD(thread);
+ if (thread->currentContext->fAutoFlush == fEnable)
+ return;
+
+ thread->currentContext->fAutoFlush = fEnable;
+ thread->currentContext->currentThread = fEnable ? thread : NULL;
+}
+
+static void packspuCheckZerroVertAttr(GLboolean fEnable)
+{
+ GET_THREAD(thread);
+
+ thread->currentContext->fCheckZerroVertAttr = fEnable;
+}
+
+void PACKSPU_APIENTRY packspu_ChromiumParameteriCR(GLenum target, GLint value)
+{
+ switch (target)
+ {
+ case GL_FLUSH_ON_THREAD_SWITCH_CR:
+ /* this is a pure packspu state, don't propagate it any further */
+ packspuFluchOnThreadSwitch(value);
+ return;
+ case GL_CHECK_ZERO_VERT_ARRT:
+ packspuCheckZerroVertAttr(value);
+ return;
+ case GL_SHARE_CONTEXT_RESOURCES_CR:
+ crStateShareContext(value);
+ break;
+ case GL_RCUSAGE_TEXTURE_SET_CR:
+ {
+ Assert(value);
+ crStateSetTextureUsed(value, GL_TRUE);
+ break;
+ }
+ case GL_RCUSAGE_TEXTURE_CLEAR_CR:
+ {
+ Assert(value);
+#ifdef DEBUG
+ {
+ CRContext *pCurState = crStateGetCurrent();
+ CRTextureObj *tobj = (CRTextureObj*)crHashtableSearch(pCurState->shared->textureTable, value);
+ Assert(tobj);
+ }
+#endif
+ crStateSetTextureUsed(value, GL_FALSE);
+ break;
+ }
+ default:
+ break;
+ }
+ crPackChromiumParameteriCR(target, value);
+}
+
+GLenum PACKSPU_APIENTRY packspu_GetError( void )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ GLenum return_val = (GLenum) 0;
+ CRContext *pCurState = crStateGetCurrent();
+ NOREF(pCurState); /* it's unused, but I don't know about side effects.. */
+
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GetError doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGetErrorSWAP( &return_val, &writeback );
+ }
+ else
+ {
+ crPackGetError( &return_val, &writeback );
+ }
+
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+
+ if (pack_spu.swap)
+ {
+ return_val = (GLenum) SWAP32(return_val);
+ }
+
+ return return_val;
+}
+
+#ifdef CHROMIUM_THREADSAFE
+GLint PACKSPU_APIENTRY packspu_VBoxPackSetInjectThread(struct VBOXUHGSMI *pHgsmi)
+{
+ GLint con = 0;
+ int i;
+ GET_THREAD(thread);
+ CRASSERT(!thread);
+ RT_NOREF(pHgsmi);
+ crLockMutex(&_PackMutex);
+ {
+ CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI() || (pack_spu.numThreads>0));
+ CRASSERT(pack_spu.numThreads<MAX_THREADS);
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (!pack_spu.thread[i].inUse)
+ {
+ thread = &pack_spu.thread[i];
+ break;
+ }
+ }
+ CRASSERT(thread);
+
+ thread->inUse = GL_TRUE;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI())
+ thread->id = crThreadID();
+ else
+ thread->id = THREAD_OFFSET_MAGIC + i;
+ thread->currentContext = NULL;
+ thread->bInjectThread = GL_TRUE;
+
+ thread->netServer.name = crStrdup(pack_spu.name);
+ thread->netServer.buffer_size = 64 * 1024;
+
+ packspuConnectToServer(&(thread->netServer)
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ CRASSERT(thread->netServer.conn);
+
+ CRASSERT(thread->packer == NULL);
+ thread->packer = crPackNewContext( pack_spu.swap );
+ CRASSERT(thread->packer);
+ crPackInitBuffer(&(thread->buffer), crNetAlloc(thread->netServer.conn),
+ thread->netServer.conn->buffer_size, thread->netServer.conn->mtu);
+ thread->buffer.canBarf = thread->netServer.conn->Barf ? GL_TRUE : GL_FALSE;
+
+ crPackSetBuffer( thread->packer, &thread->buffer );
+ crPackFlushFunc( thread->packer, packspuFlush );
+ crPackFlushArg( thread->packer, (void *) thread );
+ crPackSendHugeFunc( thread->packer, packspuHuge );
+ crPackSetContext( thread->packer );
+
+ crSetTSD(&_PackTSD, thread);
+
+ pack_spu.numThreads++;
+ }
+ crUnlockMutex(&_PackMutex);
+
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ CRASSERT(thread->id - THREAD_OFFSET_MAGIC < RT_ELEMENTS(pack_spu.thread)
+ && GET_THREAD_VAL_ID(thread->id) == thread);
+ con = thread->id;
+ }
+ return con;
+}
+
+GLuint PACKSPU_APIENTRY packspu_VBoxPackGetInjectID(GLint con)
+{
+ GLuint ret;
+
+ crLockMutex(&_PackMutex);
+ {
+ ThreadInfo *thread = NULL;
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ if (!con)
+ {
+ crError("connection expected!");
+ return 0;
+ }
+ thread = GET_THREAD_VAL_ID(con);
+ }
+ else
+ {
+ CRASSERT(!con);
+ thread = GET_THREAD_VAL();
+ }
+ CRASSERT(thread && thread->netServer.conn && thread->netServer.conn->type==CR_VBOXHGCM);
+ ret = thread->netServer.conn->u32ClientID;
+ }
+ crUnlockMutex(&_PackMutex);
+
+ return ret;
+}
+
+void PACKSPU_APIENTRY packspu_VBoxPackSetInjectID(GLuint id)
+{
+ crLockMutex(&_PackMutex);
+ {
+ GET_THREAD(thread);
+
+ CRASSERT(thread && thread->netServer.conn && thread->netServer.conn->type==CR_VBOXHGCM && thread->bInjectThread);
+ thread->netServer.conn->u32InjectClientID = id;
+ }
+ crUnlockMutex(&_PackMutex);
+}
+
+void PACKSPU_APIENTRY packspu_VBoxAttachThread()
+{
+#if 0
+ int i;
+ GET_THREAD(thread);
+
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse && thread==&pack_spu.thread[i] && thread->id==crThreadID())
+ {
+ crError("2nd attach to same thread");
+ }
+ }
+#endif
+
+ crSetTSD(&_PackTSD, NULL);
+
+ crStateVBoxAttachThread();
+}
+
+void PACKSPU_APIENTRY packspu_VBoxDetachThread()
+{
+ if (CRPACKSPU_IS_WDDM_CRHGSMI())
+ {
+ crPackSetContext(NULL);
+ crSetTSD(&_PackTSD, NULL);
+ }
+ else
+ {
+ int i;
+ GET_THREAD(thread);
+ if (thread)
+ {
+ crLockMutex(&_PackMutex);
+
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse && thread==&pack_spu.thread[i]
+ && thread->id==crThreadID() && thread->netServer.conn)
+ {
+ CRASSERT(pack_spu.numThreads>0);
+
+ packspuFlush((void *) thread);
+
+ if (pack_spu.thread[i].packer)
+ {
+ CR_LOCK_PACKER_CONTEXT(thread->packer);
+ crPackSetContext(NULL);
+ CR_UNLOCK_PACKER_CONTEXT(thread->packer);
+ crPackDeleteContext(pack_spu.thread[i].packer);
+
+ if (pack_spu.thread[i].buffer.pack)
+ {
+ crNetFree(pack_spu.thread[i].netServer.conn, pack_spu.thread[i].buffer.pack);
+ pack_spu.thread[i].buffer.pack = NULL;
+ }
+ }
+ crNetFreeConnection(pack_spu.thread[i].netServer.conn);
+
+ if (pack_spu.thread[i].netServer.name)
+ crFree(pack_spu.thread[i].netServer.name);
+
+ pack_spu.numThreads--;
+ /*note can't shift the array here, because other threads have TLS references to array elements*/
+ crMemZero(&pack_spu.thread[i], sizeof(ThreadInfo));
+
+ crSetTSD(&_PackTSD, NULL);
+
+ if (i==pack_spu.idxThreadInUse)
+ {
+ for (i=0; i<MAX_THREADS; ++i)
+ {
+ if (pack_spu.thread[i].inUse)
+ {
+ pack_spu.idxThreadInUse=i;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ for (i=0; i<CR_MAX_CONTEXTS; ++i)
+ {
+ ContextInfo *ctx = &pack_spu.context[i];
+ if (ctx->currentThread == thread)
+ {
+ CRASSERT(ctx->fAutoFlush);
+ ctx->currentThread = NULL;
+ }
+ }
+
+ crUnlockMutex(&_PackMutex);
+ }
+ }
+
+ crStateVBoxDetachThread();
+}
+
+#ifdef WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ (void) lpvReserved;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ crInitMutex(&_PackMutex);
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ crFreeMutex(&_PackMutex);
+ crNetTearDown();
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+#endif
+
+#else /*ifdef CHROMIUM_THREADSAFE*/
+GLint PACKSPU_APIENTRY packspu_VBoxPackSetInjectThread(struct VBOXUHGSMI *pHgsmi)
+{
+}
+
+GLuint PACKSPU_APIENTRY packspu_VBoxPackGetInjectID(GLint con)
+{
+ return 0;
+}
+
+void PACKSPU_APIENTRY packspu_VBoxPackSetInjectID(GLuint id)
+{
+ (void) id;
+}
+
+void PACKSPU_APIENTRY packspu_VBoxPackAttachThread()
+{
+}
+
+void PACKSPU_APIENTRY packspu_VBoxPackDetachThread()
+{
+}
+#endif /*CHROMIUM_THREADSAFE*/
+
+void PACKSPU_APIENTRY packspu_VBoxPresentComposition(GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor,
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry)
+{
+ RT_NOREF(win, pCompositor, pChangedEntry);
+}
+
+void PACKSPU_APIENTRY packspu_StringMarkerGREMEDY(GLsizei len, const GLvoid *string)
+{
+ RT_NOREF(len, string);
+}
+
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_net.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_net.c
new file mode 100644
index 00000000..6d5d41d6
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_net.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_pack.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "cr_pixeldata.h"
+#include "cr_protocol.h"
+#include "cr_error.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+
+uint32_t g_u32VBoxHostCaps = 0;
+
+static void
+packspuWriteback( const CRMessageWriteback *wb )
+{
+ int *writeback;
+ crMemcpy( &writeback, &(wb->writeback_ptr), sizeof( writeback ) );
+ *writeback = 0;
+}
+
+/**
+ * XXX Note that this routine is identical to crNetRecvReadback except
+ * we set *writeback=0 instead of decrementing it. Hmmm.
+ */
+static void
+packspuReadback( const CRMessageReadback *rb, unsigned int len )
+{
+ /* minus the header, the destination pointer,
+ * *and* the implicit writeback pointer at the head. */
+
+ int payload_len = len - sizeof( *rb );
+ int *writeback;
+ void *dest_ptr;
+ crMemcpy( &writeback, &(rb->writeback_ptr), sizeof( writeback ) );
+ crMemcpy( &dest_ptr, &(rb->readback_ptr), sizeof( dest_ptr ) );
+
+ *writeback = 0;
+ crMemcpy( dest_ptr, ((char *)rb) + sizeof(*rb), payload_len );
+}
+
+static void
+packspuReadPixels( const CRMessageReadPixels *rp, unsigned int len )
+{
+ crNetRecvReadPixels( rp, len );
+ --pack_spu.ReadPixels;
+}
+
+static int
+packspuReceiveData( CRConnection *conn, CRMessage *msg, unsigned int len )
+{
+ RT_NOREF(conn);
+ if (msg->header.type == CR_MESSAGE_REDIR_PTR)
+ msg = (CRMessage*) msg->redirptr.pMessage;
+
+ switch( msg->header.type )
+ {
+ case CR_MESSAGE_READ_PIXELS:
+ packspuReadPixels( &(msg->readPixels), len );
+ break;
+ case CR_MESSAGE_WRITEBACK:
+ packspuWriteback( &(msg->writeback) );
+ break;
+ case CR_MESSAGE_READBACK:
+ packspuReadback( &(msg->readback), len );
+ break;
+ default:
+ /*crWarning( "Why is the pack SPU getting a message of type 0x%x?", msg->type ); */
+ return 0; /* NOT HANDLED */
+ }
+ return 1; /* HANDLED */
+}
+
+static CRMessageOpcodes *
+__prependHeader( CRPackBuffer *buf, unsigned int *len, unsigned int senderID )
+{
+ int num_opcodes;
+ CRMessageOpcodes *hdr;
+ RT_NOREF(senderID);
+
+ CRASSERT( buf );
+ CRASSERT( buf->opcode_current < buf->opcode_start );
+ CRASSERT( buf->opcode_current >= buf->opcode_end );
+ CRASSERT( buf->data_current > buf->data_start );
+ CRASSERT( buf->data_current <= buf->data_end );
+
+ num_opcodes = buf->opcode_start - buf->opcode_current;
+ hdr = (CRMessageOpcodes *)
+ ( buf->data_start - ( ( num_opcodes + 3 ) & ~0x3 ) - sizeof(*hdr) );
+
+ CRASSERT( (void *) hdr >= buf->pack );
+
+ if (pack_spu.swap)
+ {
+ hdr->header.type = (CRMessageType) SWAP32(CR_MESSAGE_OPCODES);
+ hdr->numOpcodes = SWAP32(num_opcodes);
+ }
+ else
+ {
+ hdr->header.type = CR_MESSAGE_OPCODES;
+ hdr->numOpcodes = num_opcodes;
+ }
+
+ *len = buf->data_current - (unsigned char *) hdr;
+
+ return hdr;
+}
+
+
+/*
+ * This is called from either the Pack SPU and the packer library whenever
+ * we need to send a data buffer to the server.
+ */
+void packspuFlush(void *arg )
+{
+ ThreadInfo *thread = (ThreadInfo *) arg;
+ ContextInfo *ctx;
+ unsigned int len;
+ CRMessageOpcodes *hdr;
+ CRPackBuffer *buf;
+
+ /* we should _always_ pass a valid <arg> value */
+ CRASSERT(thread && thread->inUse);
+#ifdef CHROMIUM_THREADSAFE
+ CR_LOCK_PACKER_CONTEXT(thread->packer);
+#endif
+ ctx = thread->currentContext;
+ buf = &(thread->buffer);
+ CRASSERT(buf);
+
+ if (ctx && ctx->fCheckZerroVertAttr)
+ crStateCurrentRecoverNew(ctx->clientState, &thread->packer->current);
+
+ /* We're done packing into the current buffer, unbind it */
+ crPackReleaseBuffer( thread->packer );
+
+ /*
+ printf("%s thread=%p thread->id = %d thread->pc=%p t2->id=%d t2->pc=%p packbuf=%p packbuf=%p\n",
+ __FUNCTION__, (void*) thread, (int) thread->id, thread->packer,
+ (int) t2->id, t2->packer,
+ buf->pack, thread->packer->buffer.pack);
+ */
+
+ if ( buf->opcode_current == buf->opcode_start ) {
+ /*
+ printf("%s early return\n", __FUNCTION__);
+ */
+ /* XXX these calls seem to help, but might be appropriate */
+ crPackSetBuffer( thread->packer, buf );
+ crPackResetPointers(thread->packer);
+#ifdef CHROMIUM_THREADSAFE
+ CR_UNLOCK_PACKER_CONTEXT(thread->packer);
+#endif
+ return;
+ }
+
+ hdr = __prependHeader( buf, &len, 0 );
+
+ CRASSERT( thread->netServer.conn );
+
+ if ( buf->holds_BeginEnd )
+ {
+ /*crDebug("crNetBarf %d, (%d)", len, buf->size);*/
+ crNetBarf( thread->netServer.conn, &(buf->pack), hdr, len );
+ }
+ else
+ {
+ /*crDebug("crNetSend %d, (%d)", len, buf->size);*/
+ crNetSend( thread->netServer.conn, &(buf->pack), hdr, len );
+ }
+
+ buf->pack = crNetAlloc( thread->netServer.conn );
+
+ /* The network may have found a new mtu */
+ buf->mtu = thread->netServer.conn->mtu;
+
+ crPackSetBuffer( thread->packer, buf );
+
+ crPackResetPointers(thread->packer);
+
+#ifdef CHROMIUM_THREADSAFE
+ CR_UNLOCK_PACKER_CONTEXT(thread->packer);
+#endif
+}
+
+
+/**
+ * XXX NOTE: there's a lot of duplicate code here common to the
+ * pack, tilesort and replicate SPUs. Try to simplify someday!
+ */
+void packspuHuge( CROpcode opcode, void *buf )
+{
+ GET_THREAD(thread);
+ unsigned int len;
+ unsigned char *src;
+ CRMessageOpcodes *msg;
+
+ CRASSERT(thread);
+
+ /* packet length is indicated by the variable length field, and
+ includes an additional word for the opcode (with alignment) and
+ a header */
+ len = ((unsigned int *) buf)[-1];
+ if (pack_spu.swap)
+ {
+ /* It's already been swapped, swap it back. */
+ len = SWAP32(len);
+ }
+ len += 4 + sizeof(CRMessageOpcodes);
+
+ /* write the opcode in just before the length */
+ ((unsigned char *) buf)[-5] = (unsigned char) opcode;
+
+ /* fix up the pointer to the packet to include the length & opcode
+ & header */
+ src = (unsigned char *) buf - 8 - sizeof(CRMessageOpcodes);
+
+ msg = (CRMessageOpcodes *) src;
+
+ if (pack_spu.swap)
+ {
+ msg->header.type = (CRMessageType) SWAP32(CR_MESSAGE_OPCODES);
+ msg->numOpcodes = SWAP32(1);
+ }
+ else
+ {
+ msg->header.type = CR_MESSAGE_OPCODES;
+ msg->numOpcodes = 1;
+ }
+
+ CRASSERT( thread->netServer.conn );
+ crNetSend( thread->netServer.conn, NULL, src, len );
+}
+
+static void packspuFirstConnectToServer( CRNetServer *server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , struct VBOXUHGSMI *pHgsmi
+#endif
+ )
+{
+ crNetInit( packspuReceiveData, NULL );
+ crNetServerConnect( server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ if (server->conn)
+ {
+ g_u32VBoxHostCaps = crNetHostCapsGet();
+ crPackCapsSet(g_u32VBoxHostCaps);
+ }
+}
+
+void packspuConnectToServer( CRNetServer *server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , struct VBOXUHGSMI *pHgsmi
+#endif
+ )
+{
+ if (pack_spu.numThreads == 0) {
+ packspuFirstConnectToServer( server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ if (!server->conn) {
+ crError("packspuConnectToServer: no connection on first create!");
+ return;
+ }
+ pack_spu.swap = server->conn->swap;
+ }
+ else {
+ /* a new pthread */
+ crNetNewClient(server
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#endif
+ );
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_pixel.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_pixel.c
new file mode 100644
index 00000000..06734b1e
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_pixel.c
@@ -0,0 +1,727 @@
+/* Copyright (c) 2001, Stanford University
+ All rights reserved.
+
+ See the file LICENSE.txt for information on redistributing this software. */
+
+#include "cr_packfunctions.h"
+#include "cr_glstate.h"
+#include "cr_pixeldata.h"
+#include "cr_version.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+
+static GLboolean packspu_CheckTexImageFormat(GLenum format)
+{
+ if (format!=GL_COLOR_INDEX
+ && format!=GL_RED
+ && format!=GL_GREEN
+ && format!=GL_BLUE
+ && format!=GL_ALPHA
+ && format!=GL_RGB
+ && format!=GL_BGR
+ && format!=GL_RGBA
+ && format!=GL_BGRA
+ && format!=GL_LUMINANCE
+ && format!=GL_LUMINANCE_ALPHA
+ && format!=GL_DEPTH_COMPONENT
+ && format!=GL_DEPTH_STENCIL)
+ {
+ /*crWarning("crPackCheckTexImageFormat FAILED format 0x%x isn't valid", format);*/
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+static GLboolean packspu_CheckTexImageType(GLenum type)
+{
+ if (type!=GL_UNSIGNED_BYTE
+ && type!=GL_BYTE
+ && type!=GL_BITMAP
+ && type!=GL_UNSIGNED_SHORT
+ && type!=GL_SHORT
+ && type!=GL_UNSIGNED_INT
+ && type!=GL_INT
+ && type!=GL_FLOAT
+ && type!=GL_UNSIGNED_BYTE_3_3_2
+ && type!=GL_UNSIGNED_BYTE_2_3_3_REV
+ && type!=GL_UNSIGNED_SHORT_5_6_5
+ && type!=GL_UNSIGNED_SHORT_5_6_5_REV
+ && type!=GL_UNSIGNED_SHORT_4_4_4_4
+ && type!=GL_UNSIGNED_SHORT_4_4_4_4_REV
+ && type!=GL_UNSIGNED_SHORT_5_5_5_1
+ && type!=GL_UNSIGNED_SHORT_1_5_5_5_REV
+ && type!=GL_UNSIGNED_INT_8_8_8_8
+ && type!=GL_UNSIGNED_INT_8_8_8_8_REV
+ && type!=GL_UNSIGNED_INT_10_10_10_2
+ && type!=GL_UNSIGNED_INT_2_10_10_10_REV
+ && type!=GL_UNSIGNED_INT_24_8)
+ {
+ /*crWarning("crPackCheckTexImageType FAILED type 0x%x isn't valid", type);*/
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+static GLboolean packspu_CheckTexImageInternalFormat(GLint internalformat)
+{
+ if (internalformat!=1
+ && internalformat!=2
+ && internalformat!=3
+ && internalformat!=4
+ && internalformat!=GL_ALPHA
+ && internalformat!=GL_ALPHA4
+ && internalformat!=GL_ALPHA8
+ && internalformat!=GL_ALPHA12
+ && internalformat!=GL_ALPHA16
+ && internalformat!=GL_COMPRESSED_ALPHA
+ && internalformat!=GL_COMPRESSED_LUMINANCE
+ && internalformat!=GL_COMPRESSED_LUMINANCE_ALPHA
+ && internalformat!=GL_COMPRESSED_INTENSITY
+ && internalformat!=GL_COMPRESSED_RGB
+ && internalformat!=GL_COMPRESSED_RGBA
+ && internalformat!=GL_DEPTH_COMPONENT
+ && internalformat!=GL_DEPTH_COMPONENT16
+ && internalformat!=GL_DEPTH_COMPONENT24
+ && internalformat!=GL_DEPTH_COMPONENT32
+ && internalformat!=GL_DEPTH24_STENCIL8
+ && internalformat!=GL_LUMINANCE
+ && internalformat!=GL_LUMINANCE4
+ && internalformat!=GL_LUMINANCE8
+ && internalformat!=GL_LUMINANCE12
+ && internalformat!=GL_LUMINANCE16
+ && internalformat!=GL_LUMINANCE_ALPHA
+ && internalformat!=GL_LUMINANCE4_ALPHA4
+ && internalformat!=GL_LUMINANCE6_ALPHA2
+ && internalformat!=GL_LUMINANCE8_ALPHA8
+ && internalformat!=GL_LUMINANCE12_ALPHA4
+ && internalformat!=GL_LUMINANCE12_ALPHA12
+ && internalformat!=GL_LUMINANCE16_ALPHA16
+ && internalformat!=GL_INTENSITY
+ && internalformat!=GL_INTENSITY4
+ && internalformat!=GL_INTENSITY8
+ && internalformat!=GL_INTENSITY12
+ && internalformat!=GL_INTENSITY16
+ && internalformat!=GL_R3_G3_B2
+ && internalformat!=GL_RGB
+ && internalformat!=GL_RGB4
+ && internalformat!=GL_RGB5
+ && internalformat!=GL_RGB8
+ && internalformat!=GL_RGB10
+ && internalformat!=GL_RGB12
+ && internalformat!=GL_RGB16
+ && internalformat!=GL_RGBA
+ && internalformat!=GL_RGBA2
+ && internalformat!=GL_RGBA4
+ && internalformat!=GL_RGB5_A1
+ && internalformat!=GL_RGBA8
+ && internalformat!=GL_RGB10_A2
+ && internalformat!=GL_RGBA12
+ && internalformat!=GL_RGBA16
+ && internalformat!=GL_SLUMINANCE
+ && internalformat!=GL_SLUMINANCE8
+ && internalformat!=GL_SLUMINANCE_ALPHA
+ && internalformat!=GL_SLUMINANCE8_ALPHA8
+ && internalformat!=GL_SRGB
+ && internalformat!=GL_SRGB8
+ && internalformat!=GL_SRGB_ALPHA
+ && internalformat!=GL_SRGB8_ALPHA8
+#ifdef CR_EXT_texture_compression_s3tc
+ && internalformat!=GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+ && internalformat!=GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+ && internalformat!=GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+ && internalformat!=GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+# ifdef CR_EXT_texture_sRGB
+ && internalformat!=GL_COMPRESSED_SRGB_S3TC_DXT1_EXT
+ && internalformat!=GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
+ && internalformat!=GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
+ && internalformat!=GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
+# endif
+#endif
+ /** @todo ARB_texture_float*/
+ && internalformat!=GL_RGBA32F_ARB
+ && internalformat!=GL_RGB32F_ARB
+ && internalformat!=GL_ALPHA32F_ARB
+ && internalformat!=GL_INTENSITY32F_ARB
+ && internalformat!=GL_LUMINANCE32F_ARB
+ && internalformat!=GL_LUMINANCE_ALPHA32F_ARB
+ && internalformat!=GL_RGBA16F_ARB
+ && internalformat!=GL_RGB16F_ARB
+ && internalformat!=GL_ALPHA16F_ARB
+ && internalformat!=GL_INTENSITY16F_ARB
+ && internalformat!=GL_LUMINANCE16F_ARB
+ && internalformat!=GL_LUMINANCE_ALPHA16F_ARB
+ )
+ {
+ /*crWarning("crPackCheckTexImageInternalFormat FAILED internalformat 0x%x isn't valid", internalformat);*/
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+static GLboolean packspu_CheckTexImageParams(GLint internalformat, GLenum format, GLenum type)
+{
+ return packspu_CheckTexImageFormat(format)
+ && packspu_CheckTexImageType(type)
+ && packspu_CheckTexImageInternalFormat(internalformat);
+}
+
+static GLboolean packspu_CheckTexImageFormatType(GLenum format, GLenum type)
+{
+ return packspu_CheckTexImageFormat(format)
+ && packspu_CheckTexImageType(type);
+}
+
+static const CRPixelPackState _defaultPacking = {
+ 0, /* rowLength */
+ 0, /* skipRows */
+ 0, /* skipPixels */
+ 1, /* alignment */
+ 0, /* imageHeight */
+ 0, /* skipImages */
+ GL_FALSE, /* swapBytes */
+ GL_FALSE, /* psLSBFirst */
+};
+
+#define APPLY_IF_NEQ(state, field, enum) \
+ if (state.field != _defaultPacking.field) \
+ { \
+ crPackPixelStorei(enum, state.field); \
+ }
+
+#define RESTORE_IF_NEQ(state, field, enum) \
+ if (state.field != _defaultPacking.field) \
+ { \
+ crPackPixelStorei(enum, _defaultPacking.field); \
+ }
+
+static void packspu_ApplyUnpackState(void)
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ APPLY_IF_NEQ(clientState->unpack, rowLength, GL_UNPACK_ROW_LENGTH);
+ APPLY_IF_NEQ(clientState->unpack, skipRows, GL_UNPACK_SKIP_ROWS);
+ APPLY_IF_NEQ(clientState->unpack, skipPixels, GL_UNPACK_SKIP_PIXELS);
+ APPLY_IF_NEQ(clientState->unpack, alignment, GL_UNPACK_ALIGNMENT);
+ APPLY_IF_NEQ(clientState->unpack, imageHeight, GL_UNPACK_IMAGE_HEIGHT);
+ APPLY_IF_NEQ(clientState->unpack, skipImages, GL_UNPACK_SKIP_IMAGES);
+ APPLY_IF_NEQ(clientState->unpack, swapBytes, GL_UNPACK_SWAP_BYTES);
+ APPLY_IF_NEQ(clientState->unpack, psLSBFirst, GL_UNPACK_LSB_FIRST);
+}
+
+static void packspu_RestoreUnpackState(void)
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ RESTORE_IF_NEQ(clientState->unpack, rowLength, GL_UNPACK_ROW_LENGTH);
+ RESTORE_IF_NEQ(clientState->unpack, skipRows, GL_UNPACK_SKIP_ROWS);
+ RESTORE_IF_NEQ(clientState->unpack, skipPixels, GL_UNPACK_SKIP_PIXELS);
+ RESTORE_IF_NEQ(clientState->unpack, alignment, GL_UNPACK_ALIGNMENT);
+ RESTORE_IF_NEQ(clientState->unpack, imageHeight, GL_UNPACK_IMAGE_HEIGHT);
+ RESTORE_IF_NEQ(clientState->unpack, skipImages, GL_UNPACK_SKIP_IMAGES);
+ RESTORE_IF_NEQ(clientState->unpack, swapBytes, GL_UNPACK_SWAP_BYTES);
+ RESTORE_IF_NEQ(clientState->unpack, psLSBFirst, GL_UNPACK_LSB_FIRST);
+}
+
+static void packspu_ApplyPackState(void)
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ APPLY_IF_NEQ(clientState->pack, rowLength, GL_PACK_ROW_LENGTH);
+ APPLY_IF_NEQ(clientState->pack, skipRows, GL_PACK_SKIP_ROWS);
+ APPLY_IF_NEQ(clientState->pack, skipPixels, GL_PACK_SKIP_PIXELS);
+ APPLY_IF_NEQ(clientState->pack, alignment, GL_PACK_ALIGNMENT);
+ APPLY_IF_NEQ(clientState->pack, imageHeight, GL_PACK_IMAGE_HEIGHT);
+ APPLY_IF_NEQ(clientState->pack, skipImages, GL_PACK_SKIP_IMAGES);
+ APPLY_IF_NEQ(clientState->pack, swapBytes, GL_PACK_SWAP_BYTES);
+ APPLY_IF_NEQ(clientState->pack, psLSBFirst, GL_PACK_LSB_FIRST);
+}
+
+static void packspu_RestorePackState(void)
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ RESTORE_IF_NEQ(clientState->pack, rowLength, GL_PACK_ROW_LENGTH);
+ RESTORE_IF_NEQ(clientState->pack, skipRows, GL_PACK_SKIP_ROWS);
+ RESTORE_IF_NEQ(clientState->pack, skipPixels, GL_PACK_SKIP_PIXELS);
+ RESTORE_IF_NEQ(clientState->pack, alignment, GL_PACK_ALIGNMENT);
+ RESTORE_IF_NEQ(clientState->pack, imageHeight, GL_PACK_IMAGE_HEIGHT);
+ RESTORE_IF_NEQ(clientState->pack, skipImages, GL_PACK_SKIP_IMAGES);
+ RESTORE_IF_NEQ(clientState->pack, swapBytes, GL_PACK_SWAP_BYTES);
+ RESTORE_IF_NEQ(clientState->pack, psLSBFirst, GL_PACK_LSB_FIRST);
+}
+
+void PACKSPU_APIENTRY packspu_PixelStoref( GLenum pname, GLfloat param )
+{
+ /* NOTE: we do not send pixel store parameters to the server!
+ * When we pack a glDrawPixels or glTexImage2D image we interpret
+ * the user's pixel store parameters at that time and pack the
+ * image in a canonical layout (see util/pixel.c).
+ */
+ crStatePixelStoref( pname, param );
+}
+
+void PACKSPU_APIENTRY packspu_PixelStorei( GLenum pname, GLint param )
+{
+ crStatePixelStorei( pname, param );
+}
+
+void PACKSPU_APIENTRY packspu_DrawPixels( GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackDrawPixels( width, height, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY packspu_ReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels )
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+ int writeback;
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_ApplyPackState();
+ }
+
+ crPackReadPixels(x, y, width, height, format, type, pixels, &(clientState->pack), &writeback);
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_RestorePackState();
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ pack_spu.ReadPixels++;
+
+ packspuFlush((void *) thread);
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+/** @todo check with pbo's*/
+void PACKSPU_APIENTRY packspu_CopyPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum type )
+{
+ GET_THREAD(thread);
+ if (pack_spu.swap)
+ crPackCopyPixelsSWAP( x, y, width, height, type );
+ else
+ crPackCopyPixels( x, y, width, height, type );
+ /* XXX why flush here? */
+ packspuFlush( (void *) thread );
+}
+
+void PACKSPU_APIENTRY packspu_Bitmap( GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackBitmap(width, height, xorig, yorig, xmove, ymove, bitmap, &(clientState->unpack));
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY packspu_TexImage1D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (!packspu_CheckTexImageParams(internalformat, format, type))
+ {
+ if (pixels || crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ crWarning("packspu_TexImage1D invalid internalFormat(%x)/format(%x)/type(%x)", internalformat, format, type);
+ return;
+ }
+ internalformat = packspu_CheckTexImageInternalFormat(internalformat) ? internalformat:GL_RGBA;
+ format = packspu_CheckTexImageFormat(format) ? format:GL_RGBA;
+ type = packspu_CheckTexImageType(type) ? type:GL_UNSIGNED_BYTE;
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexImage1DSWAP( target, level, internalformat, width, border, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexImage1D( target, level, internalformat, width, border, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY packspu_TexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (!packspu_CheckTexImageParams(internalformat, format, type))
+ {
+ if (pixels || crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ crWarning("packspu_TexImage2D invalid internalFormat(%x)/format(%x)/type(%x)", internalformat, format, type);
+ return;
+ }
+ internalformat = packspu_CheckTexImageInternalFormat(internalformat) ? internalformat:GL_RGBA;
+ format = packspu_CheckTexImageFormat(format) ? format:GL_RGBA;
+ type = packspu_CheckTexImageType(type) ? type:GL_UNSIGNED_BYTE;
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexImage2DSWAP( target, level, internalformat, width, height, border, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexImage2D( target, level, internalformat, width, height, border, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+#ifdef GL_EXT_texture3D
+void PACKSPU_APIENTRY packspu_TexImage3DEXT( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexImage3DEXTSWAP( target, level, internalformat, width, height, depth, border, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexImage3DEXT( target, level, internalformat, width, height, depth, border, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+#endif
+
+#ifdef CR_OPENGL_VERSION_1_2
+void PACKSPU_APIENTRY packspu_TexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexImage3DSWAP( target, level, internalformat, width, height, depth, border, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexImage3D( target, level, internalformat, width, height, depth, border, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+#endif /* CR_OPENGL_VERSION_1_2 */
+
+void PACKSPU_APIENTRY packspu_TexSubImage1D( GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (!packspu_CheckTexImageFormatType(format, type))
+ {
+ crWarning("packspu_TexSubImage1D invalid format(%x)/type(%x)", format, type);
+ return;
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexSubImage1DSWAP( target, level, xoffset, width, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexSubImage1D( target, level, xoffset, width, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY packspu_TexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (!packspu_CheckTexImageFormatType(format, type))
+ {
+ crWarning("packspu_TexSubImage2D invalid format(%x)/type(%x)", format, type);
+ return;
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexSubImage2DSWAP( target, level, xoffset, yoffset, width, height, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+#ifdef CR_OPENGL_VERSION_1_2
+void PACKSPU_APIENTRY packspu_TexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ if (pack_spu.swap)
+ crPackTexSubImage3DSWAP( target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels, &(clientState->unpack) );
+ else
+ crPackTexSubImage3D( target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels, &(clientState->unpack) );
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+#endif /* CR_OPENGL_VERSION_1_2 */
+
+void PACKSPU_APIENTRY packspu_ZPixCR( GLsizei width, GLsizei height, GLenum format, GLenum type, GLenum ztype, GLint zparm, GLint length, const GLvoid *pixels )
+{
+ GET_CONTEXT(ctx);
+ CRClientState *clientState = &(ctx->clientState->client);
+ if (pack_spu.swap)
+ crPackZPixCRSWAP( width, height, format, type, ztype, zparm, length, pixels, &(clientState->unpack) );
+ else
+ crPackZPixCR( width, height, format, type, ztype, zparm, length, pixels, &(clientState->unpack) );
+}
+
+void PACKSPU_APIENTRY packspu_GetTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels)
+{
+ GET_THREAD(thread);
+ ContextInfo *ctx = thread->currentContext;
+ CRClientState *clientState = &(ctx->clientState->client);
+ int writeback = 1;
+
+ /* XXX note: we're not observing the pixel pack parameters here unless PACK PBO is bound
+ * To do so, we'd have to allocate a temporary image buffer (how large???)
+ * and copy the image to the user's buffer using the pixel pack params.
+ */
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_ApplyPackState();
+ }
+
+ if (pack_spu.swap)
+ crPackGetTexImageSWAP( target, level, format, type, pixels, &(clientState->pack), &writeback );
+ else
+ crPackGetTexImage( target, level, format, type, pixels, &(clientState->pack), &writeback );
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_RestorePackState();
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+void PACKSPU_APIENTRY packspu_GetCompressedTexImageARB( GLenum target, GLint level, GLvoid * img )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_ApplyPackState();
+ }
+
+ if (pack_spu.swap)
+ {
+ crPackGetCompressedTexImageARBSWAP( target, level, img, &writeback );
+ }
+ else
+ {
+ crPackGetCompressedTexImageARB( target, level, img, &writeback );
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ packspu_RestorePackState();
+ }
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (!crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+#endif
+ {
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexImage1DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+ GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexImage1DARB(target, level, internalformat, width, border, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexImage2DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+ GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexImage2DARB(target, level, internalformat, width, height, border, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexImage3DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+ GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexImage3DARB(target, level, internalformat, width, height, depth, border, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexSubImage1DARB(GLenum target, GLint level, GLint xoffset, GLsizei width,
+ GLenum format, GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexSubImage1DARB(target, level, xoffset, width, format, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexSubImage2DARB(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexSubImage2DARB(target, level, xoffset, yoffset, width, height, format, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
+
+void PACKSPU_APIENTRY
+packspu_CompressedTexSubImage3DARB(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format,
+ GLsizei imagesize, const GLvoid *data)
+{
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_ApplyUnpackState();
+ }
+
+ crPackCompressedTexSubImage3DARB(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imagesize, data);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ packspu_RestoreUnpackState();
+ }
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_proto.py b/src/VBox/Additions/common/crOpenGL/pack/packspu_proto.py
new file mode 100755
index 00000000..5feed9a9
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_proto.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY packspu_proto.py SCRIPT */
+
+#ifndef PACKSPU_FUNCTIONS_H
+#define PACKSPU_FUNCTIONS_H 1
+
+#include <stdio.h>
+#include "cr_string.h"
+#include "cr_spu.h"
+#include "packspu.h"
+#include "cr_packfunctions.h"
+""")
+
+
+pack_specials = []
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+# make list of special functions
+for func_name in keys:
+ if ("get" in apiutil.Properties(func_name) or
+ apiutil.FindSpecial( "packspu", func_name ) or
+ apiutil.FindSpecial( "packspu_flush", func_name ) or
+ apiutil.FindSpecial( "packspu_vertex", func_name )):
+ pack_specials.append( func_name )
+
+for func_name in keys:
+ if apiutil.FindSpecial( "packspu_unimplemented", func_name ):
+ continue
+ if func_name in pack_specials:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('extern %s PACKSPU_APIENTRY packspu_%s(%s);' % ( return_type, func_name, apiutil.MakeDeclarationString(params) ))
+
+
+print("""
+#endif
+""")
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_special b/src/VBox/Additions/common/crOpenGL/pack/packspu_special
new file mode 100644
index 00000000..13c3e0a2
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_special
@@ -0,0 +1,138 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+AreTexturesResident
+AreProgramsResidentNV
+GetString
+GetTexImage
+EnableClientState
+DisableClientState
+Enable
+Disable
+ClientActiveTextureARB
+ColorPointer
+FogCoordPointerEXT
+SecondaryColorPointerEXT
+VertexAttribPointerARB
+VertexAttribPointerNV
+GetPointerv
+VertexPointer
+NormalPointer
+TexCoordPointer
+EdgeFlagPointer
+IndexPointer
+ArrayElement
+DrawArrays
+DrawElements
+DrawRangeElements
+InterleavedArrays
+ReadPixels
+DrawPixels
+CopyPixels
+Bitmap
+SwapBuffers
+Flush
+Finish
+PixelStorei
+PixelStoref
+PushClientAttrib
+PopClientAttrib
+CreateContext
+WindowCreate
+MakeCurrent
+DestroyContext
+Begin
+End
+TexImage1D
+TexSubImage1D
+TexImage2D
+TexSubImage2D
+TexImage3D
+TexImage3DEXT
+TexSubImage3D
+ChromiumParametervCR
+MultiDrawArraysEXT
+MultiDrawElementsEXT
+GetBufferPointervARB
+GetBufferParameterivARB
+MapBufferARB
+UnmapBufferARB
+BindBufferARB
+BufferDataARB
+ZPixCR
+ActiveTextureARB
+DrawBuffer
+Flush
+GetActiveAttrib
+GetActiveUniform
+GetAttachedShaders
+GetShaderInfoLog
+GetProgramInfoLog
+GetShaderSource
+GetAttachedObjectsARB
+GetInfoLogARB
+BufferSubDataARB
+EnableVertexAttribArrayARB
+DisableVertexAttribArrayARB
+GetBufferSubDataARB
+IsEnabled
+LockArraysEXT
+UnlockArraysEXT
+CreateProgram
+LinkProgram
+DeleteProgram
+GetUniformLocation
+GetAttribLocation
+GetUniformsLocations
+GetAttribsLocations
+BindFramebufferEXT
+DeleteObjectARB
+BindFramebufferEXT
+DeleteFramebuffersEXT
+FramebufferTexture1DEXT
+FramebufferTexture2DEXT
+FramebufferTexture3DEXT
+FramebufferRenderbufferEXT
+CheckFramebufferStatusEXT
+BindTexture
+DeleteTextures
+GetPolygonStipple
+GetPixelMapfv
+GetPixelMapuiv
+GetPixelMapusv
+GetCompressedTexImageARB
+BindRenderbufferEXT
+VBoxPackSetInjectThread
+VBoxPackGetInjectID
+VBoxPackSetInjectID
+VBoxAttachThread
+VBoxDetachThread
+VBoxCreateContext
+VBoxConChromiumParameteriCR
+VBoxConChromiumParametervCR
+VBoxWindowCreate
+VBoxWindowDestroy
+VBoxConCreate
+VBoxConDestroy
+VBoxConFlush
+VBoxPresentComposition
+ChromiumParameteriCR
+CompressedTexImage1DARB
+CompressedTexImage2DARB
+CompressedTexImage3DARB
+CompressedTexSubImage1DARB
+CompressedTexSubImage2DARB
+CompressedTexSubImage3DARB
+GenFramebuffersEXT
+GenRenderbuffersEXT
+DeleteFramebuffersEXT
+DeleteRenderbuffersEXT
+GenBuffersARB
+DeleteBuffersARB
+StringMarkerGREMEDY
+GenTextures
+CompileShader
+NewList
+EndList
+GetError
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_swapbuf.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_swapbuf.c
new file mode 100644
index 00000000..bc1895fe
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_swapbuf.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_packfunctions.h"
+#include "cr_error.h"
+#include "cr_net.h"
+#include "packspu.h"
+#include "packspu_proto.h"
+
+#if 0
+
+void PACKSPU_APIENTRY packspu_SwapBuffers( GLint window, GLint flags )
+{
+ GET_THREAD(thread);
+ if (pack_spu.swap)
+ {
+ crPackSwapBuffersSWAP( window, flags );
+ }
+ else
+ {
+ crPackSwapBuffers( window, flags );
+ }
+ packspuFlush( (void *) thread );
+}
+
+
+#else
+
+void PACKSPU_APIENTRY packspu_SwapBuffers( GLint window, GLint flags )
+{
+ GET_THREAD(thread);
+
+ if (pack_spu.swap)
+ {
+ crPackSwapBuffersSWAP( window, flags );
+ }
+ else
+ {
+ crPackSwapBuffers( window, flags );
+ }
+ packspuFlush( (void *) thread );
+
+ if (!(thread->netServer.conn->actual_network))
+ {
+ /* no synchronization needed */
+ return;
+ }
+
+ if (pack_spu.swapbuffer_sync) {
+ /* This won't block unless there has been more than 1 frame
+ * since we received a writeback acknowledgement. In the
+ * normal case there's no performance penalty for doing this
+ * (beyond the cost of packing the writeback request into the
+ * stream and receiving the reply), but it eliminates the
+ * problem of runaway rendering that can occur, eg when
+ * rendering frames consisting of a single large display list
+ * in a tight loop.
+ *
+ * Note that this is *not* the same as doing a sync after each
+ * swapbuffers, which would force a round-trip 'bubble' into
+ * the network stream under normal conditions.
+ *
+ * This is complicated because writeback in the pack spu is
+ * overridden to always set the value to zero when the
+ * reply is received, rather than decrementing it:
+ */
+ switch( thread->writeback ) {
+ case 0:
+ /* Request writeback.
+ */
+ thread->writeback = 1;
+ if (pack_spu.swap)
+ {
+ crPackWritebackSWAP( (GLint *) &thread->writeback );
+ }
+ else
+ {
+ crPackWriteback( (GLint *) &thread->writeback );
+ }
+ break;
+ case 1:
+ /* Make sure writeback from previous frame has been received.
+ */
+ CRPACKSPU_WRITEBACK_WAIT(thread, thread->writeback);
+ break;
+ }
+ }
+
+ /* want to emit a parameter here */
+ if (pack_spu.emit_GATHER_POST_SWAPBUFFERS)
+ {
+ if (pack_spu.swap)
+ crPackChromiumParameteriCRSWAP(GL_GATHER_POST_SWAPBUFFERS_CR, 1);
+ else
+ crPackChromiumParameteriCR(GL_GATHER_POST_SWAPBUFFERS_CR, 1);
+ }
+}
+
+#endif
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c
new file mode 100644
index 00000000..5057bcb3
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c
@@ -0,0 +1,70 @@
+/* $Id: packspu_texture.c $ */
+
+/** @file
+ * VBox OpenGL DRI driver functions
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "packspu.h"
+#include "cr_packfunctions.h"
+#include "cr_glstate.h"
+#include "packspu_proto.h"
+
+void PACKSPU_APIENTRY packspu_ActiveTextureARB(GLenum texture)
+{
+ crStateActiveTextureARB(texture);
+ crPackActiveTextureARB(texture);
+}
+
+void PACKSPU_APIENTRY packspu_BindTexture(GLenum target, GLuint texture)
+{
+ crStateBindTexture(target, texture);
+ crPackBindTexture(target, texture);
+}
+
+void PACKSPU_APIENTRY packspu_DeleteTextures(GLsizei n, const GLuint * textures)
+{
+ crStateDeleteTextures(n, textures);
+ crPackDeleteTextures(n, textures);
+}
+
+void PACKSPU_APIENTRY packspu_GenTextures( GLsizei n, GLuint * textures )
+{
+ GET_THREAD(thread);
+ int writeback = 1;
+ unsigned int i;
+ if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network))
+ {
+ crError( "packspu_GenTextures doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" );
+ }
+ if (pack_spu.swap)
+ {
+ crPackGenTexturesSWAP( n, textures, &writeback );
+ }
+ else
+ {
+ crPackGenTextures( n, textures, &writeback );
+ }
+ packspuFlush( (void *) thread );
+ CRPACKSPU_WRITEBACK_WAIT(thread, writeback);
+ if (pack_spu.swap)
+ {
+ for (i = 0 ; i < (unsigned int) n ; i++)
+ {
+ textures[i] = SWAP32(textures[i]);
+ }
+ }
+
+ crStateRegTextures(n, textures);
+}
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_unimplemented_special b/src/VBox/Additions/common/crOpenGL/pack/packspu_unimplemented_special
new file mode 100644
index 00000000..f71300c9
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_unimplemented_special
@@ -0,0 +1,4 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_vertex_special b/src/VBox/Additions/common/crOpenGL/pack/packspu_vertex_special
new file mode 100644
index 00000000..bbbc13d0
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_vertex_special
@@ -0,0 +1,28 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+Vertex2d
+Vertex2dv
+Vertex2f
+Vertex2fv
+Vertex2i
+Vertex2iv
+Vertex2s
+Vertex2sv
+Vertex3d
+Vertex3dv
+Vertex3f
+Vertex3fv
+Vertex3i
+Vertex3iv
+Vertex3s
+Vertex3sv
+Vertex4d
+Vertex4dv
+Vertex4f
+Vertex4fv
+Vertex4i
+Vertex4iv
+Vertex4s
+Vertex4sv
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/Makefile.kup b/src/VBox/Additions/common/crOpenGL/passthrough/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/Makefile.kup
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.def b/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.def
new file mode 100644
index 00000000..9edc7163
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.def
@@ -0,0 +1,6 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.py b/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.py
new file mode 100755
index 00000000..ec7e27c7
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/passthrough.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""#include <stdio.h>
+#include "cr_error.h"
+#include "cr_string.h"
+#include "cr_spu.h"
+#include "passthroughspu.h"
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+
+print('SPUNamedFunctionTable _cr_passthrough_table[%d];' % ( len(keys) + 1 ))
+
+print("""
+static void __fillin(int offset, char *name, SPUGenericFunction func)
+{
+ _cr_passthrough_table[offset].name = crStrdup(name);
+ _cr_passthrough_table[offset].fn = func;
+}
+
+void BuildPassthroughTable( SPU *child )
+{""")
+
+for index in range(len(keys)):
+ func_name = keys[index]
+ print('\t__fillin(%3d, "%s", (SPUGenericFunction) child->dispatch_table.%s);' % (index, func_name, func_name ))
+print('\t__fillin(%3d, NULL, NULL);' % len(keys))
+print('}')
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.h b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.h
new file mode 100644
index 00000000..a0e6b942
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_passthrough_passthroughspu_h
+#define GA_INCLUDED_SRC_common_crOpenGL_passthrough_passthroughspu_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+#include "cr_spu.h"
+
+
+extern SPUNamedFunctionTable _cr_passthrough_table[];
+extern void BuildPassthroughTable( SPU *child );
+
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_passthrough_passthroughspu_h */
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.rc b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.rc
new file mode 100644
index 00000000..ad2034e5
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu.rc
@@ -0,0 +1,69 @@
+/* $Id: passthroughspu.rc $ */
+/** @file
+ * VBoxOGLpassthroughspu - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#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_DRV
+ FILESUBTYPE VFT2_DRV_DISPLAY
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxOGLpassthroughspu\0"
+#ifdef VBOX_WDDM_WOW64
+ VALUE "OriginalFilename", "VBoxOGLpassthroughspu-x86.dll\0"
+#else
+ VALUE "OriginalFilename", "VBoxOGLpassthroughspu.dll\0"
+#endif
+ 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
+
+1 RCDATA
+BEGIN
+// Machine dependent parameters
+ 17, // Height of vertical thumb
+ 17, // Width of horizontal thumb
+ 2, // Icon horiz compression factor
+ 2, // Icon vert compression factor
+ 1, // Cursor horz compression factor
+ 1, // Cursor vert compression factor
+ 0, // Kanji window height
+ 1, // cxBorder (thickness of vertical lines)
+ 1 // cyBorder (thickness of horizontal lines)
+END
diff --git a/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu_init.c b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu_init.c
new file mode 100644
index 00000000..c08a17a4
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/passthrough/passthroughspu_init.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_error.h"
+#include "passthroughspu.h"
+
+static SPUFunctions passthrough_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_passthrough_table /* THE ACTUAL FUNCTIONS */
+};
+
+static SPUFunctions *
+passthroughSPUInit( int id, SPU *child, SPU *self,
+ unsigned int context_id,
+ unsigned int num_contexts )
+{
+ (void) id;
+ (void) self;
+ (void) context_id;
+ (void) num_contexts;
+
+ if (child == NULL)
+ {
+ crError( "You can't load the passthrough SPU as the last SPU in a chain!" );
+ }
+ BuildPassthroughTable( child );
+ return &passthrough_functions;
+}
+
+static void
+passthroughSPUSelfDispatch(SPUDispatchTable *parent)
+{
+ (void)parent;
+}
+
+static int
+passthroughSPUCleanup(void)
+{
+ return 1;
+}
+
+static SPUOptions passthroughSPUOptions[] = {
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+};
+
+
+int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
+ SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
+ SPUOptionsPtr *options, int *flags )
+{
+ *name = "passthrough";
+ *super = NULL;
+ *init = passthroughSPUInit;
+ *self = passthroughSPUSelfDispatch;
+ *cleanup = passthroughSPUCleanup;
+ *options = passthroughSPUOptions;
+ *flags = (SPU_NO_PACKER|SPU_NOT_TERMINAL|SPU_MAX_SERVERS_ZERO);
+
+ return 1;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/stub.c b/src/VBox/Additions/common/crOpenGL/stub.c
new file mode 100644
index 00000000..f5369c6e
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/stub.c
@@ -0,0 +1,548 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "stub.h"
+#include <iprt/thread.h>
+
+#ifdef GLX
+Display* stubGetWindowDisplay(WindowInfo *pWindow)
+{
+#if defined(CR_NEWWINTRACK)
+ if ((NIL_RTTHREAD!=stub.hSyncThread) && (RTThreadNativeSelf()==RTThreadGetNative(stub.hSyncThread)))
+ {
+ if (pWindow && pWindow->dpy && !pWindow->syncDpy)
+ {
+ crDebug("going to XOpenDisplay(%s)", pWindow->dpyName);
+ pWindow->syncDpy = XOpenDisplay(pWindow->dpyName);
+ if (!pWindow->syncDpy)
+ {
+ crWarning("Failed to open display %s", pWindow->dpyName);
+ }
+ return pWindow->syncDpy;
+ }
+ else
+ {
+ return pWindow ? pWindow->syncDpy:NULL;
+ }
+ }
+ else
+#endif
+ {
+ return pWindow ? pWindow->dpy:NULL;
+ }
+}
+#endif
+
+/**
+ * Returns -1 on error
+ */
+GLint APIENTRY crCreateContext(char *dpyName, GLint visBits)
+{
+ ContextInfo *context;
+ stubInit();
+ /* XXX in Chromium 1.5 and earlier, the last parameter was UNDECIDED.
+ * That didn't seem right so it was changed to CHROMIUM. (Brian)
+ */
+ context = stubNewContext(dpyName, visBits, CHROMIUM, 0
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , NULL
+#endif
+ );
+ return context ? (int) context->id : -1;
+}
+
+void APIENTRY crDestroyContext( GLint context )
+{
+ stubDestroyContext(context);
+}
+
+void APIENTRY crMakeCurrent( GLint window, GLint context )
+{
+ WindowInfo *winInfo = (WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ ContextInfo *contextInfo = (ContextInfo *)
+ crHashtableSearch(stub.contextTable, context);
+ if (contextInfo && contextInfo->type == NATIVE) {
+ crWarning("Can't call crMakeCurrent with native GL context");
+ return;
+ }
+
+ stubMakeCurrent(winInfo, contextInfo);
+}
+
+GLint APIENTRY crGetCurrentContext( void )
+{
+ ContextInfo *context;
+ stubInit();
+ context = stubGetCurrentContext();
+ if (context)
+ return (GLint) context->id;
+ else
+ return 0;
+}
+
+GLint APIENTRY crGetCurrentWindow( void )
+{
+ ContextInfo *context;
+ stubInit();
+ context = stubGetCurrentContext();
+ if (context && context->currentDrawable)
+ return context->currentDrawable->spuWindow;
+ else
+ return -1;
+}
+
+void APIENTRY crSwapBuffers( GLint window, GLint flags )
+{
+ WindowInfo *winInfo = (WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo)
+ stubSwapBuffers(winInfo, flags);
+}
+
+/**
+ * Returns -1 on error
+ */
+GLint APIENTRY crWindowCreate( const char *dpyName, GLint visBits )
+{
+ stubInit();
+ return stubNewWindow( dpyName, visBits );
+}
+
+void APIENTRY crWindowDestroy( GLint window )
+{
+ stubDestroyWindow( 0, window );
+}
+
+void APIENTRY crWindowSize( GLint window, GLint w, GLint h )
+{
+ const WindowInfo *winInfo = (const WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo && winInfo->type == CHROMIUM)
+ {
+ crDebug("Dispatched crWindowSize (%i)", window);
+ stub.spu->dispatch_table.WindowSize( window, w, h );
+ }
+}
+
+void APIENTRY crWindowPosition( GLint window, GLint x, GLint y )
+{
+ const WindowInfo *winInfo = (const WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo && winInfo->type == CHROMIUM)
+ {
+ crDebug("Dispatched crWindowPosition (%i)", window);
+ stub.spu->dispatch_table.WindowPosition( window, x, y );
+ }
+}
+
+void APIENTRY crWindowVisibleRegion( GLint window, GLint cRects, const void *pRects )
+{
+ const WindowInfo *winInfo = (const WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo && winInfo->type == CHROMIUM)
+ {
+ crDebug("Dispatched crWindowVisibleRegion (%i, cRects=%i)", window, cRects);
+ stub.spu->dispatch_table.WindowVisibleRegion( window, cRects, pRects );
+ }
+}
+
+void APIENTRY crVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects)
+{
+ RT_NOREF(texture, cfg, xPos, yPos, cRects, pRects);
+ crError("not expected!");
+}
+
+void APIENTRY crWindowShow( GLint window, GLint flag )
+{
+ WindowInfo *winInfo = (WindowInfo *)
+ crHashtableSearch(stub.windowTable, (unsigned int) window);
+ if (winInfo && winInfo->type == CHROMIUM)
+ stub.spu->dispatch_table.WindowShow( window, flag );
+ winInfo->mapped = flag ? GL_TRUE : GL_FALSE;
+}
+
+void APIENTRY stub_GetChromiumParametervCR( GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values )
+{
+ char **ret;
+ switch( target )
+ {
+ case GL_HEAD_SPU_NAME_CR:
+ ret = (char **) values;
+ *ret = stub.spu->name;
+ return;
+ default:
+ stub.spu->dispatch_table.GetChromiumParametervCR( target, index, type, count, values );
+ break;
+ }
+}
+
+/*
+ * Updates geometry info for given spu window.
+ * Returns GL_TRUE if it changed since last call, GL_FALSE otherwise.
+ * bForceUpdate - forces dispatching of geometry info even if it's unchanged
+ */
+GLboolean stubUpdateWindowGeometry(WindowInfo *pWindow, GLboolean bForceUpdate)
+{
+ int winX, winY;
+ unsigned int winW, winH;
+ GLboolean res = GL_FALSE;
+
+ CRASSERT(pWindow);
+
+ stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
+
+ /** @todo remove "if (winW && winH)"?*/
+ if (winW && winH) {
+ if (stub.trackWindowSize) {
+ if (bForceUpdate || winW != pWindow->width || winH != pWindow->height) {
+ crDebug("Dispatched WindowSize (%i)", pWindow->spuWindow);
+#ifdef VBOX_WITH_WDDM
+ if (!stub.bRunningUnderWDDM || pWindow->mapped)
+#endif
+ {
+ stub.spuDispatch.WindowSize(pWindow->spuWindow, winW, winH);
+ }
+ pWindow->width = winW;
+ pWindow->height = winH;
+ res = GL_TRUE;
+ }
+ }
+ if (stub.trackWindowPos) {
+ if (bForceUpdate || winX != pWindow->x || winY != pWindow->y) {
+ crDebug("Dispatched WindowPosition (%i)", pWindow->spuWindow);
+#ifdef VBOX_WITH_WDDM
+ if (!stub.bRunningUnderWDDM || pWindow->mapped)
+#endif
+ {
+ stub.spuDispatch.WindowPosition(pWindow->spuWindow, winX, winY);
+ }
+ pWindow->x = winX;
+ pWindow->y = winY;
+ res = GL_TRUE;
+ }
+ }
+ }
+
+ return res;
+}
+
+#ifdef WINDOWS
+/*
+ * Updates visible regions for given spu window.
+ * Returns GL_TRUE if regions changed since last call, GL_FALSE otherwise.
+ */
+GLboolean stubUpdateWindowVisibileRegions(WindowInfo *pWindow)
+{
+ HRGN hVisRgn;
+ HWND hwnd;
+ DWORD dwCount;
+ LPRGNDATA lpRgnData;
+ POINT pt;
+ int iret;
+
+ if (!pWindow) return GL_FALSE;
+ hwnd = pWindow->hWnd;
+ if (!hwnd) return GL_FALSE;
+
+ if (hwnd!=WindowFromDC(pWindow->drawable))
+ {
+ crWarning("Window(%i) DC is no longer valid", pWindow->spuWindow);
+ return GL_FALSE;
+ }
+
+ hVisRgn = CreateRectRgn(0,0,0,0);
+ iret = GetRandomRgn(pWindow->drawable, hVisRgn, SYSRGN);
+
+ if (iret==1)
+ {
+ /** @todo check win95/win98 here, as rects should be already in client space there*/
+ /* Convert screen related rectangles to client related rectangles */
+ pt.x = 0;
+ pt.y = 0;
+ ScreenToClient(hwnd, &pt);
+ OffsetRgn(hVisRgn, pt.x, pt.y);
+
+ /*
+ dwCount = GetRegionData(hVisRgn, 0, NULL);
+ lpRgnData = crAlloc(dwCount);
+ crDebug("GetRandomRgn returned 1, dwCount=%d", dwCount);
+ GetRegionData(hVisRgn, dwCount, lpRgnData);
+ crDebug("Region consists of %d rects", lpRgnData->rdh.nCount);
+
+ pRects = (RECT*) lpRgnData->Buffer;
+ for (i=0; i<lpRgnData->rdh.nCount; ++i)
+ {
+ crDebug("Rgn[%d] = (%d, %d, %d, %d)", i, pRects[i].left, pRects[i].top, pRects[i].right, pRects[i].bottom);
+ }
+ crFree(lpRgnData);
+ */
+
+ if (pWindow->hVisibleRegion==INVALID_HANDLE_VALUE
+ || !EqualRgn(pWindow->hVisibleRegion, hVisRgn))
+ {
+ DeleteObject(pWindow->hVisibleRegion);
+ pWindow->hVisibleRegion = hVisRgn;
+
+ dwCount = GetRegionData(hVisRgn, 0, NULL);
+ lpRgnData = crAlloc(dwCount);
+
+ if (lpRgnData)
+ {
+ GetRegionData(hVisRgn, dwCount, lpRgnData);
+ crDebug("Dispatched WindowVisibleRegion (%i, cRects=%i)", pWindow->spuWindow, lpRgnData->rdh.nCount);
+ stub.spuDispatch.WindowVisibleRegion(pWindow->spuWindow, lpRgnData->rdh.nCount, (GLint*) lpRgnData->Buffer);
+ crFree(lpRgnData);
+ return GL_TRUE;
+ }
+ else crWarning("GetRegionData failed, VisibleRegions update failed");
+ }
+ else
+ {
+ DeleteObject(hVisRgn);
+ }
+ }
+ else
+ {
+ crWarning("GetRandomRgn returned (%d) instead of (1), VisibleRegions update failed", iret);
+ DeleteObject(hVisRgn);
+ }
+
+ return GL_FALSE;
+}
+
+# ifndef CR_NEWWINTRACK
+static void stubCBCheckWindowsInfo(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *winInfo = (WindowInfo *) data1;
+ CWPRETSTRUCT *pMsgInfo = (PCWPRETSTRUCT) data2;
+
+ (void) key;
+
+ if (winInfo && pMsgInfo && winInfo->type == CHROMIUM)
+ {
+ switch (pMsgInfo->message)
+ {
+ case WM_MOVING:
+ case WM_SIZING:
+ case WM_MOVE:
+ case WM_CREATE:
+ case WM_SIZE:
+ {
+ GLboolean changed = stub.trackWindowVisibleRgn && stubUpdateWindowVisibileRegions(winInfo);
+
+ if (stubUpdateWindowGeometry(winInfo, GL_FALSE) || changed)
+ {
+ stubForcedFlush(0);
+ }
+ break;
+ }
+
+ case WM_SHOWWINDOW:
+ case WM_ACTIVATEAPP:
+ case WM_PAINT:
+ case WM_NCPAINT:
+ case WM_NCACTIVATE:
+ case WM_ERASEBKGND:
+ {
+ if (stub.trackWindowVisibleRgn && stubUpdateWindowVisibileRegions(winInfo))
+ {
+ stubForcedFlush(0);
+ }
+ break;
+ }
+
+ default:
+ {
+ if (stub.trackWindowVisibleRgn && stubUpdateWindowVisibileRegions(winInfo))
+ {
+ crDebug("Visibility info updated due to unknown hooked message (%d)", pMsgInfo->message);
+ stubForcedFlush(0);
+ }
+ break;
+ }
+ }
+ }
+}
+
+LRESULT CALLBACK stubCBWindowMessageHookProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ CWPRETSTRUCT *pMsgInfo = (PCWPRETSTRUCT) lParam;
+
+ if (nCode>=0 && pMsgInfo)
+ {
+ switch (pMsgInfo->message)
+ {
+ case WM_MOVING:
+ case WM_SIZING:
+ case WM_MOVE:
+ case WM_ACTIVATEAPP:
+ case WM_NCPAINT:
+ case WM_NCACTIVATE:
+ case WM_ERASEBKGND:
+ case WM_CREATE:
+ case WM_SIZE:
+ case WM_SHOWWINDOW:
+ {
+ crHashtableWalk(stub.windowTable, stubCBCheckWindowsInfo, (void *) lParam);
+ break;
+ }
+
+ /** @todo remove it*/
+ default:
+ {
+ /*crDebug("hook: unknown message (%d)", pMsgInfo->message);*/
+ crHashtableWalk(stub.windowTable, stubCBCheckWindowsInfo, (void *) lParam);
+ break;
+ }
+ }
+ }
+
+ return CallNextHookEx(stub.hMessageHook, nCode, wParam, lParam);
+}
+
+void stubInstallWindowMessageHook()
+{
+ stub.hMessageHook = SetWindowsHookEx(WH_CALLWNDPROCRET, stubCBWindowMessageHookProc, 0, crThreadID());
+
+ if (!stub.hMessageHook)
+ crWarning("Window message hook install failed! (not fatal)");
+}
+
+void stubUninstallWindowMessageHook()
+{
+ if (stub.hMessageHook)
+ UnhookWindowsHookEx(stub.hMessageHook);
+}
+# endif /*# ifndef CR_NEWWINTRACK*/
+
+#elif defined(GLX) //#ifdef WINDOWS
+void stubCheckXExtensions(WindowInfo *pWindow)
+{
+ int evb, erb, vmi=0, vma=0;
+ Display *dpy = stubGetWindowDisplay(pWindow);
+
+ stub.bXExtensionsChecked = GL_TRUE;
+ stub.trackWindowVisibleRgn = 0;
+
+ XLOCK(dpy);
+ if (XCompositeQueryExtension(dpy, &evb, &erb)
+ && XCompositeQueryVersion(dpy, &vma, &vmi)
+ && (vma>0 || vmi>=4))
+ {
+ stub.bHaveXComposite = GL_TRUE;
+ crDebug("XComposite %i.%i", vma, vmi);
+ vma=0;
+ vmi=0;
+ if (XFixesQueryExtension(dpy, &evb, &erb)
+ && XFixesQueryVersion(dpy, &vma, &vmi)
+ && vma>=2)
+ {
+ crDebug("XFixes %i.%i", vma, vmi);
+ stub.bHaveXFixes = GL_TRUE;
+ stub.trackWindowVisibleRgn = 1;
+ XUNLOCK(dpy);
+ return;
+ }
+ else
+ {
+ crWarning("XFixes not found or old version (%i.%i), no VisibilityTracking", vma, vmi);
+ }
+ }
+ else
+ {
+ crWarning("XComposite not found or old version (%i.%i), no VisibilityTracking", vma, vmi);
+ }
+ XUNLOCK(dpy);
+ return;
+}
+
+/*
+ * Updates visible regions for given spu window.
+ * Returns GL_TRUE if regions changed since last call, GL_FALSE otherwise.
+ */
+GLboolean stubUpdateWindowVisibileRegions(WindowInfo *pWindow)
+{
+ XserverRegion xreg;
+ int cRects, i;
+ XRectangle *pXRects;
+ GLint* pGLRects;
+ Display *dpy;
+ bool bNoUpdate = false;
+
+ if (!stub.bXExtensionsChecked)
+ {
+ stubCheckXExtensions(pWindow);
+ if (!stub.trackWindowVisibleRgn)
+ {
+ return GL_FALSE;
+ }
+ }
+
+ dpy = stubGetWindowDisplay(pWindow);
+
+ /** @todo see comment regarding size/position updates and XSync, same applies to those functions but
+ * it seems there's no way to get even based updates for this. Or I've failed to find the appropriate extension.
+ */
+ XLOCK(dpy);
+ xreg = XCompositeCreateRegionFromBorderClip(dpy, pWindow->drawable);
+ pXRects = XFixesFetchRegion(dpy, xreg, &cRects);
+ XFixesDestroyRegion(dpy, xreg);
+ XUNLOCK(dpy);
+
+ /* Check for compiz main window */
+ if (!pWindow->pVisibleRegions && !cRects)
+ {
+ bNoUpdate = true;
+ }
+
+ if (!bNoUpdate
+ && (!pWindow->pVisibleRegions
+ || pWindow->cVisibleRegions!=cRects
+ || (pWindow->pVisibleRegions && crMemcmp(pWindow->pVisibleRegions, pXRects, cRects * sizeof(XRectangle)))))
+ {
+ if (pWindow->pVisibleRegions)
+ {
+ XFree(pWindow->pVisibleRegions);
+ }
+
+ pWindow->pVisibleRegions = pXRects;
+ pWindow->cVisibleRegions = cRects;
+
+ pGLRects = crAlloc(cRects ? 4*cRects*sizeof(GLint) : 4*sizeof(GLint));
+ if (!pGLRects)
+ {
+ crWarning("stubUpdateWindowVisibileRegions: failed to allocate %lu bytes",
+ (unsigned long)(4*cRects*sizeof(GLint)));
+ return GL_FALSE;
+ }
+
+ //crDebug("Got %i rects.", cRects);
+ for (i=0; i<cRects; ++i)
+ {
+ pGLRects[4*i+0] = pXRects[i].x;
+ pGLRects[4*i+1] = pXRects[i].y;
+ pGLRects[4*i+2] = pXRects[i].x+pXRects[i].width;
+ pGLRects[4*i+3] = pXRects[i].y+pXRects[i].height;
+ //crDebug("Rect[%i]=(%i,%i,%i,%i)", i, pGLRects[4*i+0], pGLRects[4*i+1], pGLRects[4*i+2], pGLRects[4*i+3]);
+ }
+
+ crDebug("Dispatched WindowVisibleRegion (%i, cRects=%i)", pWindow->spuWindow, cRects);
+ stub.spuDispatch.WindowVisibleRegion(pWindow->spuWindow, cRects, pGLRects);
+ crFree(pGLRects);
+ return GL_TRUE;
+ }
+ else
+ {
+ XFree(pXRects);
+ }
+
+ return GL_FALSE;
+}
+#endif //#ifdef WINDOWS
diff --git a/src/VBox/Additions/common/crOpenGL/stub.h b/src/VBox/Additions/common/crOpenGL/stub.h
new file mode 100644
index 00000000..1dd5faa2
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/stub.h
@@ -0,0 +1,370 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+
+/*
+ * How this all works...
+ *
+ * This directory implements three different interfaces to Chromium:
+ *
+ * 1. the Chromium interface - this is defined by the functions that start
+ * with the "cr" prefix and are defined in chromium.h and implemented in
+ * stub.c. Typically, this is used by parallel apps (like psubmit).
+ *
+ * 2. GLX emulation interface - the glX*() functions are emulated here.
+ * When glXCreateContext() is called we may either create a real, native
+ * GLX context or a Chromium context (depending on match_window_title and
+ * minimum_window_size).
+ *
+ * 3. WGL emulation interface - the wgl*() functions are emulated here.
+ * When wglCreateContext() is called we may either create a real, native
+ * WGL context or a Chromium context (depending on match_window_title and
+ * minimum_window_size).
+ *
+ *
+ */
+
+#ifndef GA_INCLUDED_SRC_common_crOpenGL_stub_h
+#define GA_INCLUDED_SRC_common_crOpenGL_stub_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "chromium.h"
+#include "cr_version.h"
+#include "cr_hash.h"
+#include "cr_process.h"
+#include "cr_spu.h"
+#include "cr_threads.h"
+#include "spu_dispatch_table.h"
+
+#ifdef GLX
+#include <X11/extensions/XShm.h>
+#include <sys/shm.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#if defined(WINDOWS) || defined(Linux) || defined(SunOS)
+# define CR_NEWWINTRACK
+#endif
+
+#if !defined(CHROMIUM_THREADSAFE) && defined(CR_NEWWINTRACK)
+# error CHROMIUM_THREADSAFE have to be defined
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+# include <cr_threads.h>
+#endif
+
+#if 0 && defined(CR_NEWWINTRACK) && !defined(WINDOWS)
+#define XLOCK(dpy) XLockDisplay(dpy)
+#define XUNLOCK(dpy) XUnlockDisplay(dpy)
+#else
+#define XLOCK(dpy)
+#define XUNLOCK(dpy)
+#endif
+
+/* When we first create a rendering context we can't be sure whether
+ * it'll be handled by Chromium or as a native GLX/WGL context. So in
+ * CreateContext() we'll mark the ContextInfo object as UNDECIDED then
+ * switch it to either NATIVE or CHROMIUM the first time MakeCurrent()
+ * is called. In MakeCurrent() we can use a criteria like window size
+ * or window title to decide between CHROMIUM and NATIVE.
+ */
+typedef enum
+{
+ UNDECIDED,
+ CHROMIUM,
+ NATIVE
+} ContextType;
+
+#define MAX_DPY_NAME 1000
+
+typedef struct context_info_t ContextInfo;
+typedef struct window_info_t WindowInfo;
+
+#ifdef GLX
+typedef struct glxpixmap_info_t GLX_Pixmap_t;
+
+struct glxpixmap_info_t
+{
+ int x, y;
+ unsigned int w, h, border, depth;
+ GLenum format;
+ Window root;
+ GLenum target;
+ GC gc;
+ Pixmap hShmPixmap; /* Shared memory pixmap object, if it's supported*/
+ Damage hDamage; /* damage xserver handle*/
+ Bool bPixmapImageDirty;
+ Region pDamageRegion;
+};
+#endif
+
+struct context_info_t
+{
+ char dpyName[MAX_DPY_NAME];
+ GLint spuContext; /* returned by head SPU's CreateContext() */
+ ContextType type; /* CHROMIUM, NATIVE or UNDECIDED */
+ unsigned long id; /* the client-visible handle */
+ GLint visBits;
+ WindowInfo *currentDrawable;
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ GLint spuConnection;
+ struct VBOXUHGSMI *pHgsmi;
+#endif
+
+#ifdef CHROMIUM_THREADSAFE
+ VBOXTLSREFDATA
+#endif
+
+#ifdef WINDOWS
+ HGLRC hglrc;
+#elif defined(DARWIN)
+ ContextInfo *share;
+ CGLContextObj cglc;
+
+ /* CGLContextEnable (CGLEnable, CGLDisable, and CGLIsEnabled) */
+ unsigned int options;
+
+ /* CGLContextParameter (CGLSetParameter and CGLGetParameter) */
+ GLint parambits;
+ long swap_rect[4], swap_interval;
+ unsigned long client_storage;
+ long surf_order, surf_opacy;
+
+ long disp_mask;
+#elif defined(GLX)
+ Display *dpy;
+ ContextInfo *share;
+ Bool direct;
+ GLXContext glxContext;
+ CRHashTable *pGLXPixmapsHash;
+ Bool damageQueryFailed;
+ int damageEventsBase;
+#endif
+};
+
+#ifdef DARWIN
+enum {
+ VISBIT_SWAP_RECT,
+ VISBIT_SWAP_INTERVAL,
+ VISBIT_CLIENT_STORAGE
+};
+#endif
+
+struct window_info_t
+{
+ char dpyName[MAX_DPY_NAME];
+ int x, y;
+ unsigned int width, height;
+ ContextType type;
+ GLint spuWindow; /* returned by head SPU's WindowCreate() */
+ ContextInfo *pOwner; /* ctx which created this window */
+ GLboolean mapped;
+#ifdef WINDOWS
+ HDC drawable;
+ HRGN hVisibleRegion;
+ DWORD dmPelsWidth;
+ DWORD dmPelsHeight;
+ HWND hWnd;
+#elif defined(DARWIN)
+ CGSConnectionID connection;
+ CGSWindowID drawable;
+ CGSSurfaceID surface;
+#elif defined(GLX)
+ Display *dpy;
+# ifdef CR_NEWWINTRACK
+ Display *syncDpy;
+# endif
+ GLXDrawable drawable;
+ XRectangle *pVisibleRegions;
+ GLint cVisibleRegions;
+#endif
+#ifdef CR_NEWWINTRACK
+ uint32_t u32ClientID;
+#endif
+};
+
+/* "Global" variables for the stub library */
+typedef struct {
+ /* the first SPU in the SPU chain on this app node */
+ SPU *spu;
+
+ /* OpenGL/SPU dispatch tables */
+ crOpenGLInterface wsInterface;
+ SPUDispatchTable spuDispatch;
+ SPUDispatchTable nativeDispatch;
+ GLboolean haveNativeOpenGL;
+
+ /* config options */
+ int appDrawCursor;
+ GLuint minChromiumWindowWidth;
+ GLuint minChromiumWindowHeight;
+ GLuint maxChromiumWindowWidth;
+ GLuint maxChromiumWindowHeight;
+ GLuint matchChromiumWindowCount;
+ GLuint matchChromiumWindowCounter;
+ GLuint *matchChromiumWindowID;
+ GLuint numIgnoreWindowID;
+ char *matchWindowTitle;
+ int ignoreFreeglutMenus;
+ int trackWindowSize;
+ int trackWindowPos;
+ int trackWindowVisibility;
+ int trackWindowVisibleRgn;
+ char *spu_dir;
+ int force_pbuffers;
+
+ /* thread safety stuff */
+ GLboolean threadSafe;
+#ifdef CHROMIUM_THREADSAFE
+ CRtsd dispatchTSD;
+ CRmutex mutex;
+#endif
+
+ CRpid mothershipPID;
+
+ /* contexts */
+ int freeContextNumber;
+ CRHashTable *contextTable;
+#ifndef CHROMIUM_THREADSAFE
+ ContextInfo *currentContext; /* may be NULL */
+#endif
+
+ /* windows */
+ CRHashTable *windowTable;
+
+#ifdef GLX
+ /* Shared memory, used to transfer XServer pixmaps data into client memory */
+ XShmSegmentInfo xshmSI;
+ GLboolean bShmInitFailed;
+
+ CRHashTable *pGLXPixmapsHash;
+
+ GLboolean bXExtensionsChecked;
+ GLboolean bHaveXComposite;
+ GLboolean bHaveXFixes;
+#endif
+
+#ifdef WINDOWS
+# ifndef CR_NEWWINTRACK
+ HHOOK hMessageHook;
+# endif
+# ifdef VBOX_WITH_WDDM
+ bool bRunningUnderWDDM;
+# endif
+#endif
+
+#ifdef CR_NEWWINTRACK
+ RTTHREAD hSyncThread;
+ bool volatile bShutdownSyncThread;
+#endif
+
+} Stub;
+
+#ifdef CHROMIUM_THREADSAFE
+/* we place the g_stubCurrentContextTLS outside the Stub data because Stub data is inited by the client's call,
+ * while we need g_stubCurrentContextTLS the g_stubCurrentContextTLS to be valid at any time to be able to handle
+ * THREAD_DETACH cleanup on windows.
+ * Note that we can not do
+ * STUB_INIT_LOCK();
+ * if (stub_initialized) stubSetCurrentContext(NULL);
+ * STUB_INIT_UNLOCK();
+ * on THREAD_DETACH since it may cause deadlock, i.e. in this situation loader lock is acquired first and then the init lock,
+ * but since we use GetModuleFileName in crGetProcName called from stubInitLocked, the lock order might be the oposite.
+ * Note that GetModuleFileName acquires the loader lock.
+ * */
+extern CRtsd g_stubCurrentContextTSD;
+
+DECLINLINE(ContextInfo*) stubGetCurrentContext(void)
+{
+ ContextInfo* ctx;
+ VBoxTlsRefGetCurrentFunctional(ctx, ContextInfo, &g_stubCurrentContextTSD);
+ return ctx;
+}
+# define stubSetCurrentContext(_ctx) VBoxTlsRefSetCurrent(ContextInfo, &g_stubCurrentContextTSD, _ctx)
+#else
+# define stubGetCurrentContext() (stub.currentContext)
+# define stubSetCurrentContext(_ctx) do { stub.currentContext = (_ctx); } while (0)
+#endif
+
+extern Stub stub;
+
+extern DECLEXPORT(SPUDispatchTable) glim;
+extern SPUDispatchTable stubThreadsafeDispatch;
+extern DECLEXPORT(SPUDispatchTable) stubNULLDispatch;
+
+#if defined(GLX) || defined (WINDOWS)
+extern GLboolean stubUpdateWindowVisibileRegions(WindowInfo *pWindow);
+#endif
+
+#ifdef WINDOWS
+
+/* WGL versions */
+extern WindowInfo *stubGetWindowInfo( HDC drawable );
+
+extern void stubInstallWindowMessageHook();
+extern void stubUninstallWindowMessageHook();
+
+#elif defined(DARWIN)
+
+extern CGSConnectionID _CGSDefaultConnection(void);
+extern OSStatus CGSGetWindowLevel( CGSConnectionID cid, CGSWindowID wid, CGWindowLevel *level );
+extern OSStatus CGSSetWindowAlpha( const CGSConnectionID cid, CGSWindowID wid, float alpha );
+
+/* These don't seem to be included in the OSX glext.h ... */
+extern void glPointParameteri( GLenum pname, GLint param );
+extern void glPointParameteriv( GLenum pname, const GLint * param );
+
+extern WindowInfo *stubGetWindowInfo( CGSWindowID drawable );
+
+#elif defined(GLX)
+
+/* GLX versions */
+extern WindowInfo *stubGetWindowInfo( Display *dpy, GLXDrawable drawable );
+extern void stubUseXFont( Display *dpy, Font font, int first, int count, int listbase );
+extern Display* stubGetWindowDisplay(WindowInfo *pWindow);
+
+extern void stubCheckXExtensions(WindowInfo *pWindow);
+#endif
+
+
+extern ContextInfo *stubNewContext(char *dpyName, GLint visBits, ContextType type, unsigned long shareCtx
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , struct VBOXUHGSMI *pHgsmi
+#endif
+ );
+extern void stubConChromiumParameteriCR(GLint con, GLenum param, GLint value);
+extern void stubConChromiumParametervCR(GLint con, GLenum target, GLenum type, GLsizei count, const GLvoid *values);
+extern GLboolean stubCtxCreate(ContextInfo *context);
+extern GLboolean stubCtxCheckCreate(ContextInfo *context);
+extern void stubDestroyContext( unsigned long contextId );
+extern GLboolean stubMakeCurrent( WindowInfo *window, ContextInfo *context );
+extern GLint stubNewWindow( const char *dpyName, GLint visBits );
+extern void stubDestroyWindow( GLint con, GLint window );
+extern void stubSwapBuffers(WindowInfo *window, GLint flags);
+extern void stubGetWindowGeometry(WindowInfo *win, int *x, int *y, unsigned int *w, unsigned int *h);
+extern GLboolean stubUpdateWindowGeometry(WindowInfo *pWindow, GLboolean bForceUpdate);
+extern GLboolean stubIsWindowVisible(WindowInfo *win);
+extern bool stubInit(void);
+
+extern void stubForcedFlush(GLint con);
+extern void stubConFlush(GLint con);
+extern void APIENTRY stub_GetChromiumParametervCR( GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values );
+
+extern void APIENTRY glBoundsInfoCR(const CRrecti *, const GLbyte *, GLint, GLint);
+
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+# define CR_CTX_CON(_pCtx) ((_pCtx)->spuConnection)
+#else
+# define CR_CTX_CON(_pCtx) (0)
+#endif
+
+#endif /* !GA_INCLUDED_SRC_common_crOpenGL_stub_h */
diff --git a/src/VBox/Additions/common/crOpenGL/stub_common.py b/src/VBox/Additions/common/crOpenGL/stub_common.py
new file mode 100755
index 00000000..05127343
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/stub_common.py
@@ -0,0 +1,282 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+curver = sys.version_info[0] + sys.version_info[1]/10.0
+if curver < 2.2:
+ print("Your python is version %g. Chromium requires at least"%(curver), file=sys.stderr)
+ print("version 2.2. Please upgrade your python installation.", file=sys.stderr)
+ sys.exit(1)
+
+import string;
+import re;
+
+def CopyrightC( ):
+ print("""/* Copyright (c) 2001, Stanford University
+ All rights reserved.
+
+ See the file LICENSE.txt for information on redistributing this software. */
+ """)
+
+def CopyrightDef( ):
+ print("""; Copyright (c) 2001, Stanford University
+ ; All rights reserved.
+ ;
+ ; See the file LICENSE.txt for information on redistributing this software.
+ """)
+
+def DecoderName( glName ):
+ return "crUnpack" + glName
+
+def OpcodeName( glName ):
+ # This is a bit of a hack. We want to implement the glVertexAttrib*NV
+ # functions in terms of the glVertexAttrib*ARB opcodes.
+ m = re.search( "VertexAttrib([1234](ub|b|us|s|ui|i|f|d|)v?)NV", glName )
+ if m:
+ dataType = m.group(1)
+ if dataType == "4ub":
+ dataType = "4Nub"
+ elif dataType == "4ubv":
+ dataType = "4Nubv"
+ glName = "VertexAttrib" + dataType + "ARB"
+ return "CR_" + string.upper( glName ) + "_OPCODE"
+
+def ExtendedOpcodeName( glName ):
+ return "CR_" + string.upper( glName ) + "_EXTEND_OPCODE"
+
+def PackFunction( glName ):
+ return "crPack" + glName
+
+def DoPackFunctionMapping( glName ):
+ return "__glpack_" + glName
+
+def DoStateFunctionMapping( glName ):
+ return "__glstate_" + glName
+
+def DoImmediateMapping( glName ):
+ return "__glim_" + glName
+
+
+
+# Annotations are a generalization of Specials (below).
+# Each line of an annotation file is a set of words.
+# The first word is a key; the remainder are all annotations
+# for that key. This is a useful model for grouping keys
+# (like GL function names) into overlapping subsets, all in
+# a single file.
+annotations = {}
+def LoadAnnotations(filename):
+ table = {}
+ try:
+ f = open(filename, "r")
+ except:
+ annotations[filename] = {}
+ return {}
+
+ for line in f.readlines():
+ line = line.strip()
+ if line == "" or line[0] == '#':
+ continue
+ subtable = {}
+ words = line.split()
+ for word in words[1:]:
+ subtable[word] = 1
+ table[words[0]] = subtable
+
+ annotations[filename] = table
+ return table
+
+def GetAnnotations( filename, key ):
+ table = {}
+ try:
+ table = annotations[filename]
+ except KeyError:
+ table = LoadAnnotations(filename)
+
+ try:
+ subtable = table[key]
+ except KeyError:
+ return []
+
+ return sorted(subtable.keys())
+
+def FindAnnotation( filename, key, subkey ):
+ table = {}
+ try:
+ table = annotations[filename]
+ except KeyError:
+ table = LoadAnnotations(filename)
+
+ try:
+ subtable = table[key]
+ except KeyError:
+ return 0
+
+ try:
+ return subtable[subkey]
+ except KeyError:
+ return 0
+
+
+
+specials = {}
+
+def LoadSpecials( filename ):
+ table = {}
+ try:
+ f = open( filename, "r" )
+ except:
+ specials[filename] = {}
+ return {}
+
+ for line in f.readlines():
+ line = string.strip(line)
+ if line == "" or line[0] == '#':
+ continue
+ table[line] = 1
+
+ specials[filename] = table
+ return table
+
+def FindSpecial( table_file, glName ):
+ table = {}
+ filename = table_file + "_special"
+ try:
+ table = specials[filename]
+ except KeyError:
+ table = LoadSpecials( filename )
+
+ try:
+ if (table[glName] == 1):
+ return 1
+ else:
+ return 0 #should never happen
+ except KeyError:
+ return 0
+
+def AllSpecials( table_file ):
+ table = {}
+ filename = table_file + "_special"
+ try:
+ table = specials[filename]
+ except KeyError:
+ table = LoadSpecials( filename )
+
+ return sorted(table.keys())
+
+def AllSpecials( table_file ):
+ filename = table_file + "_special"
+
+ table = {}
+ try:
+ table = specials[filename]
+ except KeyError:
+ table = LoadSpecials(filename)
+
+ return sorted(table.keys())
+
+def NumSpecials( table_file ):
+ filename = table_file + "_special"
+
+ table = {}
+ try:
+ table = specials[filename]
+ except KeyError:
+ table = LoadSpecials(filename)
+
+ return len(table.keys())
+
+lengths = {}
+lengths['GLbyte'] = 1
+lengths['GLubyte'] = 1
+lengths['GLshort'] = 2
+lengths['GLushort'] = 2
+lengths['GLint'] = 4
+lengths['GLuint'] = 4
+lengths['GLfloat'] = 4
+lengths['GLclampf'] = 4
+lengths['GLdouble'] = 8
+lengths['GLclampd'] = 8
+
+lengths['GLenum'] = 4
+lengths['GLboolean'] = 1
+lengths['GLsizei'] = 4
+lengths['GLbitfield'] = 4
+
+lengths['void'] = 0
+lengths['int'] = 4
+
+align_types = 1
+
+def FixAlignment( pos, alignment ):
+ # if we want double-alignment take word-alignment instead,
+ # yes, this is super-lame, but we know what we are doing
+ if alignment > 4:
+ alignment = 4
+ if align_types and alignment and ( pos % alignment ):
+ pos += alignment - ( pos % alignment )
+ return pos
+
+def WordAlign( pos ):
+ return FixAlignment( pos, 4 )
+
+def PointerSize():
+ return 8 # Leave room for a 64 bit pointer
+
+def PacketLength( arg_types ):
+ len = 0
+ for arg in arg_types:
+ if string.find( arg, '*') != -1:
+ size = PointerSize()
+ else:
+ temp_arg = re.sub("const ", "", arg)
+ size = lengths[temp_arg]
+ len = FixAlignment( len, size ) + size
+ len = WordAlign( len )
+ return len
+
+def InternalArgumentString( arg_names, arg_types ):
+ """Return string of C-style function declaration arguments."""
+ output = ''
+ for index in range(0,len(arg_names)):
+ if len(arg_names) != 1 and arg_names[index] == '':
+ continue
+ output += arg_types[index]
+ if arg_types[index][-1:] != '*' and arg_names[index] != '':
+ output += " ";
+ output += arg_names[index]
+ if index != len(arg_names) - 1:
+ output += ", "
+ return output
+
+def ArgumentString( arg_names, arg_types ):
+ """Return InternalArgumentString inside parenthesis."""
+ output = '( ' + InternalArgumentString(arg_names, arg_types) + ' )'
+ return output
+
+def InternalCallString( arg_names ):
+ output = ''
+ for index in range(0,len(arg_names)):
+ output += arg_names[index]
+ if arg_names[index] != '' and index != len(arg_names) - 1:
+ output += ", "
+ return output
+
+def CallString( arg_names ):
+ output = '( '
+ output += InternalCallString(arg_names)
+ output += " )"
+ return output
+
+def IsVector ( func_name ) :
+ m = re.search( r"^(SecondaryColor|Color|EdgeFlag|EvalCoord|Index|Normal|TexCoord|MultiTexCoord|Vertex|RasterPos|VertexAttrib|FogCoord|WindowPos|ProgramParameter)([1234]?)N?(ub|b|us|s|ui|i|f|d|)v(ARB|EXT|NV)?$", func_name )
+ if m :
+ if m.group(2) :
+ return string.atoi( m.group(2) )
+ else:
+ return 1
+ else:
+ return 0
diff --git a/src/VBox/Additions/common/crOpenGL/tsfuncs.py b/src/VBox/Additions/common/crOpenGL/tsfuncs.py
new file mode 100755
index 00000000..020b5cc7
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/tsfuncs.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE tsfuncs.py SCRIPT */
+
+#include "stub.h"
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ print("static %s SPULOAD_APIENTRY ts_%s(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params) ))
+ print("{")
+ print("\tSPUDispatchTable *tab = (SPUDispatchTable *) crGetTSD(&stub.dispatchTSD);")
+
+ if return_type != "void":
+ print("\treturn ", end=" ")
+
+ print("\ttab->%s(%s);" % (func_name, apiutil.MakeCallString(params)))
+ print("}")
+ print("")
+
+
+print("SPUDispatchTable stubThreadsafeDispatch = {")
+
+for func_name in keys:
+ print("\tts_%s," % func_name)
+
+print("\tNULL, /* copyList */")
+print("\tNULL, /* copy_of */")
+print("\t0, /* mark */")
+print("\tNULL /* server */")
+print("};")
diff --git a/src/VBox/Additions/common/crOpenGL/utils.c b/src/VBox/Additions/common/crOpenGL/utils.c
new file mode 100644
index 00000000..a220d09d
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/utils.c
@@ -0,0 +1,876 @@
+/*
+ * (C) Copyright IBM Corporation 2002, 2004
+ * All Rights Reserved.
+ *
+ * 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
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, 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 (including the next
+ * paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS 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.
+ */
+
+/**
+ * \file utils.c
+ * Utility functions for DRI drivers.
+ *
+ * \author Ian Romanick <idr@us.ibm.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "mtypes.h"
+#include "extensions.h"
+#include "utils.h"
+#include "dispatch.h"
+
+int driDispatchRemapTable[ driDispatchRemapTable_size ];
+
+#if defined(USE_X86_ASM)
+#include "x86/common_x86_asm.h"
+#endif
+
+#if defined(USE_PPC_ASM)
+#include "ppc/common_ppc_features.h"
+#endif
+
+unsigned
+driParseDebugString( const char * debug,
+ const struct dri_debug_control * control )
+{
+ unsigned flag;
+
+
+ flag = 0;
+ if ( debug != NULL ) {
+ while( control->string != NULL ) {
+ if ( !strcmp( debug, "all" ) ||
+ strstr( debug, control->string ) != NULL ) {
+ flag |= control->flag;
+ }
+
+ control++;
+ }
+ }
+
+ return flag;
+}
+
+
+
+/**
+ * Create the \c GL_RENDERER string for DRI drivers.
+ *
+ * Almost all DRI drivers use a \c GL_RENDERER string of the form:
+ *
+ * "Mesa DRI <chip> <driver date> <AGP speed) <CPU information>"
+ *
+ * Using the supplied chip name, driver data, and AGP speed, this function
+ * creates the string.
+ *
+ * \param buffer Buffer to hold the \c GL_RENDERER string.
+ * \param hardware_name Name of the hardware.
+ * \param driver_date Driver date.
+ * \param agp_mode AGP mode (speed).
+ *
+ * \returns
+ * The length of the string stored in \c buffer. This does \b not include
+ * the terminating \c NUL character.
+ */
+unsigned
+driGetRendererString( char * buffer, const char * hardware_name,
+ const char * driver_date, GLuint agp_mode )
+{
+#define MAX_INFO 4
+ const char * cpu[MAX_INFO];
+ unsigned next = 0;
+ unsigned i;
+ unsigned offset;
+
+
+ offset = sprintf( buffer, "Mesa DRI %s %s", hardware_name, driver_date );
+
+ /* Append any AGP-specific information.
+ */
+ switch ( agp_mode ) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ offset += sprintf( & buffer[ offset ], " AGP %ux", agp_mode );
+ break;
+
+ default:
+ break;
+ }
+
+ /* Append any CPU-specific information.
+ */
+#ifdef USE_X86_ASM
+ if ( _mesa_x86_cpu_features ) {
+ cpu[next] = " x86";
+ next++;
+ }
+# ifdef USE_MMX_ASM
+ if ( cpu_has_mmx ) {
+ cpu[next] = (cpu_has_mmxext) ? "/MMX+" : "/MMX";
+ next++;
+ }
+# endif
+# ifdef USE_3DNOW_ASM
+ if ( cpu_has_3dnow ) {
+ cpu[next] = (cpu_has_3dnowext) ? "/3DNow!+" : "/3DNow!";
+ next++;
+ }
+# endif
+# ifdef USE_SSE_ASM
+ if ( cpu_has_xmm ) {
+ cpu[next] = (cpu_has_xmm2) ? "/SSE2" : "/SSE";
+ next++;
+ }
+# endif
+
+#elif defined(USE_SPARC_ASM)
+
+ cpu[0] = " SPARC";
+ next = 1;
+
+#elif defined(USE_PPC_ASM)
+ if ( _mesa_ppc_cpu_features ) {
+ cpu[next] = (cpu_has_64) ? " PowerPC 64" : " PowerPC";
+ next++;
+ }
+
+# ifdef USE_VMX_ASM
+ if ( cpu_has_vmx ) {
+ cpu[next] = "/Altivec";
+ next++;
+ }
+# endif
+
+ if ( ! cpu_has_fpu ) {
+ cpu[next] = "/No FPU";
+ next++;
+ }
+#endif
+
+ for ( i = 0 ; i < next ; i++ ) {
+ const size_t len = strlen( cpu[i] );
+
+ strncpy( & buffer[ offset ], cpu[i], len );
+ offset += len;
+ }
+
+ return offset;
+}
+
+
+
+
+#define need_GL_ARB_multisample
+#define need_GL_ARB_transpose_matrix
+#define need_GL_ARB_window_pos
+#define need_GL_EXT_compiled_vertex_array
+#define need_GL_EXT_polygon_offset
+#define need_GL_EXT_texture_object
+#define need_GL_EXT_vertex_array
+#define need_GL_MESA_window_pos
+
+/* These are needed in *all* drivers because Mesa internally implements
+ * certain functionality in terms of functions provided by these extensions.
+ * For example, glBlendFunc is implemented by calling glBlendFuncSeparateEXT.
+ */
+#define need_GL_EXT_blend_func_separate
+#define need_GL_NV_vertex_program
+
+#include "extension_helper.h"
+
+static const struct dri_extension all_mesa_extensions[] = {
+ { "GL_ARB_multisample", GL_ARB_multisample_functions },
+ { "GL_ARB_transpose_matrix", GL_ARB_transpose_matrix_functions },
+ { "GL_ARB_window_pos", GL_ARB_window_pos_functions },
+ { "GL_EXT_blend_func_separate", GL_EXT_blend_func_separate_functions },
+ { "GL_EXT_compiled_vertex_array", GL_EXT_compiled_vertex_array_functions },
+ { "GL_EXT_polygon_offset", GL_EXT_polygon_offset_functions },
+ { "GL_EXT_texture_object", GL_EXT_texture_object_functions },
+ { "GL_EXT_vertex_array", GL_EXT_vertex_array_functions },
+ { "GL_MESA_window_pos", GL_MESA_window_pos_functions },
+ { "GL_NV_vertex_program", GL_NV_vertex_program_functions },
+ { NULL, NULL }
+};
+
+
+/**
+ * Enable extensions supported by the driver.
+ *
+ * \bug
+ * ARB_imaging isn't handled properly. In Mesa, enabling ARB_imaging also
+ * enables all the sub-extensions that are folded into it. This means that
+ * we need to add entry-points (via \c driInitSingleExtension) for those
+ * new functions here.
+ */
+void driInitExtensions( GLcontext * ctx,
+ const struct dri_extension * extensions_to_enable,
+ GLboolean enable_imaging )
+{
+ static int first_time = 1;
+ unsigned i;
+
+ if ( first_time ) {
+ for ( i = 0 ; i < driDispatchRemapTable_size ; i++ ) {
+ driDispatchRemapTable[i] = -1;
+ }
+
+ first_time = 0;
+ driInitExtensions( ctx, all_mesa_extensions, GL_FALSE );
+ }
+
+ if ( (ctx != NULL) && enable_imaging ) {
+ _mesa_enable_imaging_extensions( ctx );
+ }
+
+ for ( i = 0 ; extensions_to_enable[i].name != NULL ; i++ ) {
+ driInitSingleExtension( ctx, & extensions_to_enable[i] );
+ }
+}
+
+
+
+
+/**
+ * Enable and add dispatch functions for a single extension
+ *
+ * \param ctx Context where extension is to be enabled.
+ * \param ext Extension that is to be enabled.
+ *
+ * \sa driInitExtensions, _mesa_enable_extension, _glapi_add_entrypoint
+ *
+ * \todo
+ * Determine if it would be better to use \c strlen instead of the hardcoded
+ * for-loops.
+ */
+void driInitSingleExtension( GLcontext * ctx,
+ const struct dri_extension * ext )
+{
+ unsigned i;
+
+
+ if ( ext->functions != NULL ) {
+ for ( i = 0 ; ext->functions[i].strings != NULL ; i++ ) {
+ const char * functions[16];
+ const char * parameter_signature;
+ const char * str = ext->functions[i].strings;
+ unsigned j;
+ unsigned offset;
+
+
+ /* Separate the parameter signature from the rest of the string.
+ * If the parameter signature is empty (i.e., the string starts
+ * with a NUL character), then the function has a void parameter
+ * list.
+ */
+ parameter_signature = str;
+ while ( str[0] != '\0' ) {
+ str++;
+ }
+ str++;
+
+
+ /* Divide the string into the substrings that name each
+ * entry-point for the function.
+ */
+ for ( j = 0 ; j < 16 ; j++ ) {
+ if ( str[0] == '\0' ) {
+ functions[j] = NULL;
+ break;
+ }
+
+ functions[j] = str;
+
+ while ( str[0] != '\0' ) {
+ str++;
+ }
+ str++;
+ }
+
+
+ /* Add each entry-point to the dispatch table.
+ */
+ offset = _glapi_add_dispatch( functions, parameter_signature );
+ if (offset == -1) {
+ fprintf(stderr, "DISPATCH ERROR! _glapi_add_dispatch failed "
+ "to add %s!\n", functions[0]);
+ }
+ else if (ext->functions[i].remap_index != -1) {
+ driDispatchRemapTable[ ext->functions[i].remap_index ] =
+ offset;
+ }
+ else if (ext->functions[i].offset != offset) {
+ fprintf(stderr, "DISPATCH ERROR! %s -> %u != %u\n",
+ functions[0], offset, ext->functions[i].offset);
+ }
+ }
+ }
+
+ if ( ctx != NULL ) {
+ _mesa_enable_extension( ctx, ext->name );
+ }
+}
+
+
+/**
+ * Utility function used by drivers to test the versions of other components.
+ *
+ * If one of the version requirements is not met, a message is logged using
+ * \c __driUtilMessage.
+ *
+ * \param driver_name Name of the driver. Used in error messages.
+ * \param driActual Actual DRI version supplied __driCreateNewScreen.
+ * \param driExpected Minimum DRI version required by the driver.
+ * \param ddxActual Actual DDX version supplied __driCreateNewScreen.
+ * \param ddxExpected Minimum DDX minor and range of DDX major version required by the driver.
+ * \param drmActual Actual DRM version supplied __driCreateNewScreen.
+ * \param drmExpected Minimum DRM version required by the driver.
+ *
+ * \returns \c GL_TRUE if all version requirements are met. Otherwise,
+ * \c GL_FALSE is returned.
+ *
+ * \sa __driCreateNewScreen, driCheckDriDdxDrmVersions2, __driUtilMessage
+ *
+ * \todo
+ * Now that the old \c driCheckDriDdxDrmVersions function is gone, this
+ * function and \c driCheckDriDdxDrmVersions2 should be renamed.
+ */
+GLboolean
+driCheckDriDdxDrmVersions3(const char * driver_name,
+ const __DRIversion * driActual,
+ const __DRIversion * driExpected,
+ const __DRIversion * ddxActual,
+ const __DRIutilversion2 * ddxExpected,
+ const __DRIversion * drmActual,
+ const __DRIversion * drmExpected)
+{
+ static const char format[] = "%s DRI driver expected %s version %d.%d.x "
+ "but got version %d.%d.%d\n";
+ static const char format2[] = "%s DRI driver expected %s version %d-%d.%d.x "
+ "but got version %d.%d.%d\n";
+
+
+ /* Check the DRI version */
+ if ( (driActual->major != driExpected->major)
+ || (driActual->minor < driExpected->minor) ) {
+ fprintf(stderr, format, driver_name, "DRI",
+ driExpected->major, driExpected->minor,
+ driActual->major, driActual->minor, driActual->patch);
+ return GL_FALSE;
+ }
+
+ /* Check that the DDX driver version is compatible */
+ /* for miniglx we pass in -1 so we can ignore the DDX version */
+ if ( (ddxActual->major != -1) && ((ddxActual->major < ddxExpected->major_min)
+ || (ddxActual->major > ddxExpected->major_max)
+ || (ddxActual->minor < ddxExpected->minor)) ) {
+ fprintf(stderr, format2, driver_name, "DDX",
+ ddxExpected->major_min, ddxExpected->major_max, ddxExpected->minor,
+ ddxActual->major, ddxActual->minor, ddxActual->patch);
+ return GL_FALSE;
+ }
+
+ /* Check that the DRM driver version is compatible */
+ if ( (drmActual->major != drmExpected->major)
+ || (drmActual->minor < drmExpected->minor) ) {
+ fprintf(stderr, format, driver_name, "DRM",
+ drmExpected->major, drmExpected->minor,
+ drmActual->major, drmActual->minor, drmActual->patch);
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+GLboolean
+driCheckDriDdxDrmVersions2(const char * driver_name,
+ const __DRIversion * driActual,
+ const __DRIversion * driExpected,
+ const __DRIversion * ddxActual,
+ const __DRIversion * ddxExpected,
+ const __DRIversion * drmActual,
+ const __DRIversion * drmExpected)
+{
+ __DRIutilversion2 ddx_expected;
+ ddx_expected.major_min = ddxExpected->major;
+ ddx_expected.major_max = ddxExpected->major;
+ ddx_expected.minor = ddxExpected->minor;
+ ddx_expected.patch = ddxExpected->patch;
+ return driCheckDriDdxDrmVersions3(driver_name, driActual,
+ driExpected, ddxActual, & ddx_expected,
+ drmActual, drmExpected);
+}
+
+GLboolean driClipRectToFramebuffer( const GLframebuffer *buffer,
+ GLint *x, GLint *y,
+ GLsizei *width, GLsizei *height )
+{
+ /* left clipping */
+ if (*x < buffer->_Xmin) {
+ *width -= (buffer->_Xmin - *x);
+ *x = buffer->_Xmin;
+ }
+
+ /* right clipping */
+ if (*x + *width > buffer->_Xmax)
+ *width -= (*x + *width - buffer->_Xmax - 1);
+
+ if (*width <= 0)
+ return GL_FALSE;
+
+ /* bottom clipping */
+ if (*y < buffer->_Ymin) {
+ *height -= (buffer->_Ymin - *y);
+ *y = buffer->_Ymin;
+ }
+
+ /* top clipping */
+ if (*y + *height > buffer->_Ymax)
+ *height -= (*y + *height - buffer->_Ymax - 1);
+
+ if (*height <= 0)
+ return GL_FALSE;
+
+ return GL_TRUE;
+}
+
+/**
+ * Creates a set of \c __GLcontextModes that a driver will expose.
+ *
+ * A set of \c __GLcontextModes will be created based on the supplied
+ * parameters. The number of modes processed will be 2 *
+ * \c num_depth_stencil_bits * \c num_db_modes.
+ *
+ * For the most part, data is just copied from \c depth_bits, \c stencil_bits,
+ * \c db_modes, and \c visType into each \c __GLcontextModes element.
+ * However, the meanings of \c fb_format and \c fb_type require further
+ * explanation. The \c fb_format specifies which color components are in
+ * each pixel and what the default order is. For example, \c GL_RGB specifies
+ * that red, green, blue are available and red is in the "most significant"
+ * position and blue is in the "least significant". The \c fb_type specifies
+ * the bit sizes of each component and the actual ordering. For example, if
+ * \c GL_UNSIGNED_SHORT_5_6_5_REV is specified with \c GL_RGB, bits [15:11]
+ * are the blue value, bits [10:5] are the green value, and bits [4:0] are
+ * the red value.
+ *
+ * One subtle issue is the combination of \c GL_RGB or \c GL_BGR and either
+ * of the \c GL_UNSIGNED_INT_8_8_8_8 modes. The resulting mask values in the
+ * \c __GLcontextModes structure is \b identical to the \c GL_RGBA or
+ * \c GL_BGRA case, except the \c alphaMask is zero. This means that, as
+ * far as this routine is concerned, \c GL_RGB with \c GL_UNSIGNED_INT_8_8_8_8
+ * still uses 32-bits.
+ *
+ * If in doubt, look at the tables used in the function.
+ *
+ * \param ptr_to_modes Pointer to a pointer to a linked list of
+ * \c __GLcontextModes. Upon completion, a pointer to
+ * the next element to be process will be stored here.
+ * If the function fails and returns \c GL_FALSE, this
+ * value will be unmodified, but some elements in the
+ * linked list may be modified.
+ * \param fb_format Format of the framebuffer. Currently only \c GL_RGB,
+ * \c GL_RGBA, \c GL_BGR, and \c GL_BGRA are supported.
+ * \param fb_type Type of the pixels in the framebuffer. Currently only
+ * \c GL_UNSIGNED_SHORT_5_6_5,
+ * \c GL_UNSIGNED_SHORT_5_6_5_REV,
+ * \c GL_UNSIGNED_INT_8_8_8_8, and
+ * \c GL_UNSIGNED_INT_8_8_8_8_REV are supported.
+ * \param depth_bits Array of depth buffer sizes to be exposed.
+ * \param stencil_bits Array of stencil buffer sizes to be exposed.
+ * \param num_depth_stencil_bits Number of entries in both \c depth_bits and
+ * \c stencil_bits.
+ * \param db_modes Array of buffer swap modes. If an element has a
+ * value of \c GLX_NONE, then it represents a
+ * single-buffered mode. Other valid values are
+ * \c GLX_SWAP_EXCHANGE_OML, \c GLX_SWAP_COPY_OML, and
+ * \c GLX_SWAP_UNDEFINED_OML. See the
+ * GLX_OML_swap_method extension spec for more details.
+ * \param num_db_modes Number of entries in \c db_modes.
+ * \param visType GLX visual type. Usually either \c GLX_TRUE_COLOR or
+ * \c GLX_DIRECT_COLOR.
+ *
+ * \returns
+ * \c GL_TRUE on success or \c GL_FALSE on failure. Currently the only
+ * cause of failure is a bad parameter (i.e., unsupported \c fb_format or
+ * \c fb_type).
+ *
+ * \todo
+ * There is currently no way to support packed RGB modes (i.e., modes with
+ * exactly 3 bytes per pixel) or floating-point modes. This could probably
+ * be done by creating some new, private enums with clever names likes
+ * \c GL_UNSIGNED_3BYTE_8_8_8, \c GL_4FLOAT_32_32_32_32,
+ * \c GL_4HALF_16_16_16_16, etc. We can cross that bridge when we come to it.
+ */
+__DRIconfig **
+driCreateConfigs(GLenum fb_format, GLenum fb_type,
+ const u_int8_t * depth_bits, const u_int8_t * stencil_bits,
+ unsigned num_depth_stencil_bits,
+ const GLenum * db_modes, unsigned num_db_modes)
+{
+ static const u_int8_t bits_table[4][4] = {
+ /* R G B A */
+ { 3, 3, 2, 0 }, /* Any GL_UNSIGNED_BYTE_3_3_2 */
+ { 5, 6, 5, 0 }, /* Any GL_UNSIGNED_SHORT_5_6_5 */
+ { 8, 8, 8, 0 }, /* Any RGB with any GL_UNSIGNED_INT_8_8_8_8 */
+ { 8, 8, 8, 8 } /* Any RGBA with any GL_UNSIGNED_INT_8_8_8_8 */
+ };
+
+ static const u_int32_t masks_table_rgb[6][4] = {
+ { 0x000000E0, 0x0000001C, 0x00000003, 0x00000000 }, /* 3_3_2 */
+ { 0x00000007, 0x00000038, 0x000000C0, 0x00000000 }, /* 2_3_3_REV */
+ { 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }, /* 5_6_5 */
+ { 0x0000001F, 0x000007E0, 0x0000F800, 0x00000000 }, /* 5_6_5_REV */
+ { 0xFF000000, 0x00FF0000, 0x0000FF00, 0x00000000 }, /* 8_8_8_8 */
+ { 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 } /* 8_8_8_8_REV */
+ };
+
+ static const u_int32_t masks_table_rgba[6][4] = {
+ { 0x000000E0, 0x0000001C, 0x00000003, 0x00000000 }, /* 3_3_2 */
+ { 0x00000007, 0x00000038, 0x000000C0, 0x00000000 }, /* 2_3_3_REV */
+ { 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }, /* 5_6_5 */
+ { 0x0000001F, 0x000007E0, 0x0000F800, 0x00000000 }, /* 5_6_5_REV */
+ { 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF }, /* 8_8_8_8 */
+ { 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }, /* 8_8_8_8_REV */
+ };
+
+ static const u_int32_t masks_table_bgr[6][4] = {
+ { 0x00000007, 0x00000038, 0x000000C0, 0x00000000 }, /* 3_3_2 */
+ { 0x000000E0, 0x0000001C, 0x00000003, 0x00000000 }, /* 2_3_3_REV */
+ { 0x0000001F, 0x000007E0, 0x0000F800, 0x00000000 }, /* 5_6_5 */
+ { 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }, /* 5_6_5_REV */
+ { 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 }, /* 8_8_8_8 */
+ { 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, /* 8_8_8_8_REV */
+ };
+
+ static const u_int32_t masks_table_bgra[6][4] = {
+ { 0x00000007, 0x00000038, 0x000000C0, 0x00000000 }, /* 3_3_2 */
+ { 0x000000E0, 0x0000001C, 0x00000003, 0x00000000 }, /* 2_3_3_REV */
+ { 0x0000001F, 0x000007E0, 0x0000F800, 0x00000000 }, /* 5_6_5 */
+ { 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }, /* 5_6_5_REV */
+ { 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF }, /* 8_8_8_8 */
+ { 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 }, /* 8_8_8_8_REV */
+ };
+
+ static const u_int8_t bytes_per_pixel[6] = {
+ 1, /* 3_3_2 */
+ 1, /* 2_3_3_REV */
+ 2, /* 5_6_5 */
+ 2, /* 5_6_5_REV */
+ 4, /* 8_8_8_8 */
+ 4 /* 8_8_8_8_REV */
+ };
+
+ const u_int8_t * bits;
+ const u_int32_t * masks;
+ int index;
+ __DRIconfig **configs, **c;
+ __GLcontextModes *modes;
+ unsigned i;
+ unsigned j;
+ unsigned k;
+ unsigned num_modes;
+ unsigned num_accum_bits = 2;
+
+ switch ( fb_type ) {
+ case GL_UNSIGNED_BYTE_3_3_2:
+ index = 0;
+ break;
+ case GL_UNSIGNED_BYTE_2_3_3_REV:
+ index = 1;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ index = 2;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5_REV:
+ index = 3;
+ break;
+ case GL_UNSIGNED_INT_8_8_8_8:
+ index = 4;
+ break;
+ case GL_UNSIGNED_INT_8_8_8_8_REV:
+ index = 5;
+ break;
+ default:
+ fprintf( stderr, "[%s:%d] Unknown framebuffer type 0x%04x.\n",
+ __FUNCTION__, __LINE__, fb_type );
+ return NULL;
+ }
+
+
+ /* Valid types are GL_UNSIGNED_SHORT_5_6_5 and GL_UNSIGNED_INT_8_8_8_8 and
+ * the _REV versions.
+ *
+ * Valid formats are GL_RGBA, GL_RGB, and GL_BGRA.
+ */
+
+ switch ( fb_format ) {
+ case GL_RGB:
+ masks = masks_table_rgb[ index ];
+ break;
+
+ case GL_RGBA:
+ masks = masks_table_rgba[ index ];
+ break;
+
+ case GL_BGR:
+ masks = masks_table_bgr[ index ];
+ break;
+
+ case GL_BGRA:
+ masks = masks_table_bgra[ index ];
+ break;
+
+ default:
+ fprintf( stderr, "[%s:%d] Unknown framebuffer format 0x%04x.\n",
+ __FUNCTION__, __LINE__, fb_format );
+ return NULL;
+ }
+
+ switch ( bytes_per_pixel[ index ] ) {
+ case 1:
+ bits = bits_table[0];
+ break;
+ case 2:
+ bits = bits_table[1];
+ break;
+ default:
+ bits = ((fb_format == GL_RGB) || (fb_format == GL_BGR))
+ ? bits_table[2]
+ : bits_table[3];
+ break;
+ }
+
+ num_modes = num_depth_stencil_bits * num_db_modes * num_accum_bits;
+ configs = _mesa_calloc((num_modes + 1) * sizeof *configs);
+ if (configs == NULL)
+ return NULL;
+
+ c = configs;
+ for ( k = 0 ; k < num_depth_stencil_bits ; k++ ) {
+ for ( i = 0 ; i < num_db_modes ; i++ ) {
+ for ( j = 0 ; j < num_accum_bits ; j++ ) {
+ *c = _mesa_malloc (sizeof **c);
+ modes = &(*c)->modes;
+ c++;
+
+ memset(modes, 0, sizeof *modes);
+ modes->redBits = bits[0];
+ modes->greenBits = bits[1];
+ modes->blueBits = bits[2];
+ modes->alphaBits = bits[3];
+ modes->redMask = masks[0];
+ modes->greenMask = masks[1];
+ modes->blueMask = masks[2];
+ modes->alphaMask = masks[3];
+ modes->rgbBits = modes->redBits + modes->greenBits
+ + modes->blueBits + modes->alphaBits;
+
+ modes->accumRedBits = 16 * j;
+ modes->accumGreenBits = 16 * j;
+ modes->accumBlueBits = 16 * j;
+ modes->accumAlphaBits = (masks[3] != 0) ? 16 * j : 0;
+ modes->visualRating = (j == 0) ? GLX_NONE : GLX_SLOW_CONFIG;
+
+ modes->stencilBits = stencil_bits[k];
+ modes->depthBits = depth_bits[k];
+
+ modes->transparentPixel = GLX_NONE;
+ modes->transparentRed = GLX_DONT_CARE;
+ modes->transparentGreen = GLX_DONT_CARE;
+ modes->transparentBlue = GLX_DONT_CARE;
+ modes->transparentAlpha = GLX_DONT_CARE;
+ modes->transparentIndex = GLX_DONT_CARE;
+ modes->visualType = GLX_DONT_CARE;
+ modes->renderType = GLX_RGBA_BIT;
+ modes->drawableType = GLX_WINDOW_BIT;
+ modes->rgbMode = GL_TRUE;
+
+ if ( db_modes[i] == GLX_NONE ) {
+ modes->doubleBufferMode = GL_FALSE;
+ }
+ else {
+ modes->doubleBufferMode = GL_TRUE;
+ modes->swapMethod = db_modes[i];
+ }
+
+ modes->haveAccumBuffer = ((modes->accumRedBits +
+ modes->accumGreenBits +
+ modes->accumBlueBits +
+ modes->accumAlphaBits) > 0);
+ modes->haveDepthBuffer = (modes->depthBits > 0);
+ modes->haveStencilBuffer = (modes->stencilBits > 0);
+
+ modes->bindToTextureRgb = GL_TRUE;
+ modes->bindToTextureRgba = GL_TRUE;
+ modes->bindToMipmapTexture = GL_FALSE;
+ modes->bindToTextureTargets = modes->rgbMode ?
+ __DRI_ATTRIB_TEXTURE_1D_BIT |
+ __DRI_ATTRIB_TEXTURE_2D_BIT |
+ __DRI_ATTRIB_TEXTURE_RECTANGLE_BIT :
+ 0;
+ }
+ }
+ }
+ *c = NULL;
+
+ return configs;
+}
+
+const __DRIconfig **driConcatConfigs(__DRIconfig **a, __DRIconfig **b)
+{
+ const __DRIconfig **all;
+ int i, j, index;
+
+ i = 0;
+ while (a[i] != NULL)
+ i++;
+ j = 0;
+ while (b[j] != NULL)
+ j++;
+
+ all = _mesa_malloc((i + j + 1) * sizeof *all);
+ index = 0;
+ for (i = 0; a[i] != NULL; i++)
+ all[index++] = a[i];
+ for (j = 0; b[j] != NULL; j++)
+ all[index++] = b[j];
+ all[index++] = NULL;
+
+ _mesa_free(a);
+ _mesa_free(b);
+
+ return all;
+}
+
+#define __ATTRIB(attrib, field) \
+ { attrib, offsetof(__GLcontextModes, field) }
+
+static const struct { unsigned int attrib, offset; } attribMap[] = {
+ __ATTRIB(__DRI_ATTRIB_BUFFER_SIZE, rgbBits),
+ __ATTRIB(__DRI_ATTRIB_LEVEL, level),
+ __ATTRIB(__DRI_ATTRIB_RED_SIZE, redBits),
+ __ATTRIB(__DRI_ATTRIB_GREEN_SIZE, greenBits),
+ __ATTRIB(__DRI_ATTRIB_BLUE_SIZE, blueBits),
+ __ATTRIB(__DRI_ATTRIB_ALPHA_SIZE, alphaBits),
+ __ATTRIB(__DRI_ATTRIB_DEPTH_SIZE, depthBits),
+ __ATTRIB(__DRI_ATTRIB_STENCIL_SIZE, stencilBits),
+ __ATTRIB(__DRI_ATTRIB_ACCUM_RED_SIZE, accumRedBits),
+ __ATTRIB(__DRI_ATTRIB_ACCUM_GREEN_SIZE, accumGreenBits),
+ __ATTRIB(__DRI_ATTRIB_ACCUM_BLUE_SIZE, accumBlueBits),
+ __ATTRIB(__DRI_ATTRIB_ACCUM_ALPHA_SIZE, accumAlphaBits),
+ __ATTRIB(__DRI_ATTRIB_SAMPLE_BUFFERS, sampleBuffers),
+ __ATTRIB(__DRI_ATTRIB_SAMPLES, samples),
+ __ATTRIB(__DRI_ATTRIB_DOUBLE_BUFFER, doubleBufferMode),
+ __ATTRIB(__DRI_ATTRIB_STEREO, stereoMode),
+ __ATTRIB(__DRI_ATTRIB_AUX_BUFFERS, numAuxBuffers),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_TYPE, transparentPixel),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_INDEX_VALUE, transparentPixel),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_RED_VALUE, transparentRed),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_GREEN_VALUE, transparentGreen),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_BLUE_VALUE, transparentBlue),
+ __ATTRIB(__DRI_ATTRIB_TRANSPARENT_ALPHA_VALUE, transparentAlpha),
+ __ATTRIB(__DRI_ATTRIB_FLOAT_MODE, floatMode),
+ __ATTRIB(__DRI_ATTRIB_RED_MASK, redMask),
+ __ATTRIB(__DRI_ATTRIB_GREEN_MASK, greenMask),
+ __ATTRIB(__DRI_ATTRIB_BLUE_MASK, blueMask),
+ __ATTRIB(__DRI_ATTRIB_ALPHA_MASK, alphaMask),
+ __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_WIDTH, maxPbufferWidth),
+ __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_HEIGHT, maxPbufferHeight),
+ __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_PIXELS, maxPbufferPixels),
+ __ATTRIB(__DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH, optimalPbufferWidth),
+ __ATTRIB(__DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT, optimalPbufferHeight),
+ __ATTRIB(__DRI_ATTRIB_SWAP_METHOD, swapMethod),
+ __ATTRIB(__DRI_ATTRIB_BIND_TO_TEXTURE_RGB, bindToTextureRgb),
+ __ATTRIB(__DRI_ATTRIB_BIND_TO_TEXTURE_RGBA, bindToTextureRgba),
+ __ATTRIB(__DRI_ATTRIB_BIND_TO_MIPMAP_TEXTURE, bindToMipmapTexture),
+ __ATTRIB(__DRI_ATTRIB_BIND_TO_TEXTURE_TARGETS, bindToTextureTargets),
+ __ATTRIB(__DRI_ATTRIB_YINVERTED, yInverted),
+
+ /* The struct field doesn't matter here, these are handled by the
+ * switch in driGetConfigAttribIndex. We need them in the array
+ * so the iterator includes them though.*/
+ __ATTRIB(__DRI_ATTRIB_RENDER_TYPE, level),
+ __ATTRIB(__DRI_ATTRIB_CONFIG_CAVEAT, level),
+ __ATTRIB(__DRI_ATTRIB_SWAP_METHOD, level)
+};
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static int
+driGetConfigAttribIndex(const __DRIconfig *config,
+ unsigned int index, unsigned int *value)
+{
+ switch (attribMap[index].attrib) {
+ case __DRI_ATTRIB_RENDER_TYPE:
+ if (config->modes.rgbMode)
+ *value = __DRI_ATTRIB_RGBA_BIT;
+ else
+ *value = __DRI_ATTRIB_COLOR_INDEX_BIT;
+ break;
+ case __DRI_ATTRIB_CONFIG_CAVEAT:
+ if (config->modes.visualRating == GLX_NON_CONFORMANT_CONFIG)
+ *value = __DRI_ATTRIB_NON_CONFORMANT_CONFIG;
+ else if (config->modes.visualRating == GLX_SLOW_CONFIG)
+ *value = __DRI_ATTRIB_SLOW_BIT;
+ else
+ *value = 0;
+ break;
+ case __DRI_ATTRIB_SWAP_METHOD:
+ break;
+
+ case __DRI_ATTRIB_FLOAT_MODE:
+ *value = config->modes.floatMode;
+ break;
+
+ default:
+ *value = *(unsigned int *)
+ ((char *) &config->modes + attribMap[index].offset);
+
+ break;
+ }
+
+ return GL_TRUE;
+}
+
+int
+driGetConfigAttrib(const __DRIconfig *config,
+ unsigned int attrib, unsigned int *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(attribMap); i++)
+ if (attribMap[i].attrib == attrib)
+ return driGetConfigAttribIndex(config, i, value);
+
+ return GL_FALSE;
+}
+
+int
+driIndexConfigAttrib(const __DRIconfig *config, int index,
+ unsigned int *attrib, unsigned int *value)
+{
+ if (index >= 0 && index < ARRAY_SIZE(attribMap)) {
+ *attrib = attribMap[index].attrib;
+ return driGetConfigAttribIndex(config, index, value);
+ }
+
+ return GL_FALSE;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/vboxdri_drv.c b/src/VBox/Additions/common/crOpenGL/vboxdri_drv.c
new file mode 100644
index 00000000..e82975e3
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/vboxdri_drv.c
@@ -0,0 +1,694 @@
+/*
+ * Mesa 3-D graphics library
+ * Version: 6.3
+ *
+ * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
+ *
+ * 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
+ * BRIAN PAUL 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.
+ */
+
+/* Minimal swrast-based dri loadable driver.
+ *
+ * Todo:
+ * -- Use malloced (rather than framebuffer) memory for backbuffer
+ * -- 32bpp is hardwired -- fix
+ *
+ * NOTES:
+ * -- No mechanism for cliprects or resize notification --
+ * assumes this is a fullscreen device.
+ * -- No locking -- assumes this is the only driver accessing this
+ * device.
+ * -- Doesn't (yet) make use of any acceleration or other interfaces
+ * provided by fb. Would be entirely happy working against any
+ * fullscreen interface.
+ * -- HOWEVER: only a small number of pixelformats are supported, and
+ * the mechanism for choosing between them makes some assumptions
+ * that may not be valid everywhere.
+ */
+
+#include "driver.h"
+#include "drm.h"
+#include "utils.h"
+#include "drirenderbuffer.h"
+
+#include "buffers.h"
+#include "extensions.h"
+#include "framebuffer.h"
+#include "renderbuffer.h"
+#include "vbo/vbo.h"
+#include "swrast/swrast.h"
+#include "swrast_setup/swrast_setup.h"
+#include "tnl/tnl.h"
+#include "tnl/t_context.h"
+#include "tnl/t_pipeline.h"
+#include "drivers/common/driverfuncs.h"
+
+#define need_GL_VERSION_1_3
+#define need_GL_VERSION_1_4
+#define need_GL_VERSION_1_5
+#define need_GL_VERSION_2_0
+#define need_GL_VERSION_2_1
+
+/* sw extensions for imaging */
+#define need_GL_EXT_blend_color
+#define need_GL_EXT_blend_minmax
+#define need_GL_EXT_convolution
+#define need_GL_EXT_histogram
+#define need_GL_SGI_color_table
+
+/* sw extensions not associated with some GL version */
+#define need_GL_ARB_shader_objects
+#define need_GL_ARB_vertex_program
+#define need_GL_APPLE_vertex_array_object
+#define need_GL_ATI_fragment_shader
+#define need_GL_EXT_depth_bounds_test
+#define need_GL_EXT_framebuffer_object
+#define need_GL_EXT_framebuffer_blit
+#define need_GL_EXT_gpu_program_parameters
+#define need_GL_EXT_paletted_texture
+#define need_GL_IBM_multimode_draw_arrays
+#define need_GL_MESA_resize_buffers
+#define need_GL_NV_vertex_program
+#define need_GL_NV_fragment_program
+
+#include "extension_helper.h"
+
+const struct dri_extension card_extensions[] =
+{
+ { "GL_VERSION_1_3", GL_VERSION_1_3_functions },
+ { "GL_VERSION_1_4", GL_VERSION_1_4_functions },
+ { "GL_VERSION_1_5", GL_VERSION_1_5_functions },
+ { "GL_VERSION_2_0", GL_VERSION_2_0_functions },
+ { "GL_VERSION_2_1", GL_VERSION_2_1_functions },
+
+ { "GL_EXT_blend_color", GL_EXT_blend_color_functions },
+ { "GL_EXT_blend_minmax", GL_EXT_blend_minmax_functions },
+ { "GL_EXT_convolution", GL_EXT_convolution_functions },
+ { "GL_EXT_histogram", GL_EXT_histogram_functions },
+ { "GL_SGI_color_table", GL_SGI_color_table_functions },
+
+ { "GL_ARB_shader_objects", GL_ARB_shader_objects_functions },
+ { "GL_ARB_vertex_program", GL_ARB_vertex_program_functions },
+ { "GL_APPLE_vertex_array_object", GL_APPLE_vertex_array_object_functions },
+ { "GL_ATI_fragment_shader", GL_ATI_fragment_shader_functions },
+ { "GL_EXT_depth_bounds_test", GL_EXT_depth_bounds_test_functions },
+ { "GL_EXT_framebuffer_object", GL_EXT_framebuffer_object_functions },
+ { "GL_EXT_framebuffer_blit", GL_EXT_framebuffer_blit_functions },
+ { "GL_EXT_gpu_program_parameters", GL_EXT_gpu_program_parameters_functions },
+ { "GL_EXT_paletted_texture", GL_EXT_paletted_texture_functions },
+ { "GL_IBM_multimode_draw_arrays", GL_IBM_multimode_draw_arrays_functions },
+ { "GL_MESA_resize_buffers", GL_MESA_resize_buffers_functions },
+ { "GL_NV_vertex_program", GL_NV_vertex_program_functions },
+ { "GL_NV_fragment_program", GL_NV_fragment_program_functions },
+ { NULL, NULL }
+};
+
+void fbSetSpanFunctions(driRenderbuffer *drb, const GLvisual *vis);
+
+typedef struct {
+ GLcontext *glCtx; /* Mesa context */
+
+ struct {
+ __DRIcontextPrivate *context;
+ __DRIscreenPrivate *screen;
+ __DRIdrawablePrivate *drawable; /* drawable bound to this ctx */
+ } dri;
+
+} fbContext, *fbContextPtr;
+
+#define FB_CONTEXT(ctx) ((fbContextPtr)(ctx->DriverCtx))
+
+
+static const GLubyte *
+get_string(GLcontext *ctx, GLenum pname)
+{
+ (void) ctx;
+ switch (pname) {
+ case GL_RENDERER:
+ return (const GLubyte *) "Mesa dumb framebuffer";
+ default:
+ return NULL;
+ }
+}
+
+
+static void
+update_state( GLcontext *ctx, GLuint new_state )
+{
+ /* not much to do here - pass it on */
+ _swrast_InvalidateState( ctx, new_state );
+ _swsetup_InvalidateState( ctx, new_state );
+ _vbo_InvalidateState( ctx, new_state );
+ _tnl_InvalidateState( ctx, new_state );
+}
+
+
+/**
+ * Called by ctx->Driver.GetBufferSize from in core Mesa to query the
+ * current framebuffer size.
+ */
+static void
+get_buffer_size( GLframebuffer *buffer, GLuint *width, GLuint *height )
+{
+ GET_CURRENT_CONTEXT(ctx);
+ fbContextPtr fbmesa = FB_CONTEXT(ctx);
+
+ *width = fbmesa->dri.drawable->w;
+ *height = fbmesa->dri.drawable->h;
+}
+
+
+static void
+updateFramebufferSize(GLcontext *ctx)
+{
+ fbContextPtr fbmesa = FB_CONTEXT(ctx);
+ struct gl_framebuffer *fb = ctx->WinSysDrawBuffer;
+ if (fbmesa->dri.drawable->w != fb->Width ||
+ fbmesa->dri.drawable->h != fb->Height) {
+ driUpdateFramebufferSize(ctx, fbmesa->dri.drawable);
+ }
+}
+
+static void
+viewport(GLcontext *ctx, GLint x, GLint y, GLsizei w, GLsizei h)
+{
+ /* XXX this should be called after we acquire the DRI lock, not here */
+ updateFramebufferSize(ctx);
+}
+
+
+static void
+init_core_functions( struct dd_function_table *functions )
+{
+ functions->GetString = get_string;
+ functions->UpdateState = update_state;
+ functions->GetBufferSize = get_buffer_size;
+ functions->Viewport = viewport;
+
+ functions->Clear = _swrast_Clear; /* could accelerate with blits */
+}
+
+
+/*
+ * Generate code for span functions.
+ */
+
+/* 24-bit BGR */
+#define NAME(PREFIX) PREFIX##_B8G8R8
+#define RB_TYPE GLubyte
+#define SPAN_VARS \
+ driRenderbuffer *drb = (driRenderbuffer *) rb;
+#define INIT_PIXEL_PTR(P, X, Y) \
+ GLubyte *P = (GLubyte *)drb->Base.Data + (drb->Base.Height - (Y)) * drb->pitch + (X) * 3;
+#define INC_PIXEL_PTR(P) P += 3
+#define STORE_PIXEL(DST, X, Y, VALUE) \
+ DST[0] = VALUE[BCOMP]; \
+ DST[1] = VALUE[GCOMP]; \
+ DST[2] = VALUE[RCOMP]
+#define FETCH_PIXEL(DST, SRC) \
+ DST[RCOMP] = SRC[2]; \
+ DST[GCOMP] = SRC[1]; \
+ DST[BCOMP] = SRC[0]; \
+ DST[ACOMP] = 0xff
+
+#include "swrast/s_spantemp.h"
+
+
+/* 32-bit BGRA */
+#define NAME(PREFIX) PREFIX##_B8G8R8A8
+#define RB_TYPE GLubyte
+#define SPAN_VARS \
+ driRenderbuffer *drb = (driRenderbuffer *) rb;
+#define INIT_PIXEL_PTR(P, X, Y) \
+ GLubyte *P = (GLubyte *)drb->Base.Data + (drb->Base.Height - (Y)) * drb->pitch + (X) * 4;
+#define INC_PIXEL_PTR(P) P += 4
+#define STORE_PIXEL(DST, X, Y, VALUE) \
+ DST[0] = VALUE[BCOMP]; \
+ DST[1] = VALUE[GCOMP]; \
+ DST[2] = VALUE[RCOMP]; \
+ DST[3] = VALUE[ACOMP]
+#define STORE_PIXEL_RGB(DST, X, Y, VALUE) \
+ DST[0] = VALUE[BCOMP]; \
+ DST[1] = VALUE[GCOMP]; \
+ DST[2] = VALUE[RCOMP]; \
+ DST[3] = 0xff
+#define FETCH_PIXEL(DST, SRC) \
+ DST[RCOMP] = SRC[2]; \
+ DST[GCOMP] = SRC[1]; \
+ DST[BCOMP] = SRC[0]; \
+ DST[ACOMP] = SRC[3]
+
+#include "swrast/s_spantemp.h"
+
+
+/* 16-bit BGR (XXX implement dithering someday) */
+#define NAME(PREFIX) PREFIX##_B5G6R5
+#define RB_TYPE GLubyte
+#define SPAN_VARS \
+ driRenderbuffer *drb = (driRenderbuffer *) rb;
+#define INIT_PIXEL_PTR(P, X, Y) \
+ GLushort *P = (GLushort *)drb->Base.Data + (drb->Base.Height - (Y)) * drb->pitch + (X) * 2;
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_PIXEL(DST, X, Y, VALUE) \
+ DST[0] = ( (((VALUE[RCOMP]) & 0xf8) << 8) | (((VALUE[GCOMP]) & 0xfc) << 3) | ((VALUE[BCOMP]) >> 3) )
+#define FETCH_PIXEL(DST, SRC) \
+ DST[RCOMP] = ( (((SRC[0]) >> 8) & 0xf8) | (((SRC[0]) >> 11) & 0x7) ); \
+ DST[GCOMP] = ( (((SRC[0]) >> 3) & 0xfc) | (((SRC[0]) >> 5) & 0x3) ); \
+ DST[BCOMP] = ( (((SRC[0]) << 3) & 0xf8) | (((SRC[0]) ) & 0x7) ); \
+ DST[ACOMP] = 0xff
+
+#include "swrast/s_spantemp.h"
+
+
+/* 15-bit BGR (XXX implement dithering someday) */
+#define NAME(PREFIX) PREFIX##_B5G5R5
+#define RB_TYPE GLubyte
+#define SPAN_VARS \
+ driRenderbuffer *drb = (driRenderbuffer *) rb;
+#define INIT_PIXEL_PTR(P, X, Y) \
+ GLushort *P = (GLushort *)drb->Base.Data + (drb->Base.Height - (Y)) * drb->pitch + (X) * 2;
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_PIXEL(DST, X, Y, VALUE) \
+ DST[0] = ( (((VALUE[RCOMP]) & 0xf8) << 7) | (((VALUE[GCOMP]) & 0xf8) << 2) | ((VALUE[BCOMP]) >> 3) )
+#define FETCH_PIXEL(DST, SRC) \
+ DST[RCOMP] = ( (((SRC[0]) >> 7) & 0xf8) | (((SRC[0]) >> 10) & 0x7) ); \
+ DST[GCOMP] = ( (((SRC[0]) >> 2) & 0xf8) | (((SRC[0]) >> 5) & 0x7) ); \
+ DST[BCOMP] = ( (((SRC[0]) << 3) & 0xf8) | (((SRC[0]) ) & 0x7) ); \
+ DST[ACOMP] = 0xff
+
+#include "swrast/s_spantemp.h"
+
+
+/* 8-bit color index */
+#define NAME(PREFIX) PREFIX##_CI8
+#define CI_MODE
+#define RB_TYPE GLubyte
+#define SPAN_VARS \
+ driRenderbuffer *drb = (driRenderbuffer *) rb;
+#define INIT_PIXEL_PTR(P, X, Y) \
+ GLubyte *P = (GLubyte *)drb->Base.Data + (drb->Base.Height - (Y)) * drb->pitch + (X);
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_PIXEL(DST, X, Y, VALUE) \
+ *DST = VALUE[0]
+#define FETCH_PIXEL(DST, SRC) \
+ DST = SRC[0]
+
+#include "swrast/s_spantemp.h"
+
+
+
+void
+fbSetSpanFunctions(driRenderbuffer *drb, const GLvisual *vis)
+{
+ ASSERT(drb->Base.InternalFormat == GL_RGBA);
+ if (drb->Base.InternalFormat == GL_RGBA) {
+ if (vis->redBits == 5 && vis->greenBits == 6 && vis->blueBits == 5) {
+ drb->Base.GetRow = get_row_B5G6R5;
+ drb->Base.GetValues = get_values_B5G6R5;
+ drb->Base.PutRow = put_row_B5G6R5;
+ drb->Base.PutMonoRow = put_mono_row_B5G6R5;
+ drb->Base.PutRowRGB = put_row_rgb_B5G6R5;
+ drb->Base.PutValues = put_values_B5G6R5;
+ drb->Base.PutMonoValues = put_mono_values_B5G6R5;
+ }
+ else if (vis->redBits == 5 && vis->greenBits == 5 && vis->blueBits == 5) {
+ drb->Base.GetRow = get_row_B5G5R5;
+ drb->Base.GetValues = get_values_B5G5R5;
+ drb->Base.PutRow = put_row_B5G5R5;
+ drb->Base.PutMonoRow = put_mono_row_B5G5R5;
+ drb->Base.PutRowRGB = put_row_rgb_B5G5R5;
+ drb->Base.PutValues = put_values_B5G5R5;
+ drb->Base.PutMonoValues = put_mono_values_B5G5R5;
+ }
+ else if (vis->redBits == 8 && vis->greenBits == 8 && vis->blueBits == 8
+ && vis->alphaBits == 8) {
+ drb->Base.GetRow = get_row_B8G8R8A8;
+ drb->Base.GetValues = get_values_B8G8R8A8;
+ drb->Base.PutRow = put_row_B8G8R8A8;
+ drb->Base.PutMonoRow = put_mono_row_B8G8R8A8;
+ drb->Base.PutRowRGB = put_row_rgb_B8G8R8A8;
+ drb->Base.PutValues = put_values_B8G8R8A8;
+ drb->Base.PutMonoValues = put_mono_values_B8G8R8A8;
+ }
+ else if (vis->redBits == 8 && vis->greenBits == 8 && vis->blueBits == 8
+ && vis->alphaBits == 0) {
+ drb->Base.GetRow = get_row_B8G8R8;
+ drb->Base.GetValues = get_values_B8G8R8;
+ drb->Base.PutRow = put_row_B8G8R8;
+ drb->Base.PutMonoRow = put_mono_row_B8G8R8;
+ drb->Base.PutRowRGB = put_row_rgb_B8G8R8;
+ drb->Base.PutValues = put_values_B8G8R8;
+ drb->Base.PutMonoValues = put_mono_values_B8G8R8;
+ }
+ else if (vis->indexBits == 8) {
+ drb->Base.GetRow = get_row_CI8;
+ drb->Base.GetValues = get_values_CI8;
+ drb->Base.PutRow = put_row_CI8;
+ drb->Base.PutMonoRow = put_mono_row_CI8;
+ drb->Base.PutValues = put_values_CI8;
+ drb->Base.PutMonoValues = put_mono_values_CI8;
+ }
+ }
+ else {
+ /* hardware z/stencil/etc someday */
+ }
+}
+
+
+static void
+fbDestroyScreen( __DRIscreenPrivate *sPriv )
+{
+}
+
+
+static const __DRIconfig **fbFillInModes(unsigned pixel_bits,
+ unsigned depth_bits,
+ unsigned stencil_bits,
+ GLboolean have_back_buffer)
+{
+ unsigned deep = (depth_bits > 17);
+
+ /* Right now GLX_SWAP_COPY_OML isn't supported, but it would be easy
+ * enough to add support. Basically, if a context is created with an
+ * fbconfig where the swap method is GLX_SWAP_COPY_OML, pageflipping
+ * will never be used.
+ */
+
+ static const GLenum db_modes[2] = { GLX_NONE, GLX_SWAP_UNDEFINED_OML };
+ uint8_t depth_bits_array[4];
+ uint8_t stencil_bits_array[4];
+ if(deep) {
+ depth_bits_array[0] = 0;
+ depth_bits_array[1] = 24;
+ stencil_bits_array[0] = 0;
+ stencil_bits_array[1] = 8;
+ } else {
+ depth_bits_array[0] = depth_bits;
+ depth_bits_array[1] = 0;
+ depth_bits_array[2] = depth_bits;
+ depth_bits_array[3] = 0;
+ stencil_bits_array[0] = 0;
+ stencil_bits_array[1] = 0;
+ stencil_bits_array[2] = 8;
+ stencil_bits_array[3] = 8;
+ }
+
+ return driCreateConfigs(
+ deep ? GL_RGBA : GL_RGB,
+ deep ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT_5_6_5,
+ depth_bits_array,
+ stencil_bits_array,
+ deep ? 2 : 4,
+ db_modes, 2);
+}
+
+/**
+ * This is the driver specific part of the createNewScreen entry point.
+ * Called when using legacy DRI.
+ *
+ * return the __GLcontextModes supported by this driver
+ */
+static const __DRIconfig **fbInitScreen(__DRIscreenPrivate *psp)
+{
+ static const __DRIversion ddx_expected = { 1, 0, 0 };
+ static const __DRIversion dri_expected = { 4, 0, 0 };
+ static const __DRIversion drm_expected = { 1, 0, 0 };
+
+
+ if ( ! driCheckDriDdxDrmVersions2( "vboxvideo",
+ & psp->dri_version, & dri_expected,
+ & psp->ddx_version, & ddx_expected,
+ & psp->drm_version, & drm_expected ) ) {
+ return NULL;
+ }
+
+ driInitExtensions( NULL, card_extensions, GL_FALSE );
+
+ return fbFillInModes( psp->fbBPP,
+ (psp->fbBPP == 16) ? 16 : 24,
+ (psp->fbBPP == 16) ? 0 : 8,
+ 1);
+}
+
+/* Create the device specific context.
+ */
+static GLboolean
+fbCreateContext( const __GLcontextModes *glVisual,
+ __DRIcontextPrivate *driContextPriv,
+ void *sharedContextPrivate)
+{
+ fbContextPtr fbmesa;
+ GLcontext *ctx, *shareCtx;
+ struct dd_function_table functions;
+
+ assert(glVisual);
+ assert(driContextPriv);
+
+ /* Allocate the Fb context */
+ fbmesa = (fbContextPtr) _mesa_calloc( sizeof(*fbmesa) );
+ if ( !fbmesa )
+ return GL_FALSE;
+
+ /* Init default driver functions then plug in our FBdev-specific functions
+ */
+ _mesa_init_driver_functions(&functions);
+ init_core_functions(&functions);
+
+ /* Allocate the Mesa context */
+ if (sharedContextPrivate)
+ shareCtx = ((fbContextPtr) sharedContextPrivate)->glCtx;
+ else
+ shareCtx = NULL;
+
+ ctx = fbmesa->glCtx = _mesa_create_context(glVisual, shareCtx,
+ &functions, (void *) fbmesa);
+ if (!fbmesa->glCtx) {
+ _mesa_free(fbmesa);
+ return GL_FALSE;
+ }
+ driContextPriv->driverPrivate = fbmesa;
+
+ /* Create module contexts */
+ _swrast_CreateContext( ctx );
+ _vbo_CreateContext( ctx );
+ _tnl_CreateContext( ctx );
+ _swsetup_CreateContext( ctx );
+ _swsetup_Wakeup( ctx );
+
+
+ /* use default TCL pipeline */
+ {
+ TNLcontext *tnl = TNL_CONTEXT(ctx);
+ tnl->Driver.RunPipeline = _tnl_run_pipeline;
+ }
+
+ _mesa_enable_sw_extensions(ctx);
+
+ return GL_TRUE;
+}
+
+
+static void
+fbDestroyContext( __DRIcontextPrivate *driContextPriv )
+{
+ GET_CURRENT_CONTEXT(ctx);
+ fbContextPtr fbmesa = (fbContextPtr) driContextPriv->driverPrivate;
+ fbContextPtr current = ctx ? FB_CONTEXT(ctx) : NULL;
+
+ /* check if we're deleting the currently bound context */
+ if (fbmesa == current) {
+ _mesa_make_current(NULL, NULL, NULL);
+ }
+
+ /* Free fb context resources */
+ if ( fbmesa ) {
+ _swsetup_DestroyContext( fbmesa->glCtx );
+ _tnl_DestroyContext( fbmesa->glCtx );
+ _vbo_DestroyContext( fbmesa->glCtx );
+ _swrast_DestroyContext( fbmesa->glCtx );
+
+ /* free the Mesa context */
+ fbmesa->glCtx->DriverCtx = NULL;
+ _mesa_destroy_context( fbmesa->glCtx );
+
+ _mesa_free( fbmesa );
+ }
+}
+
+
+/* Create and initialize the Mesa and driver specific pixmap buffer
+ * data.
+ */
+static GLboolean
+fbCreateBuffer( __DRIscreenPrivate *driScrnPriv,
+ __DRIdrawablePrivate *driDrawPriv,
+ const __GLcontextModes *mesaVis,
+ GLboolean isPixmap )
+{
+ struct gl_framebuffer *mesa_framebuffer;
+
+ if (isPixmap) {
+ return GL_FALSE; /* not implemented */
+ }
+ else {
+ const GLboolean swDepth = mesaVis->depthBits > 0;
+ const GLboolean swAlpha = mesaVis->alphaBits > 0;
+ const GLboolean swAccum = mesaVis->accumRedBits > 0;
+ const GLboolean swStencil = mesaVis->stencilBits > 0;
+
+ mesa_framebuffer = _mesa_create_framebuffer(mesaVis);
+ if (!mesa_framebuffer)
+ return 0;
+
+ /* XXX double-check these parameters (bpp vs cpp, etc) */
+ {
+ driRenderbuffer *drb = driNewRenderbuffer(GL_RGBA,
+ driScrnPriv->pFB,
+ driScrnPriv->fbBPP / 8,
+ driScrnPriv->fbOrigin,
+ driScrnPriv->fbStride,
+ driDrawPriv);
+ fbSetSpanFunctions(drb, mesaVis);
+ _mesa_add_renderbuffer(mesa_framebuffer,
+ BUFFER_FRONT_LEFT, &drb->Base);
+ }
+ if (mesaVis->doubleBufferMode) {
+ /* XXX what are the correct origin/stride values? */
+ GLvoid *backBuf = _mesa_malloc(driScrnPriv->fbStride
+ * driScrnPriv->fbHeight);
+ driRenderbuffer *drb = driNewRenderbuffer(GL_RGBA,
+ backBuf,
+ driScrnPriv->fbBPP /8,
+ driScrnPriv->fbOrigin,
+ driScrnPriv->fbStride,
+ driDrawPriv);
+ fbSetSpanFunctions(drb, mesaVis);
+ _mesa_add_renderbuffer(mesa_framebuffer,
+ BUFFER_BACK_LEFT, &drb->Base);
+ }
+
+ _mesa_add_soft_renderbuffers(mesa_framebuffer,
+ GL_FALSE, /* color */
+ swDepth,
+ swStencil,
+ swAccum,
+ swAlpha, /* or always zero? */
+ GL_FALSE /* aux */);
+
+ driDrawPriv->driverPrivate = mesa_framebuffer;
+
+ return 1;
+ }
+}
+
+
+static void
+fbDestroyBuffer(__DRIdrawablePrivate *driDrawPriv)
+{
+ _mesa_unreference_framebuffer((GLframebuffer **)(&(driDrawPriv->driverPrivate)));
+}
+
+
+
+/* If the backbuffer is on a videocard, this is extraordinarily slow!
+ */
+static void
+fbSwapBuffers( __DRIdrawablePrivate *dPriv )
+{
+ struct gl_framebuffer *mesa_framebuffer = (struct gl_framebuffer *)dPriv->driverPrivate;
+ struct gl_renderbuffer * front_renderbuffer = mesa_framebuffer->Attachment[BUFFER_FRONT_LEFT].Renderbuffer;
+ void *frontBuffer = front_renderbuffer->Data;
+ int currentPitch = ((driRenderbuffer *)front_renderbuffer)->pitch;
+ void *backBuffer = mesa_framebuffer->Attachment[BUFFER_BACK_LEFT].Renderbuffer->Data;
+
+ if (dPriv->driContextPriv && dPriv->driContextPriv->driverPrivate) {
+ fbContextPtr fbmesa = (fbContextPtr) dPriv->driContextPriv->driverPrivate;
+ GLcontext *ctx = fbmesa->glCtx;
+
+ if (ctx->Visual.doubleBufferMode) {
+ int i;
+ int offset = 0;
+ char *tmp = _mesa_malloc(currentPitch);
+
+ _mesa_notifySwapBuffers( ctx ); /* flush pending rendering commands */
+
+ ASSERT(frontBuffer);
+ ASSERT(backBuffer);
+
+ for (i = 0; i < dPriv->h; i++) {
+ _mesa_memcpy(tmp, (char *) backBuffer + offset,
+ currentPitch);
+ _mesa_memcpy((char *) frontBuffer + offset, tmp,
+ currentPitch);
+ offset += currentPitch;
+ }
+
+ _mesa_free(tmp);
+ }
+ }
+ else {
+ /* XXX this shouldn't be an error but we can't handle it for now */
+ _mesa_problem(NULL, "fbSwapBuffers: drawable has no context!\n");
+ }
+}
+
+
+/* Force the context `c' to be the current context and associate with it
+ * buffer `b'.
+ */
+static GLboolean
+fbMakeCurrent( __DRIcontextPrivate *driContextPriv,
+ __DRIdrawablePrivate *driDrawPriv,
+ __DRIdrawablePrivate *driReadPriv )
+{
+ if ( driContextPriv ) {
+ fbContextPtr newFbCtx =
+ (fbContextPtr) driContextPriv->driverPrivate;
+
+ newFbCtx->dri.drawable = driDrawPriv;
+
+ _mesa_make_current( newFbCtx->glCtx,
+ driDrawPriv->driverPrivate,
+ driReadPriv->driverPrivate);
+ } else {
+ _mesa_make_current( NULL, NULL, NULL );
+ }
+
+ return GL_TRUE;
+}
+
+
+/* Force the context `c' to be unbound from its buffer.
+ */
+static GLboolean
+fbUnbindContext( __DRIcontextPrivate *driContextPriv )
+{
+ return GL_TRUE;
+}
+
+const struct __DriverAPIRec driDriverAPI = {
+ .InitScreen = fbInitScreen,
+ .DestroyScreen = fbDestroyScreen,
+ .CreateContext = fbCreateContext,
+ .DestroyContext = fbDestroyContext,
+ .CreateBuffer = fbCreateBuffer,
+ .DestroyBuffer = fbDestroyBuffer,
+ .SwapBuffers = fbSwapBuffers,
+ .MakeCurrent = fbMakeCurrent,
+ .UnbindContext = fbUnbindContext,
+};
diff --git a/src/VBox/Additions/common/crOpenGL/wgl.c b/src/VBox/Additions/common/crOpenGL/wgl.c
new file mode 100644
index 00000000..fd2ea0a3
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/wgl.c
@@ -0,0 +1,1015 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_error.h"
+#include "cr_spu.h"
+#include "cr_environment.h"
+#include "cr_mem.h"
+#include "stub.h"
+
+/* I *know* most of the parameters are unused, dammit. */
+#pragma warning( disable: 4100 )
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stdio.h>
+
+#include <iprt/cdefs.h>
+
+/* Currently host part will misbehave re-creating context with proper visual bits
+ * if contexts with alternative visual bits is requested.
+ * For now we just report a superset of all visual bits to avoid that.
+ * Better to it on the host side as well?
+ * We could also implement properly multiple pixel formats,
+ * which should be done by implementing offscreen rendering or multiple host contexts.
+ * */
+#define VBOX_CROGL_USE_VBITS_SUPERSET
+
+#ifdef VBOX_CROGL_USE_VBITS_SUPERSET
+static GLuint desiredVisual = CR_RGB_BIT | CR_ALPHA_BIT | CR_DEPTH_BIT | CR_STENCIL_BIT | CR_ACCUM_BIT | CR_DOUBLE_BIT;
+#else
+static GLuint desiredVisual = CR_RGB_BIT;
+#endif
+
+#ifndef VBOX_CROGL_USE_VBITS_SUPERSET
+/**
+ * Compute a mask of CR_*_BIT flags which reflects the attributes of
+ * the pixel format of the given hdc.
+ */
+static GLuint ComputeVisBits( HDC hdc )
+{
+ PIXELFORMATDESCRIPTOR pfd;
+ int iPixelFormat;
+ GLuint b = 0;
+
+ iPixelFormat = GetPixelFormat( hdc );
+
+ DescribePixelFormat( hdc, iPixelFormat, sizeof(pfd), &pfd );
+
+ if (pfd.cDepthBits > 0)
+ b |= CR_DEPTH_BIT;
+ if (pfd.cAccumBits > 0)
+ b |= CR_ACCUM_BIT;
+ if (pfd.cColorBits > 8)
+ b |= CR_RGB_BIT;
+ if (pfd.cStencilBits > 0)
+ b |= CR_STENCIL_BIT;
+ if (pfd.cAlphaBits > 0)
+ b |= CR_ALPHA_BIT;
+ if (pfd.dwFlags & PFD_DOUBLEBUFFER)
+ b |= CR_DOUBLE_BIT;
+ if (pfd.dwFlags & PFD_STEREO)
+ b |= CR_STEREO_BIT;
+
+ return b;
+}
+#endif
+
+DECLEXPORT(int) WINAPI wglChoosePixelFormat_prox( HDC hdc, CONST PIXELFORMATDESCRIPTOR *pfd )
+{
+ DWORD okayFlags;
+
+ CR_DDI_PROLOGUE();
+
+ stubInit();
+
+ /*
+ * NOTE!!!
+ * Here we're telling the renderspu not to use the GDI
+ * equivalent's of ChoosePixelFormat/DescribePixelFormat etc
+ * There are subtle differences in the use of these calls.
+ */
+ crSetenv("CR_WGL_DO_NOT_USE_GDI", "yes");
+
+ if ( pfd->nSize != sizeof(*pfd) || pfd->nVersion != 1 ) {
+ crError( "wglChoosePixelFormat: bad pfd\n" );
+ return 0;
+ }
+
+ okayFlags = ( PFD_DRAW_TO_WINDOW |
+ PFD_SUPPORT_GDI |
+ PFD_SUPPORT_OPENGL |
+ PFD_DOUBLEBUFFER |
+ PFD_DOUBLEBUFFER_DONTCARE |
+ PFD_SWAP_EXCHANGE |
+ PFD_SWAP_COPY |
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+// PFD_STEREO |
+ PFD_STEREO_DONTCARE |
+ PFD_DEPTH_DONTCARE );
+ if ( pfd->dwFlags & ~okayFlags ) {
+ crWarning( "wglChoosePixelFormat: only support flags=0x%x, but you gave me flags=0x%x", okayFlags, pfd->dwFlags );
+ return 0;
+ }
+
+ if ( pfd->iPixelType != PFD_TYPE_RGBA ) {
+ crError( "wglChoosePixelFormat: only support RGBA\n" );
+ }
+
+ if ( pfd->cColorBits > 32 ||
+ pfd->cRedBits > 8 ||
+ pfd->cGreenBits > 8 ||
+ pfd->cBlueBits > 8 ||
+ pfd->cAlphaBits > 8 ) {
+ crWarning( "wglChoosePixelFormat: too much color precision requested\n" );
+ }
+
+ if ( pfd->dwFlags & PFD_DOUBLEBUFFER )
+ desiredVisual |= CR_DOUBLE_BIT;
+
+ if ( pfd->dwFlags & PFD_STEREO )
+ desiredVisual |= CR_STEREO_BIT;
+
+ if ( pfd->cColorBits > 8)
+ desiredVisual |= CR_RGB_BIT;
+
+ if ( pfd->cAccumBits > 0 ||
+ pfd->cAccumRedBits > 0 ||
+ pfd->cAccumGreenBits > 0 ||
+ pfd->cAccumBlueBits > 0 ||
+ pfd->cAccumAlphaBits > 0 ) {
+ crWarning( "wglChoosePixelFormat: asked for accumulation buffer, ignoring\n" );
+ }
+
+ if ( pfd->cAccumBits > 0 )
+ desiredVisual |= CR_ACCUM_BIT;
+
+ if ( pfd->cDepthBits > 32 ) {
+ crError( "wglChoosePixelFormat; asked for too many depth bits\n" );
+ }
+
+ if ( pfd->cDepthBits > 0 )
+ desiredVisual |= CR_DEPTH_BIT;
+
+ if ( pfd->cStencilBits > 8 ) {
+ crError( "wglChoosePixelFormat: asked for too many stencil bits\n" );
+ }
+
+ if ( pfd->cStencilBits > 0 )
+ desiredVisual |= CR_STENCIL_BIT;
+
+ if ( pfd->cAuxBuffers > 0 ) {
+ crError( "wglChoosePixelFormat: asked for aux buffers\n" );
+ }
+
+ if ( pfd->iLayerType != PFD_MAIN_PLANE ) {
+ crError( "wglChoosePixelFormat: asked for a strange layer\n" );
+ }
+
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglSetPixelFormat_prox( HDC hdc, int pixelFormat,
+ CONST PIXELFORMATDESCRIPTOR *pdf )
+{
+ CR_DDI_PROLOGUE();
+
+ if ( pixelFormat != 1 ) {
+ crError( "wglSetPixelFormat: pixelFormat=%d?\n", pixelFormat );
+ }
+
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglDeleteContext_prox( HGLRC hglrc )
+{
+ CR_DDI_PROLOGUE();
+ stubDestroyContext( (unsigned long) hglrc );
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglMakeCurrent_prox( HDC hdc, HGLRC hglrc )
+{
+ ContextInfo *context;
+ WindowInfo *window;
+ BOOL ret;
+
+ CR_DDI_PROLOGUE();
+
+ crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+ window = stubGetWindowInfo(hdc);
+
+ if (hglrc!=0 && !context)
+ {
+ crWarning("wglMakeCurrent got unexpected hglrc 0x%x", hglrc);
+ }
+
+ ret = stubMakeCurrent( window, context );
+
+ crHashtableUnlock(stub.contextTable);
+ crHashtableUnlock(stub.windowTable);
+
+ return ret;
+}
+
+DECLEXPORT(HGLRC) WINAPI wglGetCurrentContext_prox( void )
+{
+ ContextInfo *context = stubGetCurrentContext();
+ CR_DDI_PROLOGUE();
+ return (HGLRC) (context ? context->id : 0);
+}
+
+DECLEXPORT(HDC) WINAPI wglGetCurrentDC_prox( void )
+{
+ ContextInfo *context = stubGetCurrentContext();
+ CR_DDI_PROLOGUE();
+ if (context && context->currentDrawable)
+ return (HDC) context->currentDrawable->drawable;
+ else
+ return (HDC) NULL;
+}
+
+DECLEXPORT(int) WINAPI wglGetPixelFormat_prox( HDC hdc )
+{
+ CR_DDI_PROLOGUE();
+ /* this is what we call our generic pixelformat, regardless of the HDC */
+ return 1;
+}
+
+DECLEXPORT(int) WINAPI wglDescribePixelFormat_prox( HDC hdc, int pixelFormat, UINT nBytes,
+ LPPIXELFORMATDESCRIPTOR pfd )
+{
+ CR_DDI_PROLOGUE();
+
+/* if ( pixelFormat != 1 ) {
+ * crError( "wglDescribePixelFormat: pixelFormat=%d?\n", pixelFormat );
+ * return 0;
+ * } */
+
+ if ( !pfd ) {
+ crWarning( "wglDescribePixelFormat: pfd=NULL\n" );
+ return 1; /* There's only one, baby */
+ }
+
+ if ( nBytes != sizeof(*pfd) ) {
+ crWarning( "wglDescribePixelFormat: nBytes=%u?\n", nBytes );
+ return 1; /* There's only one, baby */
+ }
+
+ pfd->nSize = sizeof(*pfd);
+ pfd->nVersion = 1;
+ pfd->dwFlags = ( PFD_DRAW_TO_WINDOW |
+ PFD_SUPPORT_GDI |
+ PFD_SUPPORT_OPENGL |
+ PFD_DOUBLEBUFFER );
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 32;
+ pfd->cRedBits = 8;
+ pfd->cRedShift = 24;
+ pfd->cGreenBits = 8;
+ pfd->cGreenShift = 16;
+ pfd->cBlueBits = 8;
+ pfd->cBlueShift = 8;
+ pfd->cAlphaBits = 8;
+ pfd->cAlphaShift = 0;
+ pfd->cAccumBits = 0;
+ pfd->cAccumRedBits = 0;
+ pfd->cAccumGreenBits = 0;
+ pfd->cAccumBlueBits = 0;
+ pfd->cAccumAlphaBits = 0;
+ pfd->cDepthBits = 32;
+ pfd->cStencilBits = 8;
+ pfd->cAuxBuffers = 0;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+ pfd->bReserved = 0;
+ pfd->dwLayerMask = 0;
+ pfd->dwVisibleMask = 0;
+ pfd->dwDamageMask = 0;
+
+ /* the max PFD index */
+ return 1;
+}
+
+DECLEXPORT(void) WINAPI VBoxCtxChromiumParameteriCR(HGLRC hglrc, GLenum param, GLint value)
+{
+ ContextInfo *context;
+
+ CR_DDI_PROLOGUE();
+
+// crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+
+ if (context)
+ {
+ stubCtxCheckCreate(context);
+ stubConChromiumParameteriCR(CR_CTX_CON(context), param, value);
+ }
+ else
+ crWarning("invalid context %#x", hglrc);
+
+ crHashtableUnlock(stub.contextTable);
+// crHashtableUnlock(stub.windowTable);
+}
+
+DECLEXPORT(BOOL) WINAPI wglShareLists_prox( HGLRC hglrc1, HGLRC hglrc2 )
+{
+ ContextInfo *context1, *context2;
+ GLint aSpuContexts[2];
+
+ CR_DDI_PROLOGUE();
+
+// crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context1 = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc1);
+
+ if (!context1)
+ {
+ WARN(("invalid hglrc1"));
+ return FALSE;
+ }
+
+ stubCtxCheckCreate(context1);
+
+ context2 = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc2);
+
+ if (!context2)
+ {
+ WARN(("invalid hglrc2"));
+ return FALSE;
+ }
+
+ stubCtxCheckCreate(context2);
+
+ aSpuContexts[0] = context1->spuContext;
+ aSpuContexts[1] = context2->spuContext;
+
+ stubConChromiumParametervCR(CR_CTX_CON(context2), GL_SHARE_LISTS_CR, GL_INT, 2, aSpuContexts);
+
+ crHashtableUnlock(stub.contextTable);
+
+ return TRUE;
+}
+
+DECLEXPORT(HGLRC) WINAPI VBoxCreateContext( HDC hdc, struct VBOXUHGSMI *pHgsmi )
+{
+ char *dpyName;
+ ContextInfo *context;
+
+ CR_DDI_PROLOGUE();
+
+ stubInit();
+
+ CRASSERT(stub.contextTable);
+
+ dpyName = crCalloc(MAX_DPY_NAME);
+ if (dpyName)
+ {
+ crMemset(dpyName, 0, MAX_DPY_NAME);
+ sprintf(dpyName, "%p", hdc);
+ }
+#ifndef VBOX_CROGL_USE_VBITS_SUPERSET
+ if (stub.haveNativeOpenGL)
+ desiredVisual |= ComputeVisBits( hdc );
+#endif
+
+ context = stubNewContext(dpyName, desiredVisual, UNDECIDED, 0
+#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
+ , pHgsmi
+#else
+ , NULL
+#endif
+ );
+ /* Not needed any more. */
+ crFree(dpyName);
+
+ if (!context)
+ return 0;
+
+ return (HGLRC) context->id;
+}
+
+DECLEXPORT(GLint) WINAPI VBoxGetWindowId( HDC hdc )
+{
+ WindowInfo *window;
+ GLint winid = 0;
+
+ CR_DDI_PROLOGUE();
+
+ crHashtableLock(stub.windowTable);
+
+ window = stubGetWindowInfo(hdc);
+ if (!window)
+ {
+ crWarning("stubGetWindowInfo: window not found!");
+ goto end;
+ }
+ if (!window->spuWindow)
+ {
+ crWarning("stubGetWindowInfo: window is null!");
+ goto end;
+ }
+
+ winid = window->spuWindow;
+
+end:
+ crHashtableUnlock(stub.windowTable);
+ return winid;
+}
+
+DECLEXPORT(GLint) WINAPI VBoxGetContextId( HGLRC hglrc )
+{
+ ContextInfo *context;
+ GLint ctxid = 0;
+
+ CR_DDI_PROLOGUE();
+
+// crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+ if (!context)
+ {
+ crWarning("crHashtableSearch: context not found!");
+ goto end;
+ }
+
+ if (context->type != CHROMIUM)
+ {
+ crWarning("unexpected context type %d", context->type);
+ goto end;
+ }
+
+ if (context->spuContext <= 0)
+ {
+ crWarning("no spuSontext defined");
+ goto end;
+ }
+
+ ctxid = context->spuContext;
+
+end:
+ crHashtableUnlock(stub.contextTable);
+ return ctxid;
+}
+
+
+DECLEXPORT(HGLRC) WINAPI wglCreateContext_prox( HDC hdc )
+{
+ return VBoxCreateContext(hdc, NULL);
+}
+
+DECLEXPORT(void) WINAPI VBoxFlushToHost ( HGLRC hglrc )
+{
+ ContextInfo *context;
+
+ CR_DDI_PROLOGUE();
+
+// crHashtableLock(stub.windowTable);
+ crHashtableLock(stub.contextTable);
+
+ context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc);
+
+ if (context)
+ stubConFlush(CR_CTX_CON(context));
+ else
+ crWarning("invalid context %#x", hglrc);
+
+ crHashtableUnlock(stub.contextTable);
+// crHashtableUnlock(stub.windowTable);
+}
+
+DECLEXPORT(BOOL) WINAPI
+wglSwapBuffers_prox( HDC hdc )
+{
+ WindowInfo *window = stubGetWindowInfo(hdc);
+ CR_DDI_PROLOGUE();
+ stubSwapBuffers( window, 0 );
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglCopyContext_prox( HGLRC src, HGLRC dst, UINT mask )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglCopyContext: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(HGLRC) WINAPI wglCreateLayerContext_prox( HDC hdc, int layerPlane )
+{
+ CR_DDI_PROLOGUE();
+ stubInit();
+ crWarning( "wglCreateLayerContext: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(PROC) WINAPI wglGetProcAddress_prox( LPCSTR name )
+{
+ CR_DDI_PROLOGUE();
+ return (PROC) crGetProcAddress( name );
+}
+
+DECLEXPORT(BOOL) WINAPI wglUseFontBitmapsA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglUseFontBitmapsA: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglUseFontBitmapsW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglUseFontBitmapsW: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglDescribeLayerPlane_prox( HDC hdc, int pixelFormat, int layerPlane,
+ UINT nBytes, LPLAYERPLANEDESCRIPTOR lpd )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglDescribeLayerPlane: unimplemented" );
+ return 0;
+}
+
+DECLEXPORT(int) WINAPI wglSetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start,
+ int entries, CONST COLORREF *cr )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglSetLayerPaletteEntries: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(int) WINAPI wglGetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start,
+ int entries, COLORREF *cr )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglGetLayerPaletteEntries: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglRealizeLayerPalette_prox( HDC hdc, int layerPlane, BOOL realize )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglRealizeLayerPalette: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(DWORD) WINAPI wglSwapMultipleBuffers_prox( UINT a, CONST void *b )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglSwapMultipleBuffer: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglUseFontOutlinesA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase,
+ FLOAT deviation, FLOAT extrusion, int format,
+ LPGLYPHMETRICSFLOAT gmf )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglUseFontOutlinesA: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglUseFontOutlinesW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase,
+ FLOAT deviation, FLOAT extrusion, int format,
+ LPGLYPHMETRICSFLOAT gmf )
+{
+ CR_DDI_PROLOGUE();
+ crWarning( "wglUseFontOutlinesW: unsupported" );
+ return 0;
+}
+
+DECLEXPORT(BOOL) WINAPI wglSwapLayerBuffers_prox( HDC hdc, UINT planes )
+{
+ CR_DDI_PROLOGUE();
+ if (planes == WGL_SWAP_MAIN_PLANE)
+ {
+ return wglSwapBuffers_prox(hdc);
+ }
+ else
+ {
+ crWarning( "wglSwapLayerBuffers: unsupported" );
+ return 0;
+ }
+}
+
+DECLEXPORT(BOOL) WINAPI wglChoosePixelFormatEXT_prox
+(HDC hdc, const int *piAttributes, const FLOAT *pfAttributes, UINT nMaxFormats, int *piFormats, UINT *nNumFormats)
+{
+ int *pi;
+ int wants_rgb = 0;
+
+ CR_DDI_PROLOGUE();
+
+ stubInit();
+
+ /** @todo : Need to check pfAttributes too ! */
+
+ for ( pi = (int *)piAttributes; *pi != 0; pi++ )
+ {
+ switch ( *pi )
+ {
+ case WGL_COLOR_BITS_EXT:
+ if (pi[1] > 8)
+ wants_rgb = 1;
+ pi++;
+ break;
+
+ case WGL_RED_BITS_EXT:
+ case WGL_GREEN_BITS_EXT:
+ case WGL_BLUE_BITS_EXT:
+ if (pi[1] > 3)
+ wants_rgb = 1;
+ pi++;
+ break;
+
+ case WGL_ACCUM_ALPHA_BITS_EXT:
+ case WGL_ALPHA_BITS_EXT:
+ if (pi[1] > 0)
+ desiredVisual |= CR_ALPHA_BIT;
+ pi++;
+ break;
+
+ case WGL_DOUBLE_BUFFER_EXT:
+ if (pi[1] > 0)
+ desiredVisual |= CR_DOUBLE_BIT;
+ pi++;
+ break;
+
+ case WGL_STEREO_EXT:
+ if (pi[1] > 0)
+ {
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ crWarning("WGL_STEREO_EXT not supporteed!");
+ return 0;
+// desiredVisual |= CR_STEREO_BIT;
+ }
+ pi++;
+ break;
+
+ case WGL_DEPTH_BITS_EXT:
+ if (pi[1] > 0)
+ desiredVisual |= CR_DEPTH_BIT;
+ pi++;
+ break;
+
+ case WGL_STENCIL_BITS_EXT:
+ if (pi[1] > 0)
+ desiredVisual |= CR_STENCIL_BIT;
+ pi++;
+ break;
+
+ case WGL_ACCUM_RED_BITS_EXT:
+ case WGL_ACCUM_GREEN_BITS_EXT:
+ case WGL_ACCUM_BLUE_BITS_EXT:
+ if (pi[1] > 0)
+ desiredVisual |= CR_ACCUM_BIT;
+ pi++;
+ break;
+
+ case WGL_SAMPLE_BUFFERS_EXT:
+ case WGL_SAMPLES_EXT:
+ if (pi[1] > 0)
+ {
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ crWarning("WGL_SAMPLE_BUFFERS_EXT & WGL_SAMPLES_EXT not supporteed!");
+ return 0;
+// desiredVisual |= CR_MULTISAMPLE_BIT;
+ }
+ pi++;
+ break;
+
+ case WGL_SUPPORT_OPENGL_ARB:
+ case WGL_DRAW_TO_WINDOW_ARB:
+ case WGL_ACCELERATION_ARB:
+ pi++;
+ break;
+
+ case WGL_PIXEL_TYPE_ARB:
+ if(pi[1]!=WGL_TYPE_RGBA_ARB)
+ {
+ crWarning("WGL_PIXEL_TYPE 0x%x not supported!", pi[1]);
+ return 0;
+ }
+ pi++;
+ break;
+
+ default:
+ crWarning( "wglChoosePixelFormatEXT: bad pi=0x%x", *pi );
+ return 0;
+ }
+ }
+
+ if (nNumFormats) *nNumFormats = 1;
+ if (nMaxFormats>0 && piFormats)
+ {
+ piFormats[0] = 1;
+ }
+
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglGetPixelFormatAttribivEXT_prox
+(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *pValues)
+{
+ UINT i;
+
+ CR_DDI_PROLOGUE();
+
+ if (!pValues || !piAttributes) return 0;
+
+ if ((nAttributes!=1) || (piAttributes && piAttributes[0]!=WGL_NUMBER_PIXEL_FORMATS_ARB))
+ {
+ if (iPixelFormat!=1)
+ {
+ crDebug("wglGetPixelFormatAttribivARB: bad pf:%i", iPixelFormat);
+ return 0;
+ }
+ }
+
+ for (i=0; i<nAttributes; ++i)
+ {
+ switch (piAttributes[i])
+ {
+ case WGL_NUMBER_PIXEL_FORMATS_ARB:
+ pValues[i] = 1;
+ break;
+ case WGL_DRAW_TO_WINDOW_ARB:
+ case WGL_SUPPORT_OPENGL_ARB:
+ case WGL_DOUBLE_BUFFER_ARB:
+ pValues[i] = 1;
+ break;
+ case WGL_STEREO_ARB:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0;
+ break;
+ case WGL_DRAW_TO_BITMAP_ARB:
+ case WGL_NEED_PALETTE_ARB:
+ case WGL_NEED_SYSTEM_PALETTE_ARB:
+ case WGL_SWAP_LAYER_BUFFERS_ARB:
+ case WGL_NUMBER_OVERLAYS_ARB:
+ case WGL_NUMBER_UNDERLAYS_ARB:
+ case WGL_TRANSPARENT_ARB:
+ case WGL_TRANSPARENT_RED_VALUE_ARB:
+ case WGL_TRANSPARENT_GREEN_VALUE_ARB:
+ case WGL_TRANSPARENT_BLUE_VALUE_ARB:
+ case WGL_TRANSPARENT_ALPHA_VALUE_ARB:
+ case WGL_TRANSPARENT_INDEX_VALUE_ARB:
+ case WGL_SHARE_DEPTH_ARB:
+ case WGL_SHARE_STENCIL_ARB:
+ case WGL_SHARE_ACCUM_ARB:
+ case WGL_SUPPORT_GDI_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCELERATION_ARB:
+ pValues[i] = WGL_FULL_ACCELERATION_ARB;
+ break;
+ case WGL_SWAP_METHOD_ARB:
+ pValues[i] = WGL_SWAP_UNDEFINED_ARB;
+ break;
+ case WGL_PIXEL_TYPE_ARB:
+ pValues[i] = WGL_TYPE_RGBA_ARB;
+ break;
+ case WGL_COLOR_BITS_ARB:
+ pValues[i] = 32;
+ break;
+ case WGL_RED_BITS_ARB:
+ case WGL_GREEN_BITS_ARB:
+ case WGL_BLUE_BITS_ARB:
+ case WGL_ALPHA_BITS_ARB:
+ pValues[i] = 8;
+ break;
+ case WGL_RED_SHIFT_ARB:
+ pValues[i] = 24;
+ break;
+ case WGL_GREEN_SHIFT_ARB:
+ pValues[i] = 16;
+ break;
+ case WGL_BLUE_SHIFT_ARB:
+ pValues[i] = 8;
+ break;
+ case WGL_ALPHA_SHIFT_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCUM_BITS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCUM_RED_BITS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCUM_GREEN_BITS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCUM_BLUE_BITS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_ACCUM_ALPHA_BITS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_DEPTH_BITS_ARB:
+ pValues[i] = 32;
+ break;
+ case WGL_STENCIL_BITS_ARB:
+ pValues[i] = 8;
+ break;
+ case WGL_AUX_BUFFERS_ARB:
+ pValues[i] = 0;
+ break;
+ case WGL_SAMPLE_BUFFERS_EXT:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0;
+ break;
+ case WGL_SAMPLES_EXT:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0;
+ break;
+ case 0x202d: /* <- WGL_DRAW_TO_PBUFFER_ARB this is to make VSG Open Inventor happy */
+ pValues[i] = 0;
+ break;
+ default:
+ crWarning("wglGetPixelFormatAttribivARB: bad attrib=0x%x", piAttributes[i]);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglGetPixelFormatAttribfvEXT_prox
+(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, float *pValues)
+{
+ UINT i;
+
+ CR_DDI_PROLOGUE();
+
+ if (!pValues || !piAttributes) return 0;
+
+ if ((nAttributes!=1) || (piAttributes && piAttributes[0]!=WGL_NUMBER_PIXEL_FORMATS_ARB))
+ {
+ if (iPixelFormat!=1)
+ {
+ crDebug("wglGetPixelFormatAttribivARB: bad pf:%i", iPixelFormat);
+ return 0;
+ }
+ }
+
+ for (i=0; i<nAttributes; ++i)
+ {
+ switch (piAttributes[i])
+ {
+ case WGL_NUMBER_PIXEL_FORMATS_ARB:
+ pValues[i] = 1.f;
+ break;
+ case WGL_DRAW_TO_WINDOW_ARB:
+ case WGL_SUPPORT_OPENGL_ARB:
+ case WGL_DOUBLE_BUFFER_ARB:
+ pValues[i] = 1.f;
+ break;
+ case WGL_STEREO_ARB:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0.f;
+ break;
+ case WGL_DRAW_TO_BITMAP_ARB:
+ case WGL_NEED_PALETTE_ARB:
+ case WGL_NEED_SYSTEM_PALETTE_ARB:
+ case WGL_SWAP_LAYER_BUFFERS_ARB:
+ case WGL_NUMBER_OVERLAYS_ARB:
+ case WGL_NUMBER_UNDERLAYS_ARB:
+ case WGL_TRANSPARENT_ARB:
+ case WGL_TRANSPARENT_RED_VALUE_ARB:
+ case WGL_TRANSPARENT_GREEN_VALUE_ARB:
+ case WGL_TRANSPARENT_BLUE_VALUE_ARB:
+ case WGL_TRANSPARENT_ALPHA_VALUE_ARB:
+ case WGL_TRANSPARENT_INDEX_VALUE_ARB:
+ case WGL_SHARE_DEPTH_ARB:
+ case WGL_SHARE_STENCIL_ARB:
+ case WGL_SHARE_ACCUM_ARB:
+ case WGL_SUPPORT_GDI_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCELERATION_ARB:
+ pValues[i] = WGL_FULL_ACCELERATION_ARB;
+ break;
+ case WGL_SWAP_METHOD_ARB:
+ pValues[i] = WGL_SWAP_UNDEFINED_ARB;
+ break;
+ case WGL_PIXEL_TYPE_ARB:
+ pValues[i] = WGL_TYPE_RGBA_ARB;
+ break;
+ case WGL_COLOR_BITS_ARB:
+ pValues[i] = 32.f;
+ break;
+ case WGL_RED_BITS_ARB:
+ case WGL_GREEN_BITS_ARB:
+ case WGL_BLUE_BITS_ARB:
+ case WGL_ALPHA_BITS_ARB:
+ pValues[i] = 8.f;
+ break;
+ case WGL_RED_SHIFT_ARB:
+ pValues[i] = 24.f;
+ break;
+ case WGL_GREEN_SHIFT_ARB:
+ pValues[i] = 16.f;
+ break;
+ case WGL_BLUE_SHIFT_ARB:
+ pValues[i] = 8.f;
+ break;
+ case WGL_ALPHA_SHIFT_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCUM_BITS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCUM_RED_BITS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCUM_GREEN_BITS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCUM_BLUE_BITS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_ACCUM_ALPHA_BITS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_DEPTH_BITS_ARB:
+ pValues[i] = 32.f;
+ break;
+ case WGL_STENCIL_BITS_ARB:
+ pValues[i] = 8.f;
+ break;
+ case WGL_AUX_BUFFERS_ARB:
+ pValues[i] = 0.f;
+ break;
+ case WGL_SAMPLE_BUFFERS_EXT:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0.f;
+ break;
+ case WGL_SAMPLES_EXT:
+ /** @todo this is disabled due to VSG Open Inventor interop issues
+ * it does not make any sense actually since reporting this
+ * as well as choosing a pixel format with this cap would not do anything
+ * since ICD stuff has its own pixelformat state var */
+ pValues[i] = 0.f;
+ break;
+ case 0x202d: /* <- WGL_DRAW_TO_PBUFFER_ARB this is to make VSG Open Inventor happy */
+ pValues[i] = 0.f;
+ break;
+ default:
+ crWarning("wglGetPixelFormatAttribivARB: bad attrib=0x%x", piAttributes[i]);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+DECLEXPORT(BOOL) WINAPI wglSwapIntervalEXT_prox(int interval)
+{
+ CR_DDI_PROLOGUE();
+ return TRUE;
+}
+
+DECLEXPORT(int) WINAPI wglGetSwapIntervalEXT_prox()
+{
+ CR_DDI_PROLOGUE();
+ return 1;
+}
+
+static GLubyte *gsz_wgl_extensions = "WGL_EXT_pixel_format WGL_ARB_pixel_format WGL_ARB_multisample";
+
+DECLEXPORT(const GLubyte *) WINAPI wglGetExtensionsStringEXT_prox()
+{
+ CR_DDI_PROLOGUE();
+ return gsz_wgl_extensions;
+}
+
+DECLEXPORT(const GLubyte *) WINAPI wglGetExtensionsStringARB_prox(HDC hdc)
+{
+ CR_DDI_PROLOGUE();
+ (void) hdc;
+
+ return gsz_wgl_extensions;
+}
diff --git a/src/VBox/Additions/common/crOpenGL/windows_exports.py b/src/VBox/Additions/common/crOpenGL/windows_exports.py
new file mode 100755
index 00000000..974d482e
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/windows_exports.py
@@ -0,0 +1,98 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ apiutil.CopyrightC()
+
+ print('#include "chromium.h"')
+ print('#include "stub.h"')
+ print('')
+ print('#define NAKED __declspec(naked)')
+ print('#define UNUSED(x) ((void)(x))')
+ print('')
+
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+
+ print("NAKED %s cr_gl%s(%s)" % (return_type, func_name,
+ apiutil.MakeDeclarationString( params )))
+ print("{")
+ print("\t__asm jmp [glim.%s]" % func_name)
+ for (name, type, vecSize) in params:
+ print("\tUNUSED(%s);" % name)
+ print("}")
+ print("")
+
+ print('/*')
+ print('* Aliases')
+ print('*/')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print("NAKED %s cr_gl%s(%s)" % (return_type, func_name,
+ apiutil.MakeDeclarationString( params )))
+ print("{")
+ print("\t__asm jmp [glim.%s]" % alias)
+ for (name, type, vecSize) in params:
+ print("\tUNUSED(%s);" % name)
+ print("}")
+ print("")
+
+
+ print('/*')
+ print('* No-op stubs')
+ print('*/')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print("NAKED %s cr_gl%s(%s)" % (return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print("{")
+ if return_type != "void":
+ print("return (%s) 0" % return_type)
+ print("}")
+ print("")
+
+
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py b/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py
new file mode 100755
index 00000000..0899d9d9
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py
@@ -0,0 +1,171 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE GENERATED BY THE getprocaddress.py SCRIPT */
+#include "chromium.h"
+#include "cr_string.h"
+#include "cr_version.h"
+#include "stub.h"
+#include "icd_drv.h"
+#include "cr_gl.h"
+#include "cr_error.h"
+
+#ifdef WINDOWS
+#pragma warning( disable: 4055 )
+#endif
+
+""")
+
+print("""
+struct name_address {
+ const char *name;
+ CR_PROC address;
+};
+
+PROC WINAPI wglGetProcAddress_prox( LPCSTR name );
+
+static struct name_address functions[] = {
+""")
+
+
+keys = apiutil.GetAllFunctionsAndOmittedAliases(sys.argv[1]+"/APIspec.txt")
+for func_name in keys:
+ if "Chromium" == apiutil.Category(func_name):
+ continue
+ if "VBox" == apiutil.Category(func_name):
+ continue
+ if func_name == "BoundsInfoCR":
+ continue
+ if "GL_chromium" == apiutil.Category(func_name):
+ pass #continue
+
+ # alias is the function we're aliasing
+ proc_name = func_name
+ if "omit" in apiutil.ChromiumProps(func_name):
+ alias = apiutil.Alias(func_name)
+ if alias:
+ proc_name = alias
+
+ wrap = apiutil.GetCategoryWrapper(func_name)
+ name = "gl" + func_name
+ address = "cr_gl" + proc_name
+ if wrap:
+ print('#ifdef CR_%s' % wrap)
+ print('\t{ "%s", (CR_PROC) %s },' % (name, address))
+ if wrap:
+ print('#endif')
+
+
+print("\t/* Chromium binding/glue functions */")
+
+for func_name in keys:
+ if (func_name == "Writeback" or
+ func_name == "BoundsInfoCR" or
+ func_name == "GetUniformsLocations" or
+ func_name == "GetAttribsLocations"):
+ continue
+ if apiutil.Category(func_name) == "Chromium":
+ print('\t{ "cr%s", (CR_PROC) cr%s },' % (func_name, func_name))
+
+print("\t/* Windows ICD functions */")
+
+for func_name in ( "CopyContext",
+ "CreateContext",
+ "CreateLayerContext",
+ "DeleteContext",
+ "DescribeLayerPlane",
+ "DescribePixelFormat",
+ "GetLayerPaletteEntries",
+ "RealizeLayerPalette",
+ "SetLayerPaletteEntries",
+ "SetPixelFormat",
+ "ShareLists",
+ "SwapBuffers",
+ "SwapLayerBuffers",
+ "ReleaseContext",
+ "SetContext",
+ "ValidateVersion"):
+ print('\t{ "Drv%s", (CR_PROC) Drv%s },' % (func_name, func_name))
+
+print('\t{ "DrvGetProcAddress", (CR_PROC) wglGetProcAddress_prox },')
+
+print("""
+ { NULL, NULL }
+};
+
+extern const GLubyte * WINAPI wglGetExtensionsStringEXT_prox(void);
+extern const GLubyte * WINAPI wglGetExtensionsStringARB_prox(HDC hdc);
+extern BOOL WINAPI wglChoosePixelFormatEXT_prox(HDC hdc, const int *piAttributes, const FLOAT *pfAttributes, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
+extern BOOL WINAPI wglGetPixelFormatAttribivEXT_prox(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *pValues);
+extern BOOL WINAPI wglGetPixelFormatAttribfvEXT_prox(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, float *pValues);
+
+BOOL WINAPI wglSwapIntervalEXT(int interval)
+{
+ return false;
+}
+
+CR_PROC CR_APIENTRY crGetProcAddress( const char *name )
+{
+ int i;
+ wglGetExtensionsStringEXTFunc_t wglGetExtensionsStringEXT = wglGetExtensionsStringEXT_prox;
+ wglGetExtensionsStringARBFunc_t wglGetExtensionsStringARB = wglGetExtensionsStringARB_prox;
+ wglChoosePixelFormatEXTFunc_t wglChoosePixelFormatEXT = wglChoosePixelFormatEXT_prox;
+ wglGetPixelFormatAttribivEXTFunc_t wglGetPixelFormatAttribivEXT = wglGetPixelFormatAttribivEXT_prox;
+ wglGetPixelFormatAttribfvEXTFunc_t wglGetPixelFormatAttribfvEXT = wglGetPixelFormatAttribfvEXT_prox;
+
+ stubInit();
+
+ for (i = 0; functions[i].name; i++) {
+ if (crStrcmp(name, functions[i].name) == 0) {
+ /*crDebug("crGetProcAddress(%s) returns %p", name, functions[i].address);*/
+ return functions[i].address;
+ }
+ }
+
+ if (!crStrcmp(name, "wglGetExtensionsStringEXT")) return (CR_PROC) wglGetExtensionsStringEXT;
+ if (!crStrcmp(name, "wglGetExtensionsStringARB")) return (CR_PROC) wglGetExtensionsStringARB;
+
+ if (!crStrcmp(name, "wglChoosePixelFormatEXT")) return (CR_PROC) wglChoosePixelFormatEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribivEXT")) return (CR_PROC) wglGetPixelFormatAttribivEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribfvEXT")) return (CR_PROC) wglGetPixelFormatAttribfvEXT;
+
+ if (!crStrcmp(name, "wglChoosePixelFormatARB")) return (CR_PROC) wglChoosePixelFormatEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribivARB")) return (CR_PROC) wglGetPixelFormatAttribivEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribfvARB")) return (CR_PROC) wglGetPixelFormatAttribfvEXT;
+
+ if (!crStrcmp(name, "wglSwapIntervalEXT")) return (CR_PROC) wglSwapIntervalEXT;
+
+ crDebug("Returning GetProcAddress:NULL for %s", name);
+ return NULL;
+}
+
+""")
+
+
+
+# XXX should crGetProcAddress really handle WGL/GLX functions???
+print_foo = """
+/* As these are Windows specific (i.e. wgl), define these now.... */
+#ifdef WINDOWS
+ {
+ wglGetExtensionsStringEXTFunc_t wglGetExtensionsStringEXT = NULL;
+ wglChoosePixelFormatFunc_t wglChoosePixelFormatEXT = NULL;
+ wglGetPixelFormatAttribivEXTFunc_t wglGetPixelFormatAttribivEXT = NULL;
+ wglGetPixelFormatAttribfvEXTFunc_t wglGetPixelFormatAttribfvEXT = NULL;
+ if (!crStrcmp(name, "wglGetExtensionsStringEXT")) return (CR_PROC) wglGetExtensionsStringEXT;
+ if (!crStrcmp(name, "wglChoosePixelFormatEXT")) return (CR_PROC) wglChoosePixelFormatEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribivEXT")) return (CR_PROC) wglGetPixelFormatAttribivEXT;
+ if (!crStrcmp(name, "wglGetPixelFormatAttribfvEXT")) return (CR_PROC) wglGetPixelFormatAttribfvEXT;
+ }
+#endif
+"""
diff --git a/src/VBox/Additions/common/crOpenGL/windows_i386_exports.py b/src/VBox/Additions/common/crOpenGL/windows_i386_exports.py
new file mode 100755
index 00000000..5a4e744e
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/windows_i386_exports.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+def GenerateEntrypoints():
+
+ #apiutil.CopyrightC()
+ print('%include "iprt/asmdefs.mac"')
+ print("")
+ print("%ifdef RT_ARCH_AMD64")
+ print("extern glim")
+ print("%else ; X86")
+ print("extern _glim")
+ print("%endif")
+ print("")
+
+ # Get sorted list of dispatched functions.
+ # The order is very important - it must match cr_opcodes.h
+ # and spu_dispatch_table.h
+ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+ for index in range(len(keys)):
+ func_name = keys[index]
+ if apiutil.Category(func_name) == "Chromium":
+ continue
+ if apiutil.Category(func_name) == "VBox":
+ continue
+
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword _glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; Aliases')
+ print(';')
+
+ # Now loop over all the functions and take care of any aliases
+ allkeys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt")
+ for func_name in allkeys:
+ if "omit" in apiutil.ChromiumProps(func_name):
+ continue
+
+ if func_name in keys:
+ # we already processed this function earlier
+ continue
+
+ # alias is the function we're aliasing
+ alias = apiutil.Alias(func_name)
+ if alias:
+ # this dict lookup should never fail (raise an exception)!
+ index = keys.index(alias)
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("%ifdef RT_ARCH_AMD64")
+ print("\tmov \trax, qword glim+%d" % (8*index))
+ print("\tjmp \t[rax]")
+ print("%else ; X86")
+ print("\tmov \teax, dword _glim+%d" % (4*index))
+ print("\tjmp \t[eax]")
+ print("%endif")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+ print(';')
+ print('; No-op stubs')
+ print(';')
+
+ # Now generate no-op stub functions
+ for func_name in allkeys:
+ if "stub" in apiutil.ChromiumProps(func_name):
+ print("BEGINPROC_EXPORTED cr_gl%s" % func_name)
+ print("\tleave")
+ print("\tret")
+ print("ENDPROC cr_gl%s" % func_name)
+ print("")
+
+
+GenerateEntrypoints()
+
diff --git a/src/VBox/Additions/common/crOpenGL/xfont.c b/src/VBox/Additions/common/crOpenGL/xfont.c
new file mode 100644
index 00000000..9451fa4a
--- /dev/null
+++ b/src/VBox/Additions/common/crOpenGL/xfont.c
@@ -0,0 +1,244 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "stub.h"
+
+/** code borrowed from Mesa */
+
+
+/** Fill a BITMAP with a character C from thew current font
+ in the graphics context GC. WIDTH is the width in bytes
+ and HEIGHT is the height in bits.
+
+ Note that the generated bitmaps must be used with
+
+ glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
+ glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+
+ Possible optimizations:
+
+ * use only one reusable pixmap with the maximum dimensions.
+ * draw the entire font into a single pixmap (careful with
+ proportional fonts!).
+*/
+
+
+/**
+ * Generate OpenGL-compatible bitmap.
+ */
+static void
+fill_bitmap(Display *dpy, Window win, GC gc,
+ unsigned int width, unsigned int height,
+ int x0, int y0, unsigned int c, GLubyte *bitmap)
+{
+ XImage *image;
+ unsigned int x, y;
+ Pixmap pixmap;
+ XChar2b char2b;
+
+ pixmap = XCreatePixmap(dpy, win, 8*width, height, 1);
+ XSetForeground(dpy, gc, 0);
+ XFillRectangle(dpy, pixmap, gc, 0, 0, 8*width, height);
+ XSetForeground(dpy, gc, 1);
+
+ char2b.byte1 = (c >> 8) & 0xff;
+ char2b.byte2 = (c & 0xff);
+
+ XDrawString16(dpy, pixmap, gc, x0, y0, &char2b, 1);
+
+ image = XGetImage(dpy, pixmap, 0, 0, 8*width, height, 1, XYPixmap);
+ if (image) {
+ /* Fill the bitmap (X11 and OpenGL are upside down wrt each other). */
+ for (y = 0; y < height; y++)
+ for (x = 0; x < 8*width; x++)
+ if (XGetPixel(image, x, y))
+ bitmap[width*(height - y - 1) + x/8] |= (1 << (7 - (x % 8)));
+ XDestroyImage(image);
+ }
+
+ XFreePixmap(dpy, pixmap);
+}
+
+/*
+ * determine if a given glyph is valid and return the
+ * corresponding XCharStruct.
+ */
+static XCharStruct *isvalid(XFontStruct *fs, unsigned int which)
+{
+ unsigned int rows, pages;
+ unsigned int byte1 = 0, byte2 = 0;
+ int i, valid = 1;
+
+ rows = fs->max_byte1 - fs->min_byte1 + 1;
+ pages = fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1;
+
+ if (rows == 1) {
+ /* "linear" fonts */
+ if ((fs->min_char_or_byte2 > which) ||
+ (fs->max_char_or_byte2 < which)) valid = 0;
+ }
+ else {
+ /* "matrix" fonts */
+ byte2 = which & 0xff;
+ byte1 = which >> 8;
+ if ((fs->min_char_or_byte2 > byte2) ||
+ (fs->max_char_or_byte2 < byte2) ||
+ (fs->min_byte1 > byte1) ||
+ (fs->max_byte1 < byte1)) valid = 0;
+ }
+
+ if (valid) {
+ if (fs->per_char) {
+ if (rows == 1) {
+ /* "linear" fonts */
+ return fs->per_char + (which-fs->min_char_or_byte2);
+ }
+ else {
+ /* "matrix" fonts */
+ i = ((byte1 - fs->min_byte1) * pages) +
+ (byte2 - fs->min_char_or_byte2);
+ return fs->per_char + i;
+ }
+ }
+ else {
+ return &fs->min_bounds;
+ }
+ }
+ return NULL;
+}
+
+
+void stubUseXFont( Display *dpy, Font font, int first, int count, int listbase )
+{
+ Window win;
+ Pixmap pixmap;
+ GC gc;
+ XGCValues values;
+ unsigned long valuemask;
+ XFontStruct *fs;
+ GLint swapbytes, lsbfirst, rowlength;
+ GLint skiprows, skippixels, alignment;
+ unsigned int max_width, max_height, max_bm_width, max_bm_height;
+ GLubyte *bm;
+ int i;
+
+ win = RootWindow(dpy, DefaultScreen(dpy));
+
+ fs = XQueryFont(dpy, font);
+ if (!fs) {
+ crWarning("Couldn't get font structure information");
+ return;
+ }
+
+ /* Allocate a bitmap that can fit all characters. */
+ max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
+ max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
+ max_bm_width = (max_width + 7) / 8;
+ max_bm_height = max_height;
+
+ bm = (GLubyte *) crAlloc((max_bm_width * max_bm_height) * sizeof(GLubyte));
+ if (!bm) {
+ XFreeFontInfo( NULL, fs, 1 );
+ crWarning("Couldn't allocate bitmap in glXUseXFont()");
+ return;
+ }
+
+ /* Save the current packing mode for bitmaps. */
+ glGetIntegerv(GL_UNPACK_SWAP_BYTES, &swapbytes);
+ glGetIntegerv(GL_UNPACK_LSB_FIRST, &lsbfirst);
+ glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rowlength);
+ glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skiprows);
+ glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skippixels);
+ glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
+
+ /* Enforce a standard packing mode which is compatible with
+ fill_bitmap() from above. This is actually the default mode,
+ except for the (non)alignment. */
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ pixmap = XCreatePixmap(dpy, win, 10, 10, 1);
+ values.foreground = BlackPixel(dpy, DefaultScreen (dpy));
+ values.background = WhitePixel(dpy, DefaultScreen (dpy));
+ values.font = fs->fid;
+ valuemask = GCForeground | GCBackground | GCFont;
+ gc = XCreateGC(dpy, pixmap, valuemask, &values);
+ XFreePixmap(dpy, pixmap);
+
+ for (i = 0; i < count; i++) {
+ unsigned int width, height, bm_width, bm_height;
+ GLfloat x0, y0, dx, dy;
+ XCharStruct *ch;
+ int x, y;
+ unsigned int c = first + i;
+ int list = listbase + i;
+ int valid;
+
+ /* check on index validity and get the bounds */
+ ch = isvalid(fs, c);
+ if (!ch) {
+ ch = &fs->max_bounds;
+ valid = 0;
+ }
+ else {
+ valid = 1;
+ }
+
+ /* glBitmap()' parameters:
+ straight from the glXUseXFont(3) manpage. */
+ width = ch->rbearing - ch->lbearing;
+ height = ch->ascent + ch->descent;
+ x0 = -ch->lbearing;
+ y0 = ch->descent - 0; /* XXX used to subtract 1 here */
+ /* but that caused a conformance failure */
+ dx = ch->width;
+ dy = 0;
+
+ /* X11's starting point. */
+ x = -ch->lbearing;
+ y = ch->ascent;
+
+ /* Round the width to a multiple of eight. We will use this also
+ for the pixmap for capturing the X11 font. This is slightly
+ inefficient, but it makes the OpenGL part real easy. */
+ bm_width = (width + 7) / 8;
+ bm_height = height;
+
+ glNewList(list, GL_COMPILE);
+ if (valid && (bm_width > 0) && (bm_height > 0)) {
+ crMemset(bm, '\0', bm_width * bm_height);
+ fill_bitmap(dpy, win, gc, bm_width, bm_height, x, y, c, bm);
+ glBitmap(width, height, x0, y0, dx, dy, bm);
+ }
+ else {
+ glBitmap(0, 0, 0.0, 0.0, dx, dy, NULL);
+ }
+ glEndList();
+ }
+
+ crFree(bm);
+ XFreeFontInfo(NULL, fs, 1);
+ XFreeGC(dpy, gc);
+
+ /* Restore saved packing modes. */
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+}
diff --git a/src/VBox/Additions/common/pam/Makefile.kmk b/src/VBox/Additions/common/pam/Makefile.kmk
new file mode 100644
index 00000000..06384ae1
--- /dev/null
+++ b/src/VBox/Additions/common/pam/Makefile.kmk
@@ -0,0 +1,35 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for VBox PAM module for automated logons.
+#
+
+#
+# Copyright (C) 2011-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# The PAM module.
+DLLS += pam_vbox
+
+pam_vbox_TEMPLATE = VBOXGUESTR3DLL
+pam_vbox_DEFS = LOG_TO_BACKDOOR VBOX_WITH_HGCM
+pam_vbox_DEFS += \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,)
+pam_vbox_SOURCES = pam_vbox.cpp
+pam_vbox_LIBS = \
+ pam \
+ $(VBOX_LIB_IPRT_GUEST_R3_SHARED) \
+ $(VBOX_LIB_VBGL_R3_SHARED) \
+ $(VBOX_LIB_IPRT_GUEST_R3_SHARED)
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/Additions/common/pam/pam_vbox.cpp b/src/VBox/Additions/common/pam/pam_vbox.cpp
new file mode 100644
index 00000000..b3ad83e5
--- /dev/null
+++ b/src/VBox/Additions/common/pam/pam_vbox.cpp
@@ -0,0 +1,898 @@
+/* $Id: pam_vbox.cpp $ */
+/** @file
+ * pam_vbox - PAM module for auto logons.
+ */
+
+/*
+ * Copyright (C) 2008-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+
+#ifdef DEBUG
+# define PAM_DEBUG
+#endif
+
+#include <security/pam_appl.h>
+#ifdef RT_OS_LINUX
+# include <security/_pam_macros.h>
+#endif
+
+#include <pwd.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/env.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include <VBox/VBoxGuestLib.h>
+
+#include <VBox/log.h>
+#ifdef VBOX_WITH_GUEST_PROPS
+# include <VBox/HostServices/GuestPropertySvc.h>
+#endif
+
+#define VBOX_MODULE_NAME "pam_vbox"
+
+#define VBOX_PAM_FLAG_SILENT "PAM_SILENT"
+#define VBOX_PAM_FLAG_DISALLOW_NULL_AUTHTOK "PAM_DISALLOW_NULL_AUTHTOK"
+#define VBOX_PAM_FLAG_ESTABLISH_CRED "PAM_ESTABLISH_CRED"
+#define VBOX_PAM_FLAG_DELETE_CRED "PAM_DELETE_CRED"
+#define VBOX_PAM_FLAG_REINITIALIZE_CRED "PAM_REINITIALIZE_CRED"
+#define VBOX_PAM_FLAG_REFRESH_CRED "PAM_REFRESH_CRED"
+
+RT_C_DECLS_BEGIN
+RTDECL(int) pam_sm_authenticate(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RTDECL(int) pam_sm_setcred(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RTDECL(int) pam_sm_acct_mgmt(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RTDECL(int) pam_sm_open_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RTDECL(int) pam_sm_close_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RTDECL(int) pam_sm_chauthtok(pam_handle_t *hPAM, int iFlags, int argc, const char **argv);
+RT_C_DECLS_END
+
+/** For debugging. */
+#ifdef DEBUG
+static pam_handle_t *g_pam_handle;
+static int g_verbosity = 99;
+#else
+static int g_verbosity = 0;
+#endif
+
+/**
+ * User-provided thread data for the credentials waiting thread.
+ */
+typedef struct PAMVBOXTHREAD
+{
+ /** The PAM handle. */
+ pam_handle_t *hPAM;
+ /** The timeout (in ms) to wait for credentials. */
+ uint32_t uTimeoutMS;
+ /** The overall result of the thread operation. */
+ int rc;
+} PAMVBOXTHREAD, *PPAMVBOXTHREAD;
+
+/**
+ * Write to system log.
+ *
+ * @param pszBuf Buffer to write to the log (NULL-terminated)
+ */
+static void pam_vbox_writesyslog(char *pszBuf)
+{
+#ifdef RT_OS_LINUX
+ openlog("pam_vbox", LOG_PID, LOG_AUTHPRIV);
+ syslog(LOG_ERR, "%s", pszBuf);
+ closelog();
+#elif defined(RT_OS_SOLARIS)
+ syslog(LOG_ERR, "pam_vbox: %s\n", pszBuf);
+#endif
+}
+
+
+/**
+ * Displays an error message.
+ *
+ * @param hPAM PAM handle.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+static void pam_vbox_error(pam_handle_t *hPAM, const char *pszFormat, ...)
+{
+ RT_NOREF1(hPAM);
+ va_list va;
+ char *buf;
+ va_start(va, pszFormat);
+ if (RT_SUCCESS(RTStrAPrintfV(&buf, pszFormat, va)))
+ {
+ LogRel(("%s: Error: %s", VBOX_MODULE_NAME, buf));
+ pam_vbox_writesyslog(buf);
+ RTStrFree(buf);
+ }
+ va_end(va);
+}
+
+
+/**
+ * Displays a debug message.
+ *
+ * @param hPAM PAM handle.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+static void pam_vbox_log(pam_handle_t *hPAM, const char *pszFormat, ...)
+{
+ RT_NOREF1(hPAM);
+ if (g_verbosity)
+ {
+ va_list va;
+ char *buf;
+ va_start(va, pszFormat);
+ if (RT_SUCCESS(RTStrAPrintfV(&buf, pszFormat, va)))
+ {
+ /* Only do normal logging in debug mode; could contain
+ * sensitive data! */
+ LogRel(("%s: %s", VBOX_MODULE_NAME, buf));
+ /* Log to syslog */
+ pam_vbox_writesyslog(buf);
+ RTStrFree(buf);
+ }
+ va_end(va);
+ }
+}
+
+
+/**
+ * Sets a message using PAM's conversation function.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param iStyle Style of message (0 = Information, 1 = Prompt
+ * echo off, 2 = Prompt echo on, 3 = Error).
+ * @param pszText Message text to set.
+ */
+static int vbox_set_msg(pam_handle_t *hPAM, int iStyle, const char *pszText)
+{
+ AssertPtrReturn(hPAM, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszText, VERR_INVALID_POINTER);
+
+ if (!iStyle)
+ iStyle = PAM_TEXT_INFO;
+
+ int rc = VINF_SUCCESS;
+
+ pam_message msg;
+ msg.msg_style = iStyle;
+#ifdef RT_OS_SOLARIS
+ msg.msg = (char*)pszText;
+#else
+ msg.msg = pszText;
+#endif
+
+#ifdef RT_OS_SOLARIS
+ pam_conv *conv = NULL;
+ int pamrc = pam_get_item(hPAM, PAM_CONV, (void **)&conv);
+#else
+ const pam_conv *conv = NULL;
+ int pamrc = pam_get_item(hPAM, PAM_CONV, (const void **)&conv);
+#endif
+ if ( pamrc == PAM_SUCCESS
+ && conv)
+ {
+ pam_response *resp = NULL;
+#ifdef RT_OS_SOLARIS
+ pam_message *msg_p = &msg;
+#else
+ const pam_message *msg_p = &msg;
+#endif
+ pam_vbox_log(hPAM, "Showing message \"%s\" (type %d)", pszText, iStyle);
+
+ pamrc = conv->conv(1 /* One message only */, &msg_p, &resp, conv->appdata_ptr);
+ if (resp != NULL) /* If we use PAM_TEXT_INFO we never will get something back! */
+ {
+ if (resp->resp)
+ {
+ pam_vbox_log(hPAM, "Response to message \"%s\" was \"%s\"",
+ pszText, resp->resp);
+ /** @todo Save response! */
+ free(resp->resp);
+ }
+ free(resp);
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * Initializes pam_vbox.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ */
+static int pam_vbox_init(pam_handle_t *hPAM)
+{
+#ifdef DEBUG
+ g_pam_handle = hPAM; /* hack for getting assertion text */
+#endif
+
+ /* Don't make assertions panic because the PAM stack +
+ * the current logon module won't work anymore (or just restart).
+ * This could result in not able to log into the system anymore. */
+ RTAssertSetMayPanic(false);
+
+ pam_vbox_log(hPAM, "pam_vbox: %sr%s, running on %s\n",
+ RTBldCfgVersion(), RTBldCfgRevisionStr(), RTBldCfgTargetArch());
+
+ int rc = RTR3InitDll(0);
+ if (RT_FAILURE(rc))
+ {
+ pam_vbox_error(hPAM, "pam_vbox_init: could not init runtime! rc=%Rrc. Aborting\n", rc);
+ return PAM_SUCCESS; /* Jump out as early as we can to not mess around. */
+ }
+
+ pam_vbox_log(hPAM, "pam_vbox_init: runtime initialized\n");
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3InitUser();
+ if (RT_FAILURE(rc))
+ {
+ switch(rc)
+ {
+ case VERR_ACCESS_DENIED:
+ pam_vbox_error(hPAM, "pam_vbox_init: access is denied to guest driver! Please make sure you run with sufficient rights. Aborting\n");
+ break;
+
+ case VERR_FILE_NOT_FOUND:
+ pam_vbox_error(hPAM, "pam_vbox_init: guest driver not found! Guest Additions installed? Aborting\n");
+ break;
+
+ default:
+ pam_vbox_error(hPAM, "pam_vbox_init: could not init VbglR3 library! rc=%Rrc. Aborting\n", rc);
+ break;
+ }
+ }
+ pam_vbox_log(hPAM, "pam_vbox_init: guest lib initialized\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ char *rhost = NULL;
+ char *tty = NULL;
+ char *prompt = NULL;
+#ifdef RT_OS_SOLARIS
+ pam_get_item(hPAM, PAM_RHOST, (void**) &rhost);
+ pam_get_item(hPAM, PAM_TTY, (void**) &tty);
+ pam_get_item(hPAM, PAM_USER_PROMPT, (void**) &prompt);
+#else
+ pam_get_item(hPAM, PAM_RHOST, (const void**) &rhost);
+ pam_get_item(hPAM, PAM_TTY, (const void**) &tty);
+ pam_get_item(hPAM, PAM_USER_PROMPT, (const void**) &prompt);
+#endif
+ pam_vbox_log(hPAM, "pam_vbox_init: rhost=%s, tty=%s, prompt=%s\n",
+ rhost ? rhost : "<none>", tty ? tty : "<none>", prompt ? prompt : "<none>");
+ }
+
+ return rc;
+}
+
+
+/**
+ * Shuts down pam_vbox.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ */
+static void pam_vbox_shutdown(pam_handle_t *hPAM)
+{
+ RT_NOREF1(hPAM);
+ VbglR3Term();
+}
+
+
+/**
+ * Checks for credentials provided by the host / HGCM.
+ *
+ * @return IPRT status code. VERR_NOT_FOUND if no credentials are available,
+ * VINF_SUCCESS on successful retrieval or another IPRT error.
+ * @param hPAM PAM handle.
+ */
+static int pam_vbox_check_creds(pam_handle_t *hPAM)
+{
+ int rc = VbglR3CredentialsQueryAvailability();
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_NOT_FOUND)
+ pam_vbox_error(hPAM, "pam_vbox_check_creds: could not query for credentials! rc=%Rrc. Aborting\n", rc);
+#ifdef DEBUG
+ else
+ pam_vbox_log(hPAM, "pam_vbox_check_creds: no credentials available\n");
+#endif
+ }
+ else
+ {
+ char *pszUsername;
+ char *pszPassword;
+ char *pszDomain;
+
+ rc = VbglR3CredentialsRetrieve(&pszUsername, &pszPassword, &pszDomain);
+ if (RT_FAILURE(rc))
+ {
+ pam_vbox_error(hPAM, "pam_vbox_check_creds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
+ }
+ else
+ {
+#ifdef DEBUG
+ pam_vbox_log(hPAM, "pam_vbox_check_creds: credentials retrieved: user=%s, password=%s, domain=%s\n",
+ pszUsername, pszPassword, pszDomain);
+#else
+ /* Don't log passwords in release mode! */
+ pam_vbox_log(hPAM, "pam_vbox_check_creds: credentials retrieved: user=%s, password=XXX, domain=%s\n",
+ pszUsername, pszDomain);
+#endif
+ /* Fill credentials into PAM. */
+ int pamrc = pam_set_item(hPAM, PAM_USER, pszUsername);
+ if (pamrc != PAM_SUCCESS)
+ {
+ pam_vbox_error(hPAM, "pam_vbox_check_creds: could not set user name! pamrc=%d, msg=%s. Aborting\n",
+ pamrc, pam_strerror(hPAM, pamrc));
+ }
+ else
+ {
+ pamrc = pam_set_item(hPAM, PAM_AUTHTOK, pszPassword);
+ if (pamrc != PAM_SUCCESS)
+ pam_vbox_error(hPAM, "pam_vbox_check_creds: could not set password! pamrc=%d, msg=%s. Aborting\n",
+ pamrc, pam_strerror(hPAM, pamrc));
+
+ }
+ /** @todo Add handling domains as well. */
+ VbglR3CredentialsDestroy(pszUsername, pszPassword, pszDomain,
+ 3 /* Three wipe passes */);
+ pam_vbox_log(hPAM, "pam_vbox_check_creds: returned with pamrc=%d, msg=%s\n",
+ pamrc, pam_strerror(hPAM, pamrc));
+ }
+ }
+
+#ifdef DEBUG
+ pam_vbox_log(hPAM, "pam_vbox_check_creds: returned with rc=%Rrc\n", rc);
+#endif
+ return rc;
+}
+
+
+#ifdef VBOX_WITH_GUEST_PROPS
+/**
+ * Reads a guest property.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param uClientID Guest property service client ID.
+ * @param pszKey Key (name) of guest property to read.
+ * @param fReadOnly Indicates whether this key needs to be
+ * checked if it only can be read (and *not* written)
+ * by the guest.
+ * @param pszValue Buffer where to store the key's value.
+ * @param cbValue Size of buffer (in bytes).
+ */
+static int pam_vbox_read_prop(pam_handle_t *hPAM, uint32_t uClientID,
+ const char *pszKey, bool fReadOnly,
+ char *pszValue, size_t cbValue)
+{
+ AssertPtrReturn(hPAM, VERR_INVALID_POINTER);
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+
+ int rc;
+
+ uint64_t u64Timestamp = 0;
+ char *pszValTemp;
+ char *pszFlags = NULL;
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + _1K;
+
+ /* Because there is a race condition between our reading the size of a
+ * property and the guest updating it, we loop a few times here and
+ * hope. Actually this should never go wrong, as we are generous
+ * enough with buffer space. */
+ for (unsigned i = 0; i < 10; i++)
+ {
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (pvTmpBuf)
+ {
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropRead(uClientID, pszKey, pvBuf, cbBuf,
+ &pszValTemp, &u64Timestamp, &pszFlags,
+ &cbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ switch (rc)
+ {
+ case VERR_BUFFER_OVERFLOW:
+ {
+ /* Buffer too small, try it with a bigger one next time. */
+ cbBuf += _1K;
+ continue; /* Try next round. */
+ }
+
+ default:
+ break;
+ }
+
+ /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Check security bits. */
+ if (pszFlags)
+ {
+ if ( fReadOnly
+ && !RTStrStr(pszFlags, "RDONLYGUEST"))
+ {
+ /* If we want a property which is read-only on the guest
+ * and it is *not* marked as such, deny access! */
+ pam_vbox_error(hPAM, "pam_vbox_read_prop: key \"%s\" should be read-only on guest but it is not\n",
+ pszKey);
+ rc = VERR_ACCESS_DENIED;
+ }
+ }
+ else /* No flags, no access! */
+ {
+ pam_vbox_error(hPAM, "pam_vbox_read_prop: key \"%s\" contains no/wrong flags (%s)\n",
+ pszKey, pszFlags);
+ rc = VERR_ACCESS_DENIED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* If everything went well copy property value to our destination buffer. */
+ if (!RTStrPrintf(pszValue, cbValue, "%s", pszValTemp))
+ {
+ pam_vbox_error(hPAM, "pam_vbox_read_prop: could not store value of key \"%s\"\n",
+ pszKey);
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_SUCCESS(rc))
+ pam_vbox_log(hPAM, "pam_vbox_read_prop: read key \"%s\"=\"%s\"\n",
+ pszKey, pszValue);
+ }
+ }
+
+ pam_vbox_log(hPAM, "pam_vbox_read_prop: read key \"%s\" with rc=%Rrc\n",
+ pszKey, rc);
+ return rc;
+}
+
+
+/**
+ * Waits for a guest property to be changed.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param uClientID Guest property service client ID.
+ * @param pszKey Key (name) of guest property to wait for.
+ * @param uTimeoutMS Timeout (in ms) to wait for the change. Specify
+ * RT_INDEFINITE_WAIT to wait indefinitly.
+ */
+static int pam_vbox_wait_prop(pam_handle_t *hPAM, uint32_t uClientID,
+ const char *pszKey, uint32_t uTimeoutMS)
+{
+ AssertPtrReturn(hPAM, VERR_INVALID_POINTER);
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+
+ int rc;
+
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + _1K;
+
+ for (int i = 0; i < 10; i++)
+ {
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (pvTmpBuf)
+ {
+ char *pszName = NULL;
+ char *pszValue = NULL;
+ uint64_t u64TimestampOut = 0;
+ char *pszFlags = NULL;
+
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropWait(uClientID, pszKey, pvBuf, cbBuf,
+ 0 /* Last timestamp; just wait for next event */, uTimeoutMS,
+ &pszName, &pszValue, &u64TimestampOut,
+ &pszFlags, &cbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ /* Buffer too small, try it with a bigger one next time. */
+ cbBuf += _1K;
+ continue; /* Try next round. */
+ }
+
+ /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+ break;
+ }
+
+ return rc;
+}
+#endif
+
+/**
+ * Thread function waiting for credentials to arrive.
+ *
+ * @return IPRT status code.
+ * @param hThreadSelf Thread handle.
+ * @param pvUser Pointer to a PAMVBOXTHREAD structure providing
+ * required data used / set by the thread.
+ */
+static DECLCALLBACK(int) pam_vbox_wait_thread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF1(hThreadSelf);
+ PPAMVBOXTHREAD pUserData = (PPAMVBOXTHREAD)pvUser;
+ AssertPtr(pUserData);
+
+ int rc = VINF_SUCCESS;
+ /* Get current time stamp to later calculate rest of timeout left. */
+ uint64_t u64StartMS = RTTimeMilliTS();
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ uint32_t uClientID = 0;
+ rc = VbglR3GuestPropConnect(&uClientID);
+ if (RT_FAILURE(rc))
+ {
+ pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: Unable to connect to guest property service, rc=%Rrc\n", rc);
+ }
+ else
+ {
+ pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: clientID=%u\n", uClientID);
+#endif
+ for (;;)
+ {
+#ifdef VBOX_WITH_GUEST_PROPS
+ if (uClientID)
+ {
+ rc = pam_vbox_wait_prop(pUserData->hPAM, uClientID,
+ "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
+ 500 /* Wait 500ms, same as VBoxGINA/VBoxCredProv. */);
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ /* Somebody (guest/host) wants to abort waiting for credentials. */
+ break;
+
+ case VERR_INTERRUPTED:
+ pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: The abort notification request timed out or was interrupted\n");
+ break;
+
+ case VERR_TIMEOUT:
+ /* We did not receive an abort message within time. */
+ break;
+
+ case VERR_TOO_MUCH_DATA:
+ pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: Temporarily unable to get abort notification\n");
+ break;
+
+ default:
+ pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: The abort notification request failed with rc=%Rrc\n", rc);
+ break;
+ }
+
+ if (RT_SUCCESS(rc)) /* Abort waiting. */
+ {
+ pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Got notification to abort waiting\n");
+ rc = VERR_CANCELLED;
+ break;
+ }
+ }
+#endif
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_TIMEOUT)
+ {
+ rc = pam_vbox_check_creds(pUserData->hPAM);
+ if (RT_SUCCESS(rc))
+ {
+ /* Credentials retrieved. */
+ break; /* Thread no longer is required, bail out. */
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ /* No credentials found, but try next round (if there's
+ * time left for) ... */
+#ifndef VBOX_WITH_GUEST_PROPS
+ RTThreadSleep(500); /* Wait 500 ms. */
+#endif
+ }
+ else
+ break; /* Something bad happend ... */
+ }
+ else
+ break;
+
+ /* Calculate timeout value left after process has been started. */
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
+ /* Is it time to bail out? */
+ if (pUserData->uTimeoutMS < u64Elapsed)
+ {
+ pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Waiting thread has reached timeout (%dms), exiting ...\n",
+ pUserData->uTimeoutMS);
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+#ifdef VBOX_WITH_GUEST_PROPS
+ }
+ VbglR3GuestPropDisconnect(uClientID);
+#endif
+
+ /* Save result. */
+ pUserData->rc = rc; /** @todo Use ASMAtomicXXX? */
+
+ int rc2 = RTThreadUserSignal(RTThreadSelf());
+ AssertRC(rc2);
+
+ pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Waiting thread returned with rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * Waits for credentials to arrive by creating and waiting for a thread.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param uClientID Guest property service client ID.
+ * @param uTimeoutMS Timeout (in ms) to wait for the change. Specify
+ * RT_INDEFINITE_WAIT to wait indefinitly.
+ */
+static int pam_vbox_wait_for_creds(pam_handle_t *hPAM, uint32_t uClientID, uint32_t uTimeoutMS)
+{
+ RT_NOREF1(uClientID);
+ PAMVBOXTHREAD threadData;
+ threadData.hPAM = hPAM;
+ threadData.uTimeoutMS = uTimeoutMS;
+
+ RTTHREAD threadWait;
+ int rc = RTThreadCreate(&threadWait, pam_vbox_wait_thread,
+ (void *)&threadData, 0,
+ RTTHREADTYPE_DEFAULT, 0 /* Flags */, "pam_vbox");
+ if (RT_SUCCESS(rc))
+ {
+ pam_vbox_log(hPAM, "pam_vbox_wait_for_creds: Waiting for credentials (%dms) ...\n", uTimeoutMS);
+ /* Wait for thread to initialize. */
+ /** @todo We can do something else here in the meantime later. */
+ rc = RTThreadUserWait(threadWait, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ rc = threadData.rc; /* Get back thread result to take further actions. */
+ }
+ else
+ pam_vbox_error(hPAM, "pam_vbox_wait_for_creds: Creating thread failed with rc=%Rrc\n", rc);
+
+ pam_vbox_log(hPAM, "pam_vbox_wait_for_creds: Waiting for credentials returned with rc=%Rrc\n", rc);
+ return rc;
+}
+
+
+DECLEXPORT(int) pam_sm_authenticate(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ RT_NOREF1(iFlags);
+
+ /* Parse arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (!RTStrICmp(argv[i], "debug"))
+ g_verbosity = 1;
+ else
+ pam_vbox_error(hPAM, "pam_vbox_authenticate: unknown command line argument \"%s\"\n", argv[i]);
+ }
+ pam_vbox_log(hPAM, "pam_vbox_authenticate called\n");
+
+ int rc = pam_vbox_init(hPAM);
+ if (RT_FAILURE(rc))
+ return PAM_SUCCESS; /* Jump out as early as we can to not mess around. */
+
+ bool fFallback = true;
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ uint32_t uClientId;
+ rc = VbglR3GuestPropConnect(&uClientId);
+ if (RT_SUCCESS(rc))
+ {
+ char szVal[256];
+ rc = pam_vbox_read_prop(hPAM, uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWait",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal));
+ if (RT_SUCCESS(rc))
+ {
+ /* All calls which are checked against rc2 are not critical, e.g. it does
+ * not matter if they succeed or not. */
+ uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
+ int rc2 = pam_vbox_read_prop(hPAM, uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal));
+ if (RT_SUCCESS(rc2))
+ {
+ uTimeoutMS = RTStrToUInt32(szVal);
+ if (!uTimeoutMS)
+ {
+ pam_vbox_error(hPAM, "pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
+ uTimeoutMS = RT_INDEFINITE_WAIT;
+ }
+ else
+ uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
+ }
+
+ rc2 = pam_vbox_read_prop(hPAM, uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal));
+ const char *pszWaitMsg = NULL;
+ if (RT_SUCCESS(rc2))
+ pszWaitMsg = szVal;
+
+ rc2 = vbox_set_msg(hPAM, 0 /* Info message */,
+ pszWaitMsg ? pszWaitMsg : "Waiting for credentials ...");
+ if (RT_FAILURE(rc2)) /* Not critical. */
+ pam_vbox_error(hPAM, "pam_vbox_authenticate: error setting waiting information message, rc=%Rrc\n", rc2);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Before we actuall wait for credentials just make sure we didn't already get credentials
+ * set so that we can skip waiting for them ... */
+ rc = pam_vbox_check_creds(hPAM);
+ if (rc == VERR_NOT_FOUND)
+ {
+ rc = pam_vbox_wait_for_creds(hPAM, uClientId, uTimeoutMS);
+ if (rc == VERR_TIMEOUT)
+ {
+ pam_vbox_log(hPAM, "pam_vbox_authenticate: no credentials given within time\n");
+
+ rc2 = pam_vbox_read_prop(hPAM, uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal));
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = vbox_set_msg(hPAM, 0 /* Info message */, szVal);
+ AssertRC(rc2);
+ }
+ }
+ else if (rc == VERR_CANCELLED)
+ {
+ pam_vbox_log(hPAM, "pam_vbox_authenticate: waiting aborted\n");
+
+ rc2 = pam_vbox_read_prop(hPAM, uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal));
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = vbox_set_msg(hPAM, 0 /* Info message */, szVal);
+ AssertRC(rc2);
+ }
+ }
+ }
+
+ /* If we got here we don't need the fallback, so just deactivate it. */
+ fFallback = false;
+ }
+ }
+
+ VbglR3GuestPropDisconnect(uClientId);
+ }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+ if (fFallback)
+ {
+ pam_vbox_log(hPAM, "pam_vbox_authenticate: falling back to old method\n");
+
+ /* If anything went wrong in the code above we just do a credentials
+ * check like it was before: Try retrieving the stuff and authenticating. */
+ int rc2 = pam_vbox_check_creds(hPAM);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ pam_vbox_shutdown(hPAM);
+
+ pam_vbox_log(hPAM, "pam_vbox_authenticate: overall result rc=%Rrc\n", rc);
+
+ /* Never report an error here because if no credentials from the host are available or something
+ * went wrong we then let do the authentication by the next module in the stack. */
+
+ /* We report success here because this is all we can do right now -- we passed the credentials
+ * to the next PAM module in the block above which then might do a shadow (like pam_unix/pam_unix2)
+ * password verification to "really" authenticate the user. */
+ return PAM_SUCCESS;
+}
+
+
+DECLEXPORT(int) pam_sm_setcred(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ pam_vbox_log(hPAM, "pam_vbox_setcred called, iFlags=0x%x\n", iFlags);
+ for (int i = 0; i < argc; i++)
+ pam_vbox_log(hPAM, "pam_vbox_setcred: argv[%d] = %s\n", i, argv[i]);
+ return PAM_SUCCESS;
+}
+
+
+DECLEXPORT(int) pam_sm_acct_mgmt(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ RT_NOREF3(iFlags, argc, argv);
+ pam_vbox_log(hPAM, "pam_vbox_acct_mgmt called\n");
+ return PAM_SUCCESS;
+}
+
+
+DECLEXPORT(int) pam_sm_open_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ RT_NOREF3(iFlags, argc, argv);
+ pam_vbox_log(hPAM, "pam_vbox_open_session called\n");
+ RTPrintf("This session was provided by VirtualBox Guest Additions. Have a lot of fun!\n");
+ return PAM_SUCCESS;
+}
+
+
+DECLEXPORT(int) pam_sm_close_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ RT_NOREF3(iFlags, argc, argv);
+ pam_vbox_log(hPAM, "pam_vbox_close_session called\n");
+ return PAM_SUCCESS;
+}
+
+
+DECLEXPORT(int) pam_sm_chauthtok(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
+{
+ RT_NOREF3(iFlags, argc, argv);
+ pam_vbox_log(hPAM, "pam_vbox_sm_chauthtok called\n");
+ return PAM_SUCCESS;
+}
+
+
+#ifdef DEBUG
+DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ pam_vbox_log(g_pam_handle,
+ "\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%d) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+ RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
+}
+#endif
+
diff --git a/src/VBox/Additions/common/testcase/Makefile.kmk b/src/VBox/Additions/common/testcase/Makefile.kmk
new file mode 100644
index 00000000..8c632651
--- /dev/null
+++ b/src/VBox/Additions/common/testcase/Makefile.kmk
@@ -0,0 +1,35 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Cross Platform Guest Addition test cases.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Target lists.
+#
+PROGRAMS += tstPageFusion
+
+#
+# tstPageFusion
+#
+tstPageFusion_TEMPLATE = VBoxGuestR3Exe
+tstPageFusion_DEFS.win += _WIN32_WINNT=0x0501
+tstPageFusion_SOURCES = \
+ tstPageFusion.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/testcase/tstPageFusion.cpp b/src/VBox/Additions/common/testcase/tstPageFusion.cpp
new file mode 100644
index 00000000..8ce52f94
--- /dev/null
+++ b/src/VBox/Additions/common/testcase/tstPageFusion.cpp
@@ -0,0 +1,379 @@
+/* $Id: tstPageFusion.cpp $ */
+/** @file
+ * VBoxService - Guest page sharing testcase
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/messages.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <VBox/VBoxGuestLib.h>
+#include <iprt/x86.h>
+#include <stdio.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+#ifdef RT_OS_WINDOWS
+#include <iprt/win/windows.h>
+#include <process.h> /* Needed for file version information. */
+#include <tlhelp32.h>
+#include <psapi.h>
+#include <winternl.h>
+
+#define SystemModuleInformation 11
+
+typedef struct _RTL_PROCESS_MODULE_INFORMATION
+{
+ ULONG Section;
+ PVOID MappedBase;
+ PVOID ImageBase;
+ ULONG ImageSize;
+ ULONG Flags;
+ USHORT LoadOrderIndex;
+ USHORT InitOrderIndex;
+ USHORT LoadCount;
+ USHORT OffsetToFileName;
+ CHAR FullPathName[256];
+} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULES
+{
+ ULONG NumberOfModules;
+ RTL_PROCESS_MODULE_INFORMATION Modules[1];
+} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
+
+typedef NTSTATUS (WINAPI *PFNZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
+static PFNZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
+static HMODULE hNtdll = 0;
+
+#define PAGE_STATE_INVALID 0
+#define PAGE_STATE_SHARED 1
+#define PAGE_STATE_READ_WRITE 2
+#define PAGE_STATE_READ_ONLY 3
+#define PAGE_STATE_NOT_PRESENT 4
+
+/* Page counters. */
+static unsigned cNotPresentPages = 0;
+static unsigned cWritablePages = 0;
+static unsigned cSharedPages = 0;
+static unsigned cPrivatePages = 0;
+
+/**
+ * Registers a new module with the VMM
+ * @param pModule Module ptr
+ */
+void VBoxServicePageSharingCheckModule(MODULEENTRY32 *pModule)
+{
+ DWORD dwModuleSize = pModule->modBaseSize;
+ BYTE *pBaseAddress = pModule->modBaseAddr;
+ bool fFirstLine = true;
+ unsigned uPageState, uLastPageState;
+ bool fLastWritable = false;
+ BYTE *pLastBaseAddress = pBaseAddress;
+
+ uPageState = uLastPageState = PAGE_STATE_INVALID;
+
+ printf("Check module %s base %p size %x\n", pModule->szModule, pBaseAddress, dwModuleSize);
+ do
+ {
+ bool fShared;
+ uint64_t uPageFlags;
+
+#ifdef RT_ARCH_X86
+ int rc = VbglR3PageIsShared((uint32_t)pLastBaseAddress, &fShared, &uPageFlags);
+#else
+ int rc = VbglR3PageIsShared((RTGCPTR)pLastBaseAddress, &fShared, &uPageFlags);
+#endif
+ if (RT_FAILURE(rc))
+ printf("VbglR3PageIsShared %p failed with %d\n", pLastBaseAddress, rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (uPageFlags & X86_PTE_P)
+ {
+ if (uPageFlags & X86_PTE_RW)
+ {
+ cWritablePages++;
+ uPageState = PAGE_STATE_READ_WRITE;
+ }
+ else
+ if (fShared)
+ {
+ cSharedPages++;
+ uPageState = PAGE_STATE_SHARED;
+ }
+ else
+ {
+ cPrivatePages++;
+ uPageState = PAGE_STATE_READ_ONLY;
+ }
+ }
+ else
+ {
+ cNotPresentPages++;
+ uPageState = PAGE_STATE_NOT_PRESENT;
+ }
+
+ if ( !fFirstLine
+ && uPageState != uLastPageState)
+ {
+ printf("0x%p\n", pLastBaseAddress + 0xfff);
+ }
+
+ if (uPageState != uLastPageState)
+ {
+ switch (uPageState)
+ {
+ case PAGE_STATE_READ_WRITE:
+ printf("%s RW 0x%p - ", pModule->szModule, pBaseAddress);
+ break;
+ case PAGE_STATE_SHARED:
+ printf("%s SHARED 0x%p - ", pModule->szModule, pBaseAddress);
+ break;
+ case PAGE_STATE_READ_ONLY:
+ printf("%s PRIV 0x%p - ", pModule->szModule, pBaseAddress);
+ break;
+ case PAGE_STATE_NOT_PRESENT:
+ printf("%s NP 0x%p - ", pModule->szModule, pBaseAddress);
+ break;
+ }
+
+ fFirstLine = false;
+ }
+ uLastPageState = uPageState;
+ }
+ else
+ if (!fFirstLine)
+ {
+ printf("0x%p\n", pLastBaseAddress + 0xfff);
+ fFirstLine = true;
+ }
+
+ if (dwModuleSize > PAGE_SIZE)
+ dwModuleSize -= PAGE_SIZE;
+ else
+ dwModuleSize = 0;
+
+ pLastBaseAddress = pBaseAddress;
+ pBaseAddress += PAGE_SIZE;
+ }
+ while (dwModuleSize);
+
+ printf("0x%p\n", pLastBaseAddress + 0xfff);
+ return;
+}
+
+/**
+ * Inspect all loaded modules for the specified process
+ * @param dwProcessId Process id
+ */
+void VBoxServicePageSharingInspectModules(DWORD dwProcessId)
+{
+ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
+ if (hSnapshot == INVALID_HANDLE_VALUE)
+ {
+ printf("VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
+ return;
+ }
+
+ printf("VBoxServicePageSharingInspectModules\n");
+
+ MODULEENTRY32 ModuleInfo;
+ BOOL bRet;
+
+ ModuleInfo.dwSize = sizeof(ModuleInfo);
+ bRet = Module32First(hSnapshot, &ModuleInfo);
+ do
+ {
+ /** @todo when changing this make sure VBoxService.exe is excluded! */
+ char *pszDot = strrchr(ModuleInfo.szModule, '.');
+ if ( pszDot
+ && (pszDot[1] == 'e' || pszDot[1] == 'E'))
+ continue; /* ignore executables for now. */
+
+ VBoxServicePageSharingCheckModule(&ModuleInfo);
+ }
+ while (Module32Next(hSnapshot, &ModuleInfo));
+
+ CloseHandle(hSnapshot);
+}
+
+/**
+ * Inspect all running processes for executables and dlls that might be worth sharing
+ * with other VMs.
+ *
+ */
+void VBoxServicePageSharingInspectGuest()
+{
+ VBoxServicePageSharingInspectModules(GetCurrentProcessId());
+
+ printf("\n\nUSER RESULTS\n");
+ printf("cNotPresentPages = %d\n", cNotPresentPages);
+ printf("cWritablePages = %d\n", cWritablePages);
+ printf("cPrivatePages = %d\n", cPrivatePages);
+ printf("cSharedPages = %d\n", cSharedPages);
+
+ cNotPresentPages = 0;
+ cWritablePages = 0;
+ cPrivatePages = 0;
+ cSharedPages = 0;
+
+ /* Check all loaded kernel modules. */
+ if (ZwQuerySystemInformation)
+ {
+ ULONG cbBuffer = 0;
+ PVOID pBuffer = NULL;
+ PRTL_PROCESS_MODULES pSystemModules;
+
+ NTSTATUS ret = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer);
+ if (!cbBuffer)
+ {
+ printf("ZwQuerySystemInformation returned length 0\n");
+ goto skipkernelmodules;
+ }
+
+ pBuffer = RTMemAllocZ(cbBuffer);
+ if (!pBuffer)
+ goto skipkernelmodules;
+
+ ret = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer);
+ if (ret != 0)
+ {
+ printf("ZwQuerySystemInformation returned %x (1)\n", ret);
+ goto skipkernelmodules;
+ }
+
+ pSystemModules = (PRTL_PROCESS_MODULES)pBuffer;
+ for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++)
+ {
+ /* User-mode modules seem to have no flags set; skip them as we detected them above. */
+ if (pSystemModules->Modules[i].Flags == 0)
+ continue;
+
+ /* New module; register it. */
+ char szFullFilePath[512];
+ MODULEENTRY32 ModuleInfo;
+
+ strcpy(ModuleInfo.szModule, &pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]);
+ GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
+
+ /* skip \Systemroot\system32 */
+ char *lpPath = strchr(&pSystemModules->Modules[i].FullPathName[1], '\\');
+ if (!lpPath)
+ {
+ printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
+ break;
+ }
+
+ lpPath = strchr(lpPath+1, '\\');
+ if (!lpPath)
+ {
+ printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
+ break;
+ }
+
+ strcat(szFullFilePath, lpPath);
+ strcpy(ModuleInfo.szExePath, szFullFilePath);
+ ModuleInfo.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase;
+ ModuleInfo.modBaseSize = pSystemModules->Modules[i].ImageSize;
+
+ VBoxServicePageSharingCheckModule(&ModuleInfo);
+ }
+skipkernelmodules:
+ if (pBuffer)
+ RTMemFree(pBuffer);
+ }
+ printf("\n\nKERNEL RESULTS\n");
+ printf("cNotPresentPages = %d\n", cNotPresentPages);
+ printf("cWritablePages = %d\n", cWritablePages);
+ printf("cPrivatePages = %d\n", cPrivatePages);
+ printf("cSharedPages = %d\n", cSharedPages);
+}
+#else
+void VBoxServicePageSharingInspectGuest()
+{
+ /** @todo other platforms */
+}
+#endif
+
+
+/** @copydoc VBOXSERVICE::pfnInit */
+static DECLCALLBACK(int) VBoxServicePageSharingInit(void)
+{
+ printf("VBoxServicePageSharingInit\n");
+
+#ifdef RT_OS_WINDOWS
+ hNtdll = LoadLibrary("ntdll.dll");
+
+ if (hNtdll)
+ ZwQuerySystemInformation = (PFNZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation");
+#endif
+
+ /** @todo report system name and version */
+ /* Never fail here. */
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) VBoxServicePageSharingTerm(void)
+{
+ printf("VBoxServicePageSharingTerm\n");
+
+#ifdef RT_OS_WINDOWS
+ if (hNtdll)
+ FreeLibrary(hNtdll);
+#endif
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init globals and such.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Connect to the kernel part before daemonizing so we can fail
+ * and complain if there is some kind of problem. We need to initialize
+ * the guest lib *before* we do the pre-init just in case one of services
+ * needs do to some initial stuff with it.
+ */
+ printf("Calling VbgR3Init()\n");
+ rc = VbglR3Init();
+ if (RT_FAILURE(rc))
+ {
+ printf("VbglR3Init failed with rc=%Rrc.\n", rc);
+ return -1;
+ }
+ VBoxServicePageSharingInit();
+
+ VBoxServicePageSharingInspectGuest();
+
+ VBoxServicePageSharingTerm();
+ return 0;
+}
+